This commit is contained in:
parent
9f88b44b91
commit
eeb6ec9dd8
@ -17,7 +17,7 @@
|
|||||||
"deck": true,
|
"deck": true,
|
||||||
"zapPool": true,
|
"zapPool": true,
|
||||||
"notificationGraph": false,
|
"notificationGraph": false,
|
||||||
"communityLeaders": false
|
"communityLeaders": true
|
||||||
},
|
},
|
||||||
"signUp": {
|
"signUp": {
|
||||||
"moderation": false,
|
"moderation": false,
|
||||||
@ -27,6 +27,9 @@
|
|||||||
"bypassImgProxyError": true,
|
"bypassImgProxyError": true,
|
||||||
"preferLargeMedia": true
|
"preferLargeMedia": true
|
||||||
},
|
},
|
||||||
|
"communityLeaders": {
|
||||||
|
"list": "naddr1qq4xc6tnw3ez6vp58y6rywpjxckngdtyxukngwr9vckkze33vcknzcnrxcenje35xqmn2cczyp3lucccm3v9s087z6qslpkap8schltk427zfgqgrn3g2menq5zw6qcyqqq82vqprpmhxue69uhhyetvv9ujuumwdae8gtnnda3kjctv7rajfl"
|
||||||
|
},
|
||||||
"noteCreatorToast": false,
|
"noteCreatorToast": false,
|
||||||
"hideFromNavbar": [],
|
"hideFromNavbar": [],
|
||||||
"eventLinkPrefix": "note",
|
"eventLinkPrefix": "note",
|
||||||
|
@ -25,6 +25,7 @@ class IndexedDB extends Dexie {
|
|||||||
super("EventDB");
|
super("EventDB");
|
||||||
|
|
||||||
this.version(5).stores({
|
this.version(5).stores({
|
||||||
|
// TODO use multientry index for *tags
|
||||||
events: "id, pubkey, kind, created_at, [pubkey+kind]",
|
events: "id, pubkey, kind, created_at, [pubkey+kind]",
|
||||||
tags: "id, eventId, [type+value]",
|
tags: "id, eventId, [type+value]",
|
||||||
});
|
});
|
||||||
@ -108,40 +109,63 @@ class IndexedDB extends Dexie {
|
|||||||
subscribeToAuthors = this._throttle(async function (callback: (event: TaggedNostrEvent) => void, limit?: number) {
|
subscribeToAuthors = this._throttle(async function (callback: (event: TaggedNostrEvent) => void, limit?: number) {
|
||||||
const authors = [...this.subscribedAuthors];
|
const authors = [...this.subscribedAuthors];
|
||||||
this.subscribedAuthors.clear();
|
this.subscribedAuthors.clear();
|
||||||
|
|
||||||
|
// Start timing
|
||||||
|
console.time("subscribeToAuthors");
|
||||||
|
|
||||||
await this.events
|
await this.events
|
||||||
.where("pubkey")
|
.where("pubkey")
|
||||||
.anyOf(authors)
|
.anyOf(authors)
|
||||||
.limit(limit || 1000)
|
.limit(limit || 1000)
|
||||||
.each(callback);
|
.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];
|
const ids = [...this.subscribedEventIds];
|
||||||
this.subscribedEventIds.clear();
|
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("|"));
|
const tagPairs = [...this.subscribedTags].map(tag => tag.split("|"));
|
||||||
this.subscribedTags.clear();
|
this.subscribedTags.clear();
|
||||||
|
|
||||||
|
console.time("subscribeToTags");
|
||||||
|
|
||||||
await this.tags
|
await this.tags
|
||||||
.where("[type+value]")
|
.where("[type+value]")
|
||||||
.anyOf(tagPairs)
|
.anyOf(tagPairs)
|
||||||
.each(tag => this.subscribedEventIds.add(tag.eventId));
|
.each(tag => this.subscribedEventIds.add(tag.eventId));
|
||||||
|
|
||||||
await this.subscribeToEventIds(callback);
|
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];
|
const authorsAndKinds = [...this.subscribedAuthorsAndKinds];
|
||||||
this.subscribedAuthorsAndKinds.clear();
|
this.subscribedAuthorsAndKinds.clear();
|
||||||
|
|
||||||
|
console.time("subscribeToAuthorsAndKinds");
|
||||||
|
|
||||||
// parse pair[1] as int
|
// parse pair[1] as int
|
||||||
const pairs = authorsAndKinds.map(pair => {
|
const pairs = authorsAndKinds.map(pair => {
|
||||||
const [author, kind] = pair.split("|");
|
const [author, kind] = pair.split("|");
|
||||||
return [author, parseInt(kind)];
|
return [author, parseInt(kind)];
|
||||||
});
|
});
|
||||||
await this.events.where("[pubkey+kind]").anyOf(pairs).each(callback);
|
await this.events.where("[pubkey+kind]").anyOf(pairs).each(callback);
|
||||||
}, 200);
|
|
||||||
|
console.timeEnd("subscribeToAuthorsAndKinds");
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
|
||||||
async find(filter: Filter, callback: (event: TaggedNostrEvent) => void): Promise<void> {
|
async find(filter: Filter, callback: (event: TaggedNostrEvent) => void): Promise<void> {
|
||||||
if (!filter) return;
|
if (!filter) return;
|
||||||
|
@ -119,7 +119,7 @@ function renderToken(t: Token | Footnotes | Footnote | FootnoteRef, tags: Array<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps, ref) => {
|
const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: MarkdownProps, ref) => {
|
||||||
const parsed = useMemo(() => {
|
const parsed = useMemo(() => {
|
||||||
return marked.use(markedFootnote()).lexer(props.content);
|
return marked.use(markedFootnote()).lexer(props.content);
|
||||||
}, [props.content, props.tags]);
|
}, [props.content, props.tags]);
|
||||||
@ -130,4 +130,7 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: Markdo
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Markdown.displayName = "Markdown";
|
Markdown.displayName = "Markdown";
|
||||||
|
|
||||||
|
export { Markdown };
|
||||||
|
@ -325,4 +325,5 @@ const AsyncFooterIcon = forwardRef((props: AsyncIconProps & { value: number }, r
|
|||||||
</AsyncIcon>
|
</AsyncIcon>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
AsyncFooterIcon.displayName = "AsyncFooterIcon";
|
AsyncFooterIcon.displayName = "AsyncFooterIcon";
|
||||||
|
@ -33,15 +33,6 @@ export default function NoteReaction(props: NoteReactionProps) {
|
|||||||
return null;
|
return null;
|
||||||
}, [ev]);
|
}, [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
|
* Some clients embed the reposted note in the content
|
||||||
*/
|
*/
|
||||||
@ -63,6 +54,15 @@ export default function NoteReaction(props: NoteReactionProps) {
|
|||||||
return props.root;
|
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) {
|
if (!inView) {
|
||||||
return <div className="card reaction" ref={ref}></div>;
|
return <div className="card reaction" ref={ref}></div>;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { FormattedMessage } from "react-intl";
|
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();
|
const navigate = useNavigate();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -7,7 +7,12 @@ import { UserCache } from "@/Cache";
|
|||||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||||
import { FormattedMessage } from "react-intl";
|
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 => ({
|
const { publicKey, readonly } = useLogin(s => ({
|
||||||
publicKey: s.publicKey,
|
publicKey: s.publicKey,
|
||||||
readonly: s.readonly,
|
readonly: s.readonly,
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import AccountName from "./AccountName";
|
import AccountName from "./AccountName";
|
||||||
import { FormattedMessage } from "react-intl";
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p className="success">
|
<p className="success">
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
import { LoginSession, LoginStore } from "@/Login";
|
import { LoginSession, LoginStore } from "@/Login";
|
||||||
import { useSyncExternalStore } from "react";
|
|
||||||
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
|
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
|
||||||
|
|
||||||
export default function useLogin<T = LoginSession>(selector?: (v: LoginSession) => T) {
|
export default function useLogin<T = LoginSession>(selector?: (v: LoginSession) => T) {
|
||||||
if (selector) {
|
const defaultSelector = (v: LoginSession) => v as unknown as T;
|
||||||
return useSyncExternalStoreWithSelector(
|
|
||||||
s => LoginStore.hook(s),
|
return useSyncExternalStoreWithSelector<LoginSession, T>(
|
||||||
() => LoginStore.snapshot(),
|
s => LoginStore.hook(s),
|
||||||
undefined,
|
() => LoginStore.snapshot(),
|
||||||
selector,
|
undefined,
|
||||||
);
|
selector || defaultSelector,
|
||||||
} else {
|
);
|
||||||
return useSyncExternalStore<T>(
|
|
||||||
s => LoginStore.hook(s),
|
|
||||||
() => LoginStore.snapshot() as T,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,7 @@ export default function Index() {
|
|||||||
useTheme();
|
useTheme();
|
||||||
useLoginRelays();
|
useLoginRelays();
|
||||||
useLoginFeed();
|
useLoginFeed();
|
||||||
if (CONFIG.features.communityLeaders) {
|
useCommunityLeaders();
|
||||||
useCommunityLeaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
const hideHeaderPaths = ["/login", "/new"];
|
const hideHeaderPaths = ["/login", "/new"];
|
||||||
const shouldHideFooter = location.pathname.startsWith("/messages/");
|
const shouldHideFooter = location.pathname.startsWith("/messages/");
|
||||||
|
@ -6,7 +6,13 @@ import { subscribeToNotifications } from "@/Notifications";
|
|||||||
import useEventPublisher from "@/Hooks/useEventPublisher";
|
import useEventPublisher from "@/Hooks/useEventPublisher";
|
||||||
import messages from "./messages";
|
import messages from "./messages";
|
||||||
|
|
||||||
const StatusIndicator = ({ status, enabledMessage, disabledMessage }) => {
|
interface StatusIndicatorProps {
|
||||||
|
status: boolean;
|
||||||
|
enabledMessage: React.ComponentProps<typeof FormattedMessage>;
|
||||||
|
disabledMessage: React.ComponentProps<typeof FormattedMessage>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StatusIndicator = ({ status, enabledMessage, disabledMessage }: StatusIndicatorProps) => {
|
||||||
return status ? (
|
return status ? (
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<Icon name="check" size={20} className="text-green-500 mr-2" />
|
<Icon name="check" size={20} className="text-green-500 mr-2" />
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { ExternalStore } from "@snort/shared";
|
import { ExternalStore } from "@snort/shared";
|
||||||
import { NostrEvent, TaggedNostrEvent } from "@snort/system";
|
import { NostrEvent, TaggedNostrEvent } from "@snort/system";
|
||||||
import { ZapTarget } from "@/Zapper";
|
import { ZapTarget } from "@/Zapper";
|
||||||
import { useSyncExternalStore } from "react";
|
|
||||||
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
|
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
|
||||||
|
|
||||||
interface NoteCreatorDataSnapshot {
|
interface NoteCreatorDataSnapshot {
|
||||||
@ -90,17 +89,12 @@ const NoteCreatorState = new NoteCreatorStore();
|
|||||||
export function useNoteCreator<T extends object = NoteCreatorDataSnapshot>(
|
export function useNoteCreator<T extends object = NoteCreatorDataSnapshot>(
|
||||||
selector?: (v: NoteCreatorDataSnapshot) => T,
|
selector?: (v: NoteCreatorDataSnapshot) => T,
|
||||||
) {
|
) {
|
||||||
if (selector) {
|
const defaultSelector = (v: NoteCreatorDataSnapshot) => v as unknown as T;
|
||||||
return useSyncExternalStoreWithSelector<NoteCreatorDataSnapshot, T>(
|
|
||||||
c => NoteCreatorState.hook(c),
|
return useSyncExternalStoreWithSelector<NoteCreatorDataSnapshot, T>(
|
||||||
() => NoteCreatorState.snapshot(),
|
c => NoteCreatorState.hook(c),
|
||||||
undefined,
|
() => NoteCreatorState.snapshot(),
|
||||||
selector,
|
undefined,
|
||||||
);
|
selector || defaultSelector,
|
||||||
} else {
|
);
|
||||||
return useSyncExternalStore<T>(
|
|
||||||
c => NoteCreatorState.hook(c),
|
|
||||||
() => NoteCreatorState.snapshot() as T,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user