This commit is contained in:
parent
981ab5790a
commit
5a63a21fec
@ -314,18 +314,18 @@ export function NoteCreator() {
|
|||||||
onChange={e => {
|
onChange={e => {
|
||||||
note.update(
|
note.update(
|
||||||
v =>
|
v =>
|
||||||
(v.selectedCustomRelays =
|
(v.selectedCustomRelays =
|
||||||
// set false if all relays selected
|
// set false if all relays selected
|
||||||
e.target.checked &&
|
e.target.checked &&
|
||||||
note.selectedCustomRelays &&
|
note.selectedCustomRelays &&
|
||||||
note.selectedCustomRelays.length == a.length - 1
|
note.selectedCustomRelays.length == a.length - 1
|
||||||
? undefined
|
? undefined
|
||||||
: // otherwise return selectedCustomRelays with target relay added / removed
|
: // otherwise return selectedCustomRelays with target relay added / removed
|
||||||
a.filter(el =>
|
a.filter(el =>
|
||||||
el === r
|
el === r
|
||||||
? e.target.checked
|
? e.target.checked
|
||||||
: !note.selectedCustomRelays || note.selectedCustomRelays.includes(el),
|
: !note.selectedCustomRelays || note.selectedCustomRelays.includes(el),
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -394,9 +394,9 @@ export function NoteCreator() {
|
|||||||
onChange={e =>
|
onChange={e =>
|
||||||
note.update(
|
note.update(
|
||||||
v =>
|
v =>
|
||||||
(v.zapSplits = arr.map((vv, ii) =>
|
(v.zapSplits = arr.map((vv, ii) =>
|
||||||
ii === i ? { ...vv, weight: Number(e.target.value) } : vv,
|
ii === i ? { ...vv, weight: Number(e.target.value) } : vv,
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -572,35 +572,43 @@ export function NoteCreator() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{note.preview && getPreviewNote()}
|
{note.preview && getPreviewNote()}
|
||||||
{!note.preview && (<>
|
{!note.preview && (
|
||||||
<div onPaste={handlePaste} className={classNames("note-creator", { poll: Boolean(note.pollOptions) })}>
|
<>
|
||||||
<Textarea
|
<div onPaste={handlePaste} className={classNames("note-creator", { poll: Boolean(note.pollOptions) })}>
|
||||||
onDragOver={handleDragOver}
|
<Textarea
|
||||||
onDragLeave={handleDragLeave}
|
onDragOver={handleDragOver}
|
||||||
onDrop={handleDrop}
|
onDragLeave={handleDragLeave}
|
||||||
autoFocus
|
onDrop={handleDrop}
|
||||||
className={classNames("textarea", { "textarea--focused": note.active })}
|
autoFocus
|
||||||
onChange={c => onChange(c)}
|
className={classNames("textarea", { "textarea--focused": note.active })}
|
||||||
value={note.note}
|
onChange={c => onChange(c)}
|
||||||
onFocus={() => note.update(v => (v.active = true))}
|
value={note.note}
|
||||||
onKeyDown={e => {
|
onFocus={() => note.update(v => (v.active = true))}
|
||||||
if (e.key === "Enter" && e.metaKey) {
|
onKeyDown={e => {
|
||||||
sendNote().catch(console.warn);
|
if (e.key === "Enter" && e.metaKey) {
|
||||||
}
|
sendNote().catch(console.warn);
|
||||||
}}
|
}
|
||||||
/>
|
}}
|
||||||
{renderPollOptions()}
|
/>
|
||||||
</div>
|
{renderPollOptions()}
|
||||||
<div className="flex flex-col g4">
|
</div>
|
||||||
<TagsInput value={note.hashTags} onChange={e => note.update(s => s.hashTags = e)} placeHolder={formatMessage({
|
<div className="flex flex-col g4">
|
||||||
defaultMessage: "Add up to 4 hashtags"
|
<TagsInput
|
||||||
})} separators={["Enter", ","]} />
|
value={note.hashTags}
|
||||||
{note.hashTags.length > 4 && <small className="warning">
|
onChange={e => note.update(s => (s.hashTags = e))}
|
||||||
<FormattedMessage defaultMessage="Try to use less than 4 hashtags to stay on topic 🙏" />
|
placeHolder={formatMessage({
|
||||||
</small>}
|
defaultMessage: "Add up to 4 hashtags",
|
||||||
<TrendingHashTagsLine onClick={t => note.update(s => s.hashTags = appendDedupe(s.hashTags, [t]))} />
|
})}
|
||||||
</div>
|
separators={["Enter", ","]}
|
||||||
</>
|
/>
|
||||||
|
{note.hashTags.length > 4 && (
|
||||||
|
<small className="warning">
|
||||||
|
<FormattedMessage defaultMessage="Try to use less than 5 hashtags to stay on topic 🙏" />
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
<TrendingHashTagsLine onClick={t => note.update(s => (s.hashTags = appendDedupe(s.hashTags, [t])))} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{uploader.progress.length > 0 && <FileUploadProgress progress={uploader.progress} />}
|
{uploader.progress.length > 0 && <FileUploadProgress progress={uploader.progress} />}
|
||||||
{noteCreatorFooter()}
|
{noteCreatorFooter()}
|
||||||
@ -627,7 +635,7 @@ export function NoteCreator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TrendingHashTagsLine(props: { onClick: (tag: string) => void }) {
|
function TrendingHashTagsLine(props: { onClick: (tag: string) => void }) {
|
||||||
const [hashtags, setHashtags] = useState<Array<{ hashtag: string, posts: number }>>();
|
const [hashtags, setHashtags] = useState<Array<{ hashtag: string; posts: number }>>();
|
||||||
const { lang } = useLocale();
|
const { lang } = useLocale();
|
||||||
|
|
||||||
async function loadTrendingHashtags() {
|
async function loadTrendingHashtags() {
|
||||||
@ -641,14 +649,18 @@ function TrendingHashTagsLine(props: { onClick: (tag: string) => void }) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!hashtags || hashtags.length === 0) return;
|
if (!hashtags || hashtags.length === 0) return;
|
||||||
return <div className="flex flex-col g4">
|
return (
|
||||||
<small>
|
<div className="flex flex-col g4">
|
||||||
<FormattedMessage defaultMessage="Popular Hashtags" />
|
<small>
|
||||||
</small>
|
<FormattedMessage defaultMessage="Popular Hashtags" />
|
||||||
<div className="flex g4 flex-wrap">
|
</small>
|
||||||
{hashtags.slice(0, 5).map(a => <span className="px-2 py-1 bg-dark rounded-full pointer nowrap" onClick={() => props.onClick(a.hashtag)}>
|
<div className="flex g4 flex-wrap">
|
||||||
#{a.hashtag}
|
{hashtags.slice(0, 5).map(a => (
|
||||||
</span>)}
|
<span className="px-2 py-1 bg-dark rounded-full pointer nowrap" onClick={() => props.onClick(a.hashtag)}>
|
||||||
|
#{a.hashtag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,29 +7,33 @@ import { HashTagHeader } from "Pages/HashTagsPage";
|
|||||||
import { useLocale } from "IntlProvider";
|
import { useLocale } from "IntlProvider";
|
||||||
|
|
||||||
export default function TrendingHashtags({ title }: { title?: ReactNode }) {
|
export default function TrendingHashtags({ title }: { title?: ReactNode }) {
|
||||||
const [hashtags, setHashtags] = useState<Array<{ hashtag: string, posts: number }>>();
|
const [hashtags, setHashtags] = useState<Array<{ hashtag: string; posts: number }>>();
|
||||||
const [error, setError] = useState<Error>();
|
const [error, setError] = useState<Error>();
|
||||||
const { lang } = useLocale();
|
const { lang } = useLocale();
|
||||||
|
|
||||||
async function loadTrendingHashtags() {
|
async function loadTrendingHashtags() {
|
||||||
const api = new NostrBandApi();
|
const api = new NostrBandApi();
|
||||||
const rsp = await api.trendingHashtags(lang);
|
const rsp = await api.trendingHashtags(lang);
|
||||||
setHashtags(rsp.hashtags);
|
setHashtags(rsp.hashtags);
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadTrendingHashtags().catch(e => {
|
loadTrendingHashtags().catch(e => {
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
setError(e);
|
setError(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (error) return <ErrorOrOffline error={error} onRetry={loadTrendingHashtags} className="p" />;
|
if (error) return <ErrorOrOffline error={error} onRetry={loadTrendingHashtags} className="p" />;
|
||||||
if (!hashtags) return <PageSpinner />;
|
if (!hashtags) return <PageSpinner />;
|
||||||
|
|
||||||
return <>
|
return (
|
||||||
{title}
|
<>
|
||||||
{hashtags.map(a => <HashTagHeader tag={a.hashtag} className="bb p" />)}
|
{title}
|
||||||
|
{hashtags.map(a => (
|
||||||
|
<HashTagHeader tag={a.hashtag} events={a.posts} className="bb p" />
|
||||||
|
))}
|
||||||
</>
|
</>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
6
packages/app/src/External/NostrBand.ts
vendored
6
packages/app/src/External/NostrBand.ts
vendored
@ -20,9 +20,9 @@ export interface TrendingNoteResponse {
|
|||||||
|
|
||||||
export interface TrendingHashtagsResponse {
|
export interface TrendingHashtagsResponse {
|
||||||
hashtags: Array<{
|
hashtags: Array<{
|
||||||
hashtag: string,
|
hashtag: string;
|
||||||
posts: number
|
posts: number;
|
||||||
}>
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SuggestedFollow {
|
export interface SuggestedFollow {
|
||||||
|
@ -12,6 +12,7 @@ import { setTags } from "Login";
|
|||||||
import AsyncButton from "Element/AsyncButton";
|
import AsyncButton from "Element/AsyncButton";
|
||||||
import ProfileImage from "Element/User/ProfileImage";
|
import ProfileImage from "Element/User/ProfileImage";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import { formatShort } from "Number";
|
||||||
|
|
||||||
const HashTagsPage = () => {
|
const HashTagsPage = () => {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@ -34,7 +35,7 @@ const HashTagsPage = () => {
|
|||||||
|
|
||||||
export default HashTagsPage;
|
export default HashTagsPage;
|
||||||
|
|
||||||
export function HashTagHeader({ tag, className }: { tag: string, className?: string }) {
|
export function HashTagHeader({ tag, events, className }: { tag: string; events?: number; className?: string }) {
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const isFollowing = useMemo(() => {
|
const isFollowing = useMemo(() => {
|
||||||
return login.tags.item.includes(tag);
|
return login.tags.item.includes(tag);
|
||||||
@ -63,7 +64,19 @@ export function HashTagHeader({ tag, className }: { tag: string, className?: str
|
|||||||
return (
|
return (
|
||||||
<div className={classNames("flex flex-col", className)}>
|
<div className={classNames("flex flex-col", className)}>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<b className="text-lg">#{tag}</b>
|
<div className="flex g8 items-center">
|
||||||
|
<b className="text-xl">#{tag}</b>
|
||||||
|
{events && (
|
||||||
|
<small>
|
||||||
|
<FormattedMessage
|
||||||
|
defaultMessage="{n} notes"
|
||||||
|
values={{
|
||||||
|
n: formatShort(events),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</small>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{isFollowing ? (
|
{isFollowing ? (
|
||||||
<AsyncButton className="secondary" onClick={() => followTags(login.tags.item.filter(t => t !== tag))}>
|
<AsyncButton className="secondary" onClick={() => followTags(login.tags.item.filter(t => t !== tag))}>
|
||||||
<FormattedMessage defaultMessage="Unfollow" />
|
<FormattedMessage defaultMessage="Unfollow" />
|
||||||
|
@ -298,6 +298,9 @@
|
|||||||
"defaultMessage": "Parent",
|
"defaultMessage": "Parent",
|
||||||
"description": "Link to parent note in thread"
|
"description": "Link to parent note in thread"
|
||||||
},
|
},
|
||||||
|
"AIgmDy": {
|
||||||
|
"defaultMessage": "Add up to 4 hashtags"
|
||||||
|
},
|
||||||
"AN0Z7Q": {
|
"AN0Z7Q": {
|
||||||
"defaultMessage": "Muted Words"
|
"defaultMessage": "Muted Words"
|
||||||
},
|
},
|
||||||
@ -879,6 +882,9 @@
|
|||||||
"XICsE8": {
|
"XICsE8": {
|
||||||
"defaultMessage": "File hosts"
|
"defaultMessage": "File hosts"
|
||||||
},
|
},
|
||||||
|
"XXm7jJ": {
|
||||||
|
"defaultMessage": "Trending Hashtags"
|
||||||
|
},
|
||||||
"XgWvGA": {
|
"XgWvGA": {
|
||||||
"defaultMessage": "Reactions"
|
"defaultMessage": "Reactions"
|
||||||
},
|
},
|
||||||
@ -993,9 +999,15 @@
|
|||||||
"d7d0/x": {
|
"d7d0/x": {
|
||||||
"defaultMessage": "LN Address"
|
"defaultMessage": "LN Address"
|
||||||
},
|
},
|
||||||
|
"d8gpCh": {
|
||||||
|
"defaultMessage": "Try to use less than 5 hashtags to stay on topic 🙏"
|
||||||
|
},
|
||||||
"dOQCL8": {
|
"dOQCL8": {
|
||||||
"defaultMessage": "Display name"
|
"defaultMessage": "Display name"
|
||||||
},
|
},
|
||||||
|
"ddd3JX": {
|
||||||
|
"defaultMessage": "Popular Hashtags"
|
||||||
|
},
|
||||||
"deEeEI": {
|
"deEeEI": {
|
||||||
"defaultMessage": "Register"
|
"defaultMessage": "Register"
|
||||||
},
|
},
|
||||||
@ -1382,6 +1394,9 @@
|
|||||||
"ugyJnE": {
|
"ugyJnE": {
|
||||||
"defaultMessage": "Sending notes and other stuff"
|
"defaultMessage": "Sending notes and other stuff"
|
||||||
},
|
},
|
||||||
|
"un1nGw": {
|
||||||
|
"defaultMessage": "{n} notes"
|
||||||
|
},
|
||||||
"usAvMr": {
|
"usAvMr": {
|
||||||
"defaultMessage": "Edit Profile"
|
"defaultMessage": "Edit Profile"
|
||||||
},
|
},
|
||||||
|
@ -98,6 +98,7 @@
|
|||||||
"9wO4wJ": "Lightning Invoice",
|
"9wO4wJ": "Lightning Invoice",
|
||||||
"ABAQyo": "Chats",
|
"ABAQyo": "Chats",
|
||||||
"ADmfQT": "Parent",
|
"ADmfQT": "Parent",
|
||||||
|
"AIgmDy": "Add up to 4 hashtags",
|
||||||
"AN0Z7Q": "Muted Words",
|
"AN0Z7Q": "Muted Words",
|
||||||
"ASRK0S": "This author has been muted",
|
"ASRK0S": "This author has been muted",
|
||||||
"Ai8VHU": "Unlimited note retention on Snort relay",
|
"Ai8VHU": "Unlimited note retention on Snort relay",
|
||||||
@ -289,6 +290,7 @@
|
|||||||
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
|
"X7xU8J": "nsec, npub, nip-05, hex, mnemonic",
|
||||||
"XECMfW": "Send usage metrics",
|
"XECMfW": "Send usage metrics",
|
||||||
"XICsE8": "File hosts",
|
"XICsE8": "File hosts",
|
||||||
|
"XXm7jJ": "Trending Hashtags",
|
||||||
"XgWvGA": "Reactions",
|
"XgWvGA": "Reactions",
|
||||||
"Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.",
|
"Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.",
|
||||||
"XrSk2j": "Redeem",
|
"XrSk2j": "Redeem",
|
||||||
@ -326,7 +328,9 @@
|
|||||||
"d+6YsV": "Lists to mute:",
|
"d+6YsV": "Lists to mute:",
|
||||||
"d6CyG5": "History",
|
"d6CyG5": "History",
|
||||||
"d7d0/x": "LN Address",
|
"d7d0/x": "LN Address",
|
||||||
|
"d8gpCh": "Try to use less than 5 hashtags to stay on topic 🙏",
|
||||||
"dOQCL8": "Display name",
|
"dOQCL8": "Display name",
|
||||||
|
"ddd3JX": "Popular Hashtags",
|
||||||
"deEeEI": "Register",
|
"deEeEI": "Register",
|
||||||
"dmsiLv": "A default Zap Pool split of {n} has been configured for {site} developers, you can disable it at any time in {link}",
|
"dmsiLv": "A default Zap Pool split of {n} has been configured for {site} developers, you can disable it at any time in {link}",
|
||||||
"e61Jf3": "Coming soon",
|
"e61Jf3": "Coming soon",
|
||||||
@ -455,6 +459,7 @@
|
|||||||
"uSV4Ti": "Reposts need to be manually confirmed",
|
"uSV4Ti": "Reposts need to be manually confirmed",
|
||||||
"uc0din": "Send sats splits to",
|
"uc0din": "Send sats splits to",
|
||||||
"ugyJnE": "Sending notes and other stuff",
|
"ugyJnE": "Sending notes and other stuff",
|
||||||
|
"un1nGw": "{n} notes",
|
||||||
"usAvMr": "Edit Profile",
|
"usAvMr": "Edit Profile",
|
||||||
"v8lolG": "Start chat",
|
"v8lolG": "Start chat",
|
||||||
"vB3oQ/": "Must be a contact list or pubkey list",
|
"vB3oQ/": "Must be a contact list or pubkey list",
|
||||||
|
Loading…
Reference in New Issue
Block a user