This commit is contained in:
parent
9f88b44b91
commit
eeb6ec9dd8
@ -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",
|
||||
|
@ -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,41 +109,64 @@ 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);
|
||||
|
||||
// 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();
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
console.timeEnd("subscribeToAuthorsAndKinds");
|
||||
}, 200);
|
||||
|
||||
|
||||
async find(filter: Filter, callback: (event: TaggedNostrEvent) => void): Promise<void> {
|
||||
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(() => {
|
||||
return marked.use(markedFootnote()).lexer(props.content);
|
||||
}, [props.content, props.tags]);
|
||||
@ -130,4 +130,7 @@ export const Markdown = forwardRef<HTMLDivElement, MarkdownProps>((props: Markdo
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
Markdown.displayName = "Markdown";
|
||||
|
||||
export { Markdown };
|
||||
|
@ -325,4 +325,5 @@ const AsyncFooterIcon = forwardRef((props: AsyncIconProps & { value: number }, r
|
||||
</AsyncIcon>
|
||||
);
|
||||
});
|
||||
|
||||
AsyncFooterIcon.displayName = "AsyncFooterIcon";
|
||||
|
@ -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 <div className="card reaction" ref={ref}></div>;
|
||||
}
|
||||
|
@ -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 (
|
||||
<>
|
||||
|
@ -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,
|
||||
|
@ -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 (
|
||||
<div>
|
||||
<p className="success">
|
||||
|
@ -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<T = LoginSession>(selector?: (v: LoginSession) => T) {
|
||||
if (selector) {
|
||||
return useSyncExternalStoreWithSelector(
|
||||
const defaultSelector = (v: LoginSession) => v as unknown as T;
|
||||
|
||||
return useSyncExternalStoreWithSelector<LoginSession, T>(
|
||||
s => LoginStore.hook(s),
|
||||
() => LoginStore.snapshot(),
|
||||
undefined,
|
||||
selector,
|
||||
);
|
||||
} else {
|
||||
return useSyncExternalStore<T>(
|
||||
s => LoginStore.hook(s),
|
||||
() => LoginStore.snapshot() as T,
|
||||
selector || defaultSelector,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -26,9 +26,7 @@ export default function Index() {
|
||||
useTheme();
|
||||
useLoginRelays();
|
||||
useLoginFeed();
|
||||
if (CONFIG.features.communityLeaders) {
|
||||
useCommunityLeaders();
|
||||
}
|
||||
|
||||
const hideHeaderPaths = ["/login", "/new"];
|
||||
const shouldHideFooter = location.pathname.startsWith("/messages/");
|
||||
|
@ -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<typeof FormattedMessage>;
|
||||
disabledMessage: React.ComponentProps<typeof FormattedMessage>;
|
||||
}
|
||||
|
||||
const StatusIndicator = ({ status, enabledMessage, disabledMessage }: StatusIndicatorProps) => {
|
||||
return status ? (
|
||||
<div className="flex items-center">
|
||||
<Icon name="check" size={20} className="text-green-500 mr-2" />
|
||||
|
@ -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<T extends object = NoteCreatorDataSnapshot>(
|
||||
selector?: (v: NoteCreatorDataSnapshot) => T,
|
||||
) {
|
||||
if (selector) {
|
||||
const defaultSelector = (v: NoteCreatorDataSnapshot) => v as unknown as T;
|
||||
|
||||
return useSyncExternalStoreWithSelector<NoteCreatorDataSnapshot, T>(
|
||||
c => NoteCreatorState.hook(c),
|
||||
() => NoteCreatorState.snapshot(),
|
||||
undefined,
|
||||
selector,
|
||||
);
|
||||
} else {
|
||||
return useSyncExternalStore<T>(
|
||||
c => NoteCreatorState.hook(c),
|
||||
() => NoteCreatorState.snapshot() as T,
|
||||
selector || defaultSelector,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user