mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-11 05:09:36 +02:00
convert decryption cache to service
This commit is contained in:
parent
cbddaaa44f
commit
7ebf09c24f
@ -25,6 +25,11 @@ export const DEFAULT_ICE_SERVERS: RTCIceServer[] = [
|
||||
{
|
||||
urls: ["stun:stun.l.google.com:19302"],
|
||||
},
|
||||
{
|
||||
urls: ["turn:172.234.18.173:3478"],
|
||||
username: "free",
|
||||
credential: "free",
|
||||
},
|
||||
];
|
||||
|
||||
export const NOSTR_CONNECT_PERMISSIONS = [
|
||||
|
29
src/hooks/use-kind4-decryption.ts
Normal file
29
src/hooks/use-kind4-decryption.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import decryptionCacheService from "../services/decryption-cache";
|
||||
import useCurrentAccount from "./use-current-account";
|
||||
import useSubject from "./use-subject";
|
||||
import { getDMRecipient, getDMSender } from "../helpers/nostr/dms";
|
||||
|
||||
export function useKind4Decrypt(event: NostrEvent, pubkey?: string) {
|
||||
const account = useCurrentAccount()!;
|
||||
|
||||
pubkey = pubkey || event.pubkey === account.pubkey ? getDMRecipient(event) : getDMSender(event);
|
||||
|
||||
const container = useMemo(
|
||||
() => decryptionCacheService.getOrCreateContainer(event.id, "nip04", pubkey, event.content),
|
||||
[event, pubkey],
|
||||
);
|
||||
|
||||
const plaintext = useSubject(container.plaintext);
|
||||
const error = useSubject(container.error);
|
||||
|
||||
const requestDecrypt = useCallback(() => {
|
||||
const p = decryptionCacheService.requestDecrypt(container);
|
||||
decryptionCacheService.startDecryptionQueue();
|
||||
return p;
|
||||
}, [container]);
|
||||
|
||||
return { container, error, plaintext, requestDecrypt };
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
import { PropsWithChildren, createContext, useCallback, useContext, useMemo, useRef } from "react";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import Subject from "../../classes/subject";
|
||||
import { useSigningContext } from "./signing-provider";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import createDefer, { Deferred } from "../../classes/deferred";
|
||||
|
||||
class DecryptionContainer {
|
||||
id = nanoid(8);
|
||||
pubkey: string;
|
||||
data: string;
|
||||
|
||||
plaintext = new Subject<string>();
|
||||
error = new Subject<Error>();
|
||||
|
||||
constructor(pubkey: string, data: string) {
|
||||
this.pubkey = pubkey;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
type DecryptionContextType = {
|
||||
getOrCreateContainer: (pubkey: string, data: string) => DecryptionContainer;
|
||||
startQueue: () => void;
|
||||
clearQueue: () => void;
|
||||
addToQueue: (container: DecryptionContainer) => Promise<string>;
|
||||
getQueue: () => DecryptionContainer[];
|
||||
};
|
||||
const DecryptionContext = createContext<DecryptionContextType>({
|
||||
getOrCreateContainer: () => {
|
||||
throw new Error("No DecryptionProvider");
|
||||
},
|
||||
startQueue: () => {},
|
||||
clearQueue: () => {},
|
||||
addToQueue: () => Promise.reject(new Error("No DecryptionProvider")),
|
||||
getQueue: () => [],
|
||||
});
|
||||
|
||||
export function useDecryptionContext() {
|
||||
return useContext(DecryptionContext);
|
||||
}
|
||||
export function useDecryptionContainer(pubkey: string, data: string) {
|
||||
const { getOrCreateContainer, addToQueue, startQueue } = useContext(DecryptionContext);
|
||||
const container = getOrCreateContainer(pubkey, data);
|
||||
|
||||
const plaintext = useSubject(container.plaintext);
|
||||
const error = useSubject(container.error);
|
||||
|
||||
const requestDecrypt = useCallback(() => {
|
||||
const p = addToQueue(container);
|
||||
startQueue();
|
||||
return p;
|
||||
}, [addToQueue, startQueue]);
|
||||
|
||||
return { container, error, plaintext, requestDecrypt };
|
||||
}
|
||||
|
||||
export default function DecryptionProvider({ children }: PropsWithChildren) {
|
||||
const { requestDecrypt } = useSigningContext();
|
||||
|
||||
const containers = useRef<DecryptionContainer[]>([]);
|
||||
const queue = useRef<DecryptionContainer[]>([]);
|
||||
const promises = useRef<Map<DecryptionContainer, Deferred<string>>>(new Map());
|
||||
const running = useRef<boolean>(false);
|
||||
|
||||
const getQueue = useCallback(() => queue.current, []);
|
||||
const clearQueue = useCallback(() => {
|
||||
queue.current = [];
|
||||
promises.current.clear();
|
||||
}, []);
|
||||
const addToQueue = useCallback((container: DecryptionContainer) => {
|
||||
queue.current.unshift(container);
|
||||
let p = promises.current.get(container);
|
||||
if (!p) {
|
||||
p = createDefer<string>();
|
||||
promises.current.set(container, p);
|
||||
}
|
||||
return p;
|
||||
}, []);
|
||||
|
||||
const getOrCreateContainer = useCallback((pubkey: string, data: string) => {
|
||||
let container = containers.current.find((c) => c.pubkey === pubkey && c.data === data);
|
||||
if (!container) {
|
||||
container = new DecryptionContainer(pubkey, data);
|
||||
containers.current.push(container);
|
||||
}
|
||||
return container;
|
||||
}, []);
|
||||
|
||||
const startQueue = useCallback(() => {
|
||||
if (running.current === true) return;
|
||||
running.current = false;
|
||||
|
||||
async function decryptNext() {
|
||||
if (running.current === true) return;
|
||||
|
||||
const container = queue.current.pop();
|
||||
if (!container) {
|
||||
running.current = false;
|
||||
promises.current.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = promises.current.get(container)!;
|
||||
|
||||
try {
|
||||
const plaintext = await requestDecrypt(container.data, container.pubkey);
|
||||
|
||||
// set plaintext
|
||||
container.plaintext.next(plaintext);
|
||||
promise.resolve(plaintext);
|
||||
|
||||
// remove promise
|
||||
promises.current.delete(container);
|
||||
|
||||
setTimeout(() => decryptNext(), 100);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
// set error
|
||||
container.error.next(e);
|
||||
promise.reject(e);
|
||||
|
||||
// clear queue
|
||||
running.current = false;
|
||||
queue.current = [];
|
||||
promises.current.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// start cycle
|
||||
decryptNext();
|
||||
}, [requestDecrypt]);
|
||||
|
||||
const context = useMemo(
|
||||
() => ({ getQueue, addToQueue, clearQueue, getOrCreateContainer, startQueue }),
|
||||
[getQueue, addToQueue, clearQueue, getOrCreateContainer, startQueue],
|
||||
);
|
||||
|
||||
return <DecryptionContext.Provider value={context}>{children}</DecryptionContext.Provider>;
|
||||
}
|
@ -8,7 +8,6 @@ import NotificationsProvider from "./notifications-provider";
|
||||
import { DefaultEmojiProvider, UserEmojiProvider } from "./emoji-provider";
|
||||
import { AllUserSearchDirectoryProvider } from "./user-directory-provider";
|
||||
import BreakpointProvider from "./breakpoint-provider";
|
||||
import DecryptionProvider from "./decryption-provider";
|
||||
import DMTimelineProvider from "./dms-provider";
|
||||
import PublishProvider from "./publish-provider";
|
||||
import WebOfTrustProvider from "./web-of-trust-provider";
|
||||
@ -26,19 +25,17 @@ export const GlobalProviders = ({ children }: { children: React.ReactNode }) =>
|
||||
<BreakpointProvider>
|
||||
<SigningProvider>
|
||||
<PublishProvider>
|
||||
<DecryptionProvider>
|
||||
<NotificationsProvider>
|
||||
<DMTimelineProvider>
|
||||
<DefaultEmojiProvider>
|
||||
<UserEmojiProvider>
|
||||
<AllUserSearchDirectoryProvider>
|
||||
<WebOfTrustProvider>{children}</WebOfTrustProvider>
|
||||
</AllUserSearchDirectoryProvider>
|
||||
</UserEmojiProvider>
|
||||
</DefaultEmojiProvider>
|
||||
</DMTimelineProvider>
|
||||
</NotificationsProvider>
|
||||
</DecryptionProvider>
|
||||
<NotificationsProvider>
|
||||
<DMTimelineProvider>
|
||||
<DefaultEmojiProvider>
|
||||
<UserEmojiProvider>
|
||||
<AllUserSearchDirectoryProvider>
|
||||
<WebOfTrustProvider>{children}</WebOfTrustProvider>
|
||||
</AllUserSearchDirectoryProvider>
|
||||
</UserEmojiProvider>
|
||||
</DefaultEmojiProvider>
|
||||
</DMTimelineProvider>
|
||||
</NotificationsProvider>
|
||||
</PublishProvider>
|
||||
</SigningProvider>
|
||||
</BreakpointProvider>
|
||||
|
@ -42,14 +42,14 @@ export function SigningProvider({ children }: { children: React.ReactNode }) {
|
||||
const requestDecrypt = useCallback(
|
||||
async (data: string, pubkey: string) => {
|
||||
if (!current) throw new Error("No account");
|
||||
return await signingService.requestDecrypt(data, pubkey, current);
|
||||
return await signingService.nip04Decrypt(data, pubkey, current);
|
||||
},
|
||||
[toast, current],
|
||||
);
|
||||
const requestEncrypt = useCallback(
|
||||
async (data: string, pubkey: string) => {
|
||||
if (!current) throw new Error("No account");
|
||||
return await signingService.requestEncrypt(data, pubkey, current);
|
||||
return await signingService.nip04Encrypt(data, pubkey, current);
|
||||
},
|
||||
[toast, current],
|
||||
);
|
||||
|
126
src/services/decryption-cache.ts
Normal file
126
src/services/decryption-cache.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import Subject from "../classes/subject";
|
||||
import _throttle from "lodash.throttle";
|
||||
|
||||
import createDefer, { Deferred } from "../classes/deferred";
|
||||
import signingService from "./signing";
|
||||
import accountService from "./account";
|
||||
import { logger } from "../helpers/debug";
|
||||
|
||||
type EncryptionType = "nip04" | "nip44";
|
||||
|
||||
class DecryptionContainer {
|
||||
/** event id */
|
||||
id: string;
|
||||
type: "nip04" | "nip44";
|
||||
pubkey: string;
|
||||
cipherText: string;
|
||||
|
||||
plaintext = new Subject<string>();
|
||||
error = new Subject<Error>();
|
||||
|
||||
constructor(id: string, type: EncryptionType = "nip04", pubkey: string, cipherText: string) {
|
||||
this.id = id;
|
||||
this.pubkey = pubkey;
|
||||
this.cipherText = cipherText;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
class DecryptionCache {
|
||||
containers = new Map<string, DecryptionContainer>();
|
||||
log = logger.extend("DecryptionCache");
|
||||
|
||||
getContainer(id: string) {
|
||||
return this.containers.get(id);
|
||||
}
|
||||
getOrCreateContainer(id: string, type: EncryptionType, pubkey: string, cipherText: string) {
|
||||
let container = this.containers.get(id);
|
||||
if (!container) {
|
||||
container = new DecryptionContainer(id, type, pubkey, cipherText);
|
||||
this.containers.set(id, container);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
private async decryptContainer(container: DecryptionContainer) {
|
||||
const account = accountService.current.value;
|
||||
if (!account) throw new Error("Missing account");
|
||||
|
||||
switch (container.type) {
|
||||
case "nip04":
|
||||
return await signingService.nip04Decrypt(container.cipherText, container.pubkey, account);
|
||||
case "nip44":
|
||||
return await signingService.nip44Decrypt(container.cipherText, container.pubkey, account);
|
||||
}
|
||||
}
|
||||
|
||||
promises = new Map<DecryptionContainer, Deferred<string>>();
|
||||
|
||||
private decryptQueue: DecryptionContainer[] = [];
|
||||
private decryptQueueRunning = false;
|
||||
private async decryptNext() {
|
||||
const container = this.decryptQueue.pop();
|
||||
if (!container) {
|
||||
this.decryptQueueRunning = false;
|
||||
this.decryptQueue = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = this.promises.get(container)!;
|
||||
|
||||
try {
|
||||
if (!container.plaintext.value) {
|
||||
const plaintext = await this.decryptContainer(container);
|
||||
|
||||
// set plaintext
|
||||
container.plaintext.next(plaintext);
|
||||
promise.resolve(plaintext);
|
||||
|
||||
// remove promise
|
||||
this.promises.delete(container);
|
||||
}
|
||||
|
||||
setTimeout(() => this.decryptNext(), 100);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
// set error
|
||||
container.error.next(e);
|
||||
promise.reject(e);
|
||||
|
||||
// clear queue
|
||||
this.decryptQueueRunning = false;
|
||||
this.decryptQueue = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startDecryptionQueue() {
|
||||
if (!this.decryptQueueRunning) {
|
||||
this.decryptQueueRunning = true;
|
||||
this.decryptNext();
|
||||
}
|
||||
}
|
||||
|
||||
requestDecrypt(container: DecryptionContainer) {
|
||||
if (container.plaintext.value) return Promise.resolve(container.plaintext.value);
|
||||
|
||||
let p = this.promises.get(container);
|
||||
if (!p) {
|
||||
p = createDefer<string>();
|
||||
this.promises.set(container, p);
|
||||
|
||||
this.decryptQueue.unshift(container);
|
||||
this.startDecryptionQueue();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
const decryptionCacheService = new DecryptionCache();
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
// @ts-expect-error
|
||||
window.decryptionCacheService = decryptionCacheService;
|
||||
}
|
||||
|
||||
export default decryptionCacheService;
|
@ -26,7 +26,7 @@ class SigningService {
|
||||
return signed;
|
||||
}
|
||||
|
||||
async requestEncrypt(plaintext: string, pubkey: string, account: Account) {
|
||||
async nip04Encrypt(plaintext: string, pubkey: string, account: Account) {
|
||||
if (account.readonly) throw new Error("Can not encrypt in readonly mode");
|
||||
await this.unlockAccount(account);
|
||||
|
||||
@ -35,7 +35,7 @@ class SigningService {
|
||||
return account.signer.nip04.encrypt(pubkey, plaintext);
|
||||
}
|
||||
|
||||
async requestDecrypt(ciphertext: string, pubkey: string, account: Account) {
|
||||
async nip04Decrypt(ciphertext: string, pubkey: string, account: Account) {
|
||||
if (account.readonly) throw new Error("Can not decrypt in readonly mode");
|
||||
await this.unlockAccount(account);
|
||||
|
||||
@ -43,6 +43,24 @@ class SigningService {
|
||||
if (!account.signer.nip04) throw new Error("Signer does not support NIP-04");
|
||||
return account.signer.nip04.decrypt(pubkey, ciphertext);
|
||||
}
|
||||
|
||||
async nip44Encrypt(plaintext: string, pubkey: string, account: Account) {
|
||||
if (account.readonly) throw new Error("Can not encrypt in readonly mode");
|
||||
await this.unlockAccount(account);
|
||||
|
||||
if (!account.signer) throw new Error("Account missing signer");
|
||||
if (!account.signer.nip44) throw new Error("Signer does not support NIP-44");
|
||||
return account.signer.nip44.encrypt(pubkey, plaintext);
|
||||
}
|
||||
|
||||
async nip44Decrypt(ciphertext: string, pubkey: string, account: Account) {
|
||||
if (account.readonly) throw new Error("Can not decrypt in readonly mode");
|
||||
await this.unlockAccount(account);
|
||||
|
||||
if (!account.signer) throw new Error("Account missing signer");
|
||||
if (!account.signer.nip44) throw new Error("Signer does not support NIP-44");
|
||||
return account.signer.nip44.decrypt(pubkey, ciphertext);
|
||||
}
|
||||
}
|
||||
|
||||
const signingService = new SigningService();
|
||||
|
@ -3,7 +3,7 @@ import { Button, ButtonGroup, Card, Flex, IconButton } from "@chakra-ui/react";
|
||||
import { UNSAFE_DataRouterContext, useLocation, useNavigate } from "react-router-dom";
|
||||
import { NostrEvent, kinds } from "nostr-tools";
|
||||
|
||||
import { ChevronLeftIcon, ThreadIcon } from "../../components/icons";
|
||||
import { ThreadIcon } from "../../components/icons";
|
||||
import UserAvatar from "../../components/user/user-avatar";
|
||||
import UserLink from "../../components/user/user-link";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
@ -14,7 +14,6 @@ import IntersectionObserverProvider from "../../providers/local/intersection-obs
|
||||
import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-cursor-intersection-callback";
|
||||
import TimelineActionAndStatus from "../../components/timeline/timeline-action-and-status";
|
||||
import UserDnsIdentity from "../../components/user/user-dns-identity";
|
||||
import { useDecryptionContext } from "../../providers/global/decryption-provider";
|
||||
import SendMessageForm from "./components/send-message-form";
|
||||
import { groupMessages } from "../../helpers/nostr/dms";
|
||||
import ThreadDrawer from "./components/thread-drawer";
|
||||
@ -27,7 +26,8 @@ import RelaySet from "../../classes/relay-set";
|
||||
import useAppSettings from "../../hooks/use-app-settings";
|
||||
import { truncateId } from "../../helpers/string";
|
||||
import useRouterMarker from "../../hooks/use-router-marker";
|
||||
import BackButton, { BackIconButton } from "../../components/router/back-button";
|
||||
import { BackIconButton } from "../../components/router/back-button";
|
||||
import decryptionCacheService from "../../services/decryption-cache";
|
||||
|
||||
/** This is broken out from DirectMessageChatPage for performance reasons. Don't use outside of file */
|
||||
const ChatLog = memo(({ timeline }: { timeline: TimelineLoader }) => {
|
||||
@ -52,7 +52,6 @@ function DirectMessageChatPage({ pubkey }: { pubkey: string }) {
|
||||
const { autoDecryptDMs } = useAppSettings();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { getOrCreateContainer, addToQueue, startQueue } = useDecryptionContext();
|
||||
|
||||
const { router } = useContext(UNSAFE_DataRouterContext)!;
|
||||
const marker = useRouterMarker(router);
|
||||
@ -104,13 +103,11 @@ function DirectMessageChatPage({ pubkey }: { pubkey: string }) {
|
||||
const decryptAll = async () => {
|
||||
const promises = timeline.timeline.value
|
||||
.map((message) => {
|
||||
const container = getOrCreateContainer(pubkey, message.content);
|
||||
if (container.plaintext.value === undefined) return addToQueue(container);
|
||||
const container = decryptionCacheService.getOrCreateContainer(message.id, "nip04", pubkey, message.content);
|
||||
return decryptionCacheService.requestDecrypt(container);
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
startQueue();
|
||||
|
||||
setLoading(true);
|
||||
Promise.all(promises).finally(() => setLoading(false));
|
||||
};
|
||||
|
@ -3,11 +3,9 @@ import { Alert, AlertDescription, AlertIcon, Button, ButtonProps } from "@chakra
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import { UnlockIcon } from "../../../components/icons";
|
||||
import { useDecryptionContainer } from "../../../providers/global/decryption-provider";
|
||||
import useCurrentAccount from "../../../hooks/use-current-account";
|
||||
import { getDMRecipient, getDMSender } from "../../../helpers/nostr/dms";
|
||||
import DebugEventButton from "../../../components/debug-modal/debug-event-button";
|
||||
import useAppSettings from "../../../hooks/use-app-settings";
|
||||
import { useKind4Decrypt } from "../../../hooks/use-kind4-decryption";
|
||||
|
||||
export default function DecryptPlaceholder({
|
||||
children,
|
||||
@ -17,14 +15,9 @@ export default function DecryptPlaceholder({
|
||||
children: (decrypted: string) => JSX.Element;
|
||||
message: NostrEvent;
|
||||
} & Omit<ButtonProps, "children">): JSX.Element {
|
||||
const account = useCurrentAccount();
|
||||
const { autoDecryptDMs } = useAppSettings();
|
||||
const isOwn = account?.pubkey === message.pubkey;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { requestDecrypt, plaintext, error } = useDecryptionContainer(
|
||||
isOwn ? getDMRecipient(message) : getDMSender(message),
|
||||
message.content,
|
||||
);
|
||||
const { requestDecrypt, plaintext, error } = useKind4Decrypt(message);
|
||||
|
||||
const decrypt = async () => {
|
||||
setLoading(true);
|
||||
|
@ -8,10 +8,10 @@ import { useSigningContext } from "../../../providers/global/signing-provider";
|
||||
import MagicTextArea, { RefType } from "../../../components/magic-textarea";
|
||||
import { useTextAreaUploadFileWithForm } from "../../../hooks/use-textarea-upload-file";
|
||||
import { DraftNostrEvent } from "../../../types/nostr-event";
|
||||
import { useDecryptionContext } from "../../../providers/global/decryption-provider";
|
||||
import useUserMailboxes from "../../../hooks/use-user-mailboxes";
|
||||
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
||||
import useCacheForm from "../../../hooks/use-cache-form";
|
||||
import decryptionCacheService from "../../../services/decryption-cache";
|
||||
|
||||
export default function SendMessageForm({
|
||||
pubkey,
|
||||
@ -20,7 +20,6 @@ export default function SendMessageForm({
|
||||
}: { pubkey: string; rootId?: string } & Omit<FlexProps, "children">) {
|
||||
const publish = usePublishEvent();
|
||||
const { requestEncrypt } = useSigningContext();
|
||||
const { getOrCreateContainer } = useDecryptionContext();
|
||||
|
||||
const [loadingMessage, setLoadingMessage] = useState("");
|
||||
const { getValues, setValue, watch, handleSubmit, formState, reset } = useForm({
|
||||
@ -64,7 +63,9 @@ export default function SendMessageForm({
|
||||
reset({ content: "" });
|
||||
|
||||
// add plaintext to decryption context
|
||||
getOrCreateContainer(pubkey, encrypted).plaintext.next(values.content);
|
||||
decryptionCacheService
|
||||
.getOrCreateContainer(pub.event.id, "nip04", pubkey, encrypted)
|
||||
.plaintext.next(values.content);
|
||||
|
||||
// refocus input
|
||||
setTimeout(() => textAreaRef.current?.focus(), 50);
|
||||
|
@ -27,8 +27,8 @@ import { Thread, useThreadsContext } from "../../../providers/local/thread-provi
|
||||
import ThreadButton from "../../../components/message/thread-button";
|
||||
import SendMessageForm from "./send-message-form";
|
||||
import { groupMessages } from "../../../helpers/nostr/dms";
|
||||
import { useDecryptionContext } from "../../../providers/global/decryption-provider";
|
||||
import DirectMessageBlock from "./direct-message-block";
|
||||
import decryptionCacheService from "../../../services/decryption-cache";
|
||||
|
||||
function MessagePreview({ message, ...props }: { message: NostrEvent } & Omit<TextProps, "children">) {
|
||||
return (
|
||||
@ -102,7 +102,6 @@ export default function ThreadDrawer({
|
||||
...props
|
||||
}: Omit<DrawerProps, "children"> & { threadId: string; pubkey: string }) {
|
||||
const { threads, getRoot } = useThreadsContext();
|
||||
const { startQueue, getOrCreateContainer, addToQueue } = useDecryptionContext();
|
||||
|
||||
const thread = threads[threadId];
|
||||
const [loading, setLoading] = useState(false);
|
||||
@ -111,18 +110,21 @@ export default function ThreadDrawer({
|
||||
|
||||
const promises = thread.messages
|
||||
.map((message) => {
|
||||
const container = getOrCreateContainer(pubkey, message.content);
|
||||
if (container.plaintext.value === undefined) return addToQueue(container);
|
||||
const container = decryptionCacheService.getOrCreateContainer(message.id, "nip04", pubkey, message.content);
|
||||
if (container.plaintext.value === undefined) return decryptionCacheService.requestDecrypt(container);
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
if (thread.root) {
|
||||
const rootContainer = getOrCreateContainer(pubkey, thread.root.content);
|
||||
if (rootContainer.plaintext.value === undefined) addToQueue(rootContainer);
|
||||
const rootContainer = decryptionCacheService.getOrCreateContainer(
|
||||
thread.root.id,
|
||||
"nip04",
|
||||
pubkey,
|
||||
thread.root.content,
|
||||
);
|
||||
if (rootContainer.plaintext.value === undefined) decryptionCacheService.requestDecrypt(rootContainer);
|
||||
}
|
||||
|
||||
startQueue();
|
||||
|
||||
setLoading(true);
|
||||
Promise.all(promises).finally(() => setLoading(false));
|
||||
};
|
||||
|
@ -16,16 +16,16 @@ import { useTimelineCurserIntersectionCallback } from "../../hooks/use-timeline-
|
||||
import TimelineActionAndStatus from "../../components/timeline/timeline-action-and-status";
|
||||
import { useDMTimeline } from "../../providers/global/dms-provider";
|
||||
import UserName from "../../components/user/user-name";
|
||||
import { useDecryptionContainer } from "../../providers/global/decryption-provider";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { CheckIcon } from "../../components/icons";
|
||||
import UserDnsIdentity from "../../components/user/user-dns-identity";
|
||||
import useEventIntersectionRef from "../../hooks/use-event-intersection-ref";
|
||||
import { useKind4Decrypt } from "../../hooks/use-kind4-decryption";
|
||||
|
||||
function MessagePreview({ message, pubkey }: { message: NostrEvent; pubkey: string }) {
|
||||
const ref = useEventIntersectionRef(message);
|
||||
|
||||
const { plaintext } = useDecryptionContainer(pubkey, message.content);
|
||||
const { plaintext } = useKind4Decrypt(message);
|
||||
return (
|
||||
<Text isTruncated ref={ref}>
|
||||
{plaintext || "<Encrypted>"}
|
||||
|
@ -19,11 +19,12 @@ import UserAvatar from "../../../components/user/user-avatar";
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import UserName from "../../../components/user/user-name";
|
||||
import UserDnsIdentity from "../../../components/user/user-dns-identity";
|
||||
import { useDecryptionContainer, useDecryptionContext } from "../../../providers/global/decryption-provider";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import { useKind4Decrypt } from "../../../hooks/use-kind4-decryption";
|
||||
import decryptionCacheService from "../../../services/decryption-cache";
|
||||
|
||||
function MessagePreview({ message, pubkey }: { message: NostrEvent; pubkey: string }) {
|
||||
const { plaintext } = useDecryptionContainer(pubkey, message.content);
|
||||
const { plaintext } = useKind4Decrypt(message);
|
||||
return <Text isTruncated>{plaintext || "<Encrypted>"}</Text>;
|
||||
}
|
||||
|
||||
@ -50,7 +51,6 @@ function Conversation({ conversation }: { conversation: KnownConversation }) {
|
||||
export default function DMsCard({ ...props }: Omit<CardProps, "children">) {
|
||||
const navigate = useNavigate();
|
||||
const account = useCurrentAccount()!;
|
||||
const { getOrCreateContainer, addToQueue, startQueue } = useDecryptionContext();
|
||||
|
||||
const timeline = useDMTimeline();
|
||||
|
||||
@ -74,13 +74,16 @@ export default function DMsCard({ ...props }: Omit<CardProps, "children">) {
|
||||
const last = conversation.messages.find((m) => m.pubkey === conversation.correspondent);
|
||||
if (!last) return;
|
||||
|
||||
const container = getOrCreateContainer(conversation.correspondent, last.content);
|
||||
if (container.plaintext.value === undefined) return addToQueue(container);
|
||||
const container = decryptionCacheService.getOrCreateContainer(
|
||||
last.id,
|
||||
"nip04",
|
||||
conversation.correspondent,
|
||||
last.content,
|
||||
);
|
||||
return decryptionCacheService.requestDecrypt(container);
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
startQueue();
|
||||
|
||||
setLoading(true);
|
||||
Promise.all(promises).finally(() => setLoading(false));
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user