diff --git a/packages/app/config/iris.json b/packages/app/config/iris.json index d348da66..591743c5 100644 --- a/packages/app/config/iris.json +++ b/packages/app/config/iris.json @@ -17,7 +17,7 @@ "deck": true, "zapPool": true, "notificationGraph": false, - "communityLeaders": false + "communityLeaders": true }, "signUp": { "moderation": false, @@ -27,6 +27,9 @@ "bypassImgProxyError": true, "preferLargeMedia": true }, + "communityLeaders": { + "list": "naddr1qq4xc6tnw3ez6vp58y6rywpjxckngdtyxukngwr9vckkze33vcknzcnrxcenje35xqmn2cczyp3lucccm3v9s087z6qslpkap8schltk427zfgqgrn3g2menq5zw6qcyqqq82vqprpmhxue69uhhyetvv9ujuumwdae8gtnnda3kjctv7rajfl" + }, "noteCreatorToast": false, "hideFromNavbar": [], "eventLinkPrefix": "note", diff --git a/packages/app/src/Cache/IndexedDB.ts b/packages/app/src/Cache/IndexedDB.ts index 420ef143..ddb7f5eb 100644 --- a/packages/app/src/Cache/IndexedDB.ts +++ b/packages/app/src/Cache/IndexedDB.ts @@ -25,6 +25,7 @@ class IndexedDB extends Dexie { super("EventDB"); this.version(5).stores({ + // TODO use multientry index for *tags events: "id, pubkey, kind, created_at, [pubkey+kind]", tags: "id, eventId, [type+value]", }); @@ -108,40 +109,63 @@ class IndexedDB extends Dexie { subscribeToAuthors = this._throttle(async function (callback: (event: TaggedNostrEvent) => void, limit?: number) { const authors = [...this.subscribedAuthors]; this.subscribedAuthors.clear(); + + // Start timing + console.time("subscribeToAuthors"); + await this.events .where("pubkey") .anyOf(authors) .limit(limit || 1000) .each(callback); - }, 200); - subscribeToEventIds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) { + // End timing and log the elapsed time + console.timeEnd("subscribeToAuthors"); +}, 200); + +subscribeToEventIds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) { const ids = [...this.subscribedEventIds]; this.subscribedEventIds.clear(); - await this.events.where("id").anyOf(ids).each(callback); - }, 200); - subscribeToTags = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) { + console.time("subscribeToEventIds"); + + await this.events.where("id").anyOf(ids).each(callback); + + console.timeEnd("subscribeToEventIds"); +}, 200); + +subscribeToTags = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) { const tagPairs = [...this.subscribedTags].map(tag => tag.split("|")); this.subscribedTags.clear(); + + console.time("subscribeToTags"); + await this.tags .where("[type+value]") .anyOf(tagPairs) .each(tag => this.subscribedEventIds.add(tag.eventId)); await this.subscribeToEventIds(callback); - }, 200); - subscribeToAuthorsAndKinds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) { + console.timeEnd("subscribeToTags"); +}, 200); + +subscribeToAuthorsAndKinds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) { const authorsAndKinds = [...this.subscribedAuthorsAndKinds]; this.subscribedAuthorsAndKinds.clear(); + + console.time("subscribeToAuthorsAndKinds"); + // parse pair[1] as int const pairs = authorsAndKinds.map(pair => { const [author, kind] = pair.split("|"); return [author, parseInt(kind)]; }); await this.events.where("[pubkey+kind]").anyOf(pairs).each(callback); - }, 200); + + console.timeEnd("subscribeToAuthorsAndKinds"); +}, 200); + async find(filter: Filter, callback: (event: TaggedNostrEvent) => void): Promise { if (!filter) return; diff --git a/packages/app/src/Element/Event/Markdown.tsx b/packages/app/src/Element/Event/Markdown.tsx index 5d6352a7..6ffac7a2 100644 --- a/packages/app/src/Element/Event/Markdown.tsx +++ b/packages/app/src/Element/Event/Markdown.tsx @@ -119,7 +119,7 @@ function renderToken(t: Token | Footnotes | Footnote | FootnoteRef, tags: Array< } } -export const Markdown = forwardRef((props: MarkdownProps, ref) => { +const Markdown = forwardRef((props: MarkdownProps, ref) => { const parsed = useMemo(() => { return marked.use(markedFootnote()).lexer(props.content); }, [props.content, props.tags]); @@ -130,4 +130,7 @@ export const Markdown = forwardRef((props: Markdo ); }); + Markdown.displayName = "Markdown"; + +export { Markdown }; diff --git a/packages/app/src/Element/Event/NoteFooter.tsx b/packages/app/src/Element/Event/NoteFooter.tsx index be083e89..6b3319ce 100644 --- a/packages/app/src/Element/Event/NoteFooter.tsx +++ b/packages/app/src/Element/Event/NoteFooter.tsx @@ -325,4 +325,5 @@ const AsyncFooterIcon = forwardRef((props: AsyncIconProps & { value: number }, r ); }); + AsyncFooterIcon.displayName = "AsyncFooterIcon"; diff --git a/packages/app/src/Element/Event/NoteReaction.tsx b/packages/app/src/Element/Event/NoteReaction.tsx index f5cfac06..f2b37104 100644 --- a/packages/app/src/Element/Event/NoteReaction.tsx +++ b/packages/app/src/Element/Event/NoteReaction.tsx @@ -33,15 +33,6 @@ export default function NoteReaction(props: NoteReactionProps) { return null; }, [ev]); - if ( - ev.kind !== EventKind.Reaction && - ev.kind !== EventKind.Repost && - (ev.kind !== EventKind.TextNote || - ev.tags.every((a, i) => a[1] !== refEvent?.[1] || a[3] !== "mention" || ev.content !== `#[${i}]`)) - ) { - return null; - } - /** * Some clients embed the reposted note in the content */ @@ -63,6 +54,15 @@ export default function NoteReaction(props: NoteReactionProps) { return props.root; } + if ( + ev.kind !== EventKind.Reaction && + ev.kind !== EventKind.Repost && + (ev.kind !== EventKind.TextNote || + ev.tags.every((a, i) => a[1] !== refEvent?.[1] || a[3] !== "mention" || ev.content !== `#[${i}]`)) + ) { + return null; + } + if (!inView) { return
; } diff --git a/packages/app/src/Element/IrisAccount/AccountName.tsx b/packages/app/src/Element/IrisAccount/AccountName.tsx index 5940f741..bc082375 100644 --- a/packages/app/src/Element/IrisAccount/AccountName.tsx +++ b/packages/app/src/Element/IrisAccount/AccountName.tsx @@ -1,7 +1,12 @@ import { useNavigate } from "react-router-dom"; import { FormattedMessage } from "react-intl"; -export default function AccountName({ name = "", link = true }) { +interface AccountNameProps { + name?: string; + link?: boolean; +} + +export default function AccountName({ name = "", link = true }: AccountNameProps) { const navigate = useNavigate(); return ( <> diff --git a/packages/app/src/Element/IrisAccount/ActiveAccount.tsx b/packages/app/src/Element/IrisAccount/ActiveAccount.tsx index f46c9add..ebcaae3f 100644 --- a/packages/app/src/Element/IrisAccount/ActiveAccount.tsx +++ b/packages/app/src/Element/IrisAccount/ActiveAccount.tsx @@ -7,7 +7,12 @@ import { UserCache } from "@/Cache"; import useEventPublisher from "@/Hooks/useEventPublisher"; import { FormattedMessage } from "react-intl"; -export default function ActiveAccount({ name = "", setAsPrimary = () => {} }) { +interface ActiveAccountProps { + name?: string; + setAsPrimary: () => void; +} + +export default function ActiveAccount({ name = "", setAsPrimary = () => {} }: ActiveAccountProps) { const { publicKey, readonly } = useLogin(s => ({ publicKey: s.publicKey, readonly: s.readonly, diff --git a/packages/app/src/Element/IrisAccount/ReservedAccount.tsx b/packages/app/src/Element/IrisAccount/ReservedAccount.tsx index 5d74da02..5043275d 100644 --- a/packages/app/src/Element/IrisAccount/ReservedAccount.tsx +++ b/packages/app/src/Element/IrisAccount/ReservedAccount.tsx @@ -1,7 +1,13 @@ import AccountName from "./AccountName"; import { FormattedMessage } from "react-intl"; -export default function ReservedAccount({ name = "", enableReserved = () => {}, declineReserved = () => {} }) { +interface ReservedAccountProps { + name?: string; + enableReserved: () => void; + declineReserved: () => void; +} + +export default function ReservedAccount({ name = "", enableReserved = () => {}, declineReserved = () => {} }: ReservedAccountProps) { return (

diff --git a/packages/app/src/Hooks/useLogin.tsx b/packages/app/src/Hooks/useLogin.tsx index dabce519..5c92b27d 100644 --- a/packages/app/src/Hooks/useLogin.tsx +++ b/packages/app/src/Hooks/useLogin.tsx @@ -1,19 +1,13 @@ import { LoginSession, LoginStore } from "@/Login"; -import { useSyncExternalStore } from "react"; import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector"; export default function useLogin(selector?: (v: LoginSession) => T) { - if (selector) { - return useSyncExternalStoreWithSelector( - s => LoginStore.hook(s), - () => LoginStore.snapshot(), - undefined, - selector, - ); - } else { - return useSyncExternalStore( - s => LoginStore.hook(s), - () => LoginStore.snapshot() as T, - ); - } + const defaultSelector = (v: LoginSession) => v as unknown as T; + + return useSyncExternalStoreWithSelector( + s => LoginStore.hook(s), + () => LoginStore.snapshot(), + undefined, + selector || defaultSelector, + ); } diff --git a/packages/app/src/Pages/Layout/index.tsx b/packages/app/src/Pages/Layout/index.tsx index 1113550b..0af64450 100644 --- a/packages/app/src/Pages/Layout/index.tsx +++ b/packages/app/src/Pages/Layout/index.tsx @@ -26,9 +26,7 @@ export default function Index() { useTheme(); useLoginRelays(); useLoginFeed(); - if (CONFIG.features.communityLeaders) { - useCommunityLeaders(); - } + useCommunityLeaders(); const hideHeaderPaths = ["/login", "/new"]; const shouldHideFooter = location.pathname.startsWith("/messages/"); diff --git a/packages/app/src/Pages/settings/Notifications.tsx b/packages/app/src/Pages/settings/Notifications.tsx index ac80a545..1e8bd4f2 100644 --- a/packages/app/src/Pages/settings/Notifications.tsx +++ b/packages/app/src/Pages/settings/Notifications.tsx @@ -6,7 +6,13 @@ import { subscribeToNotifications } from "@/Notifications"; import useEventPublisher from "@/Hooks/useEventPublisher"; import messages from "./messages"; -const StatusIndicator = ({ status, enabledMessage, disabledMessage }) => { +interface StatusIndicatorProps { + status: boolean; + enabledMessage: React.ComponentProps; + disabledMessage: React.ComponentProps; +} + +const StatusIndicator = ({ status, enabledMessage, disabledMessage }: StatusIndicatorProps) => { return status ? (

diff --git a/packages/app/src/State/NoteCreator.tsx b/packages/app/src/State/NoteCreator.tsx index 60595a3c..2208391d 100644 --- a/packages/app/src/State/NoteCreator.tsx +++ b/packages/app/src/State/NoteCreator.tsx @@ -1,7 +1,6 @@ import { ExternalStore } from "@snort/shared"; import { NostrEvent, TaggedNostrEvent } from "@snort/system"; import { ZapTarget } from "@/Zapper"; -import { useSyncExternalStore } from "react"; import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector"; interface NoteCreatorDataSnapshot { @@ -90,17 +89,12 @@ const NoteCreatorState = new NoteCreatorStore(); export function useNoteCreator( selector?: (v: NoteCreatorDataSnapshot) => T, ) { - if (selector) { - return useSyncExternalStoreWithSelector( - c => NoteCreatorState.hook(c), - () => NoteCreatorState.snapshot(), - undefined, - selector, - ); - } else { - return useSyncExternalStore( - c => NoteCreatorState.hook(c), - () => NoteCreatorState.snapshot() as T, - ); - } + const defaultSelector = (v: NoteCreatorDataSnapshot) => v as unknown as T; + + return useSyncExternalStoreWithSelector( + c => NoteCreatorState.hook(c), + () => NoteCreatorState.snapshot(), + undefined, + selector || defaultSelector, + ); }