Skip to content

Commit

Permalink
chore: worker cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
apotdevin committed Jun 15, 2024
1 parent 5ec8003 commit 637f076
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 117 deletions.
4 changes: 2 additions & 2 deletions src/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,8 @@ export type SendMessage = {
export type SendMessageInput = {
contact_id: Scalars['String']['input'];
receiver_money_address: Scalars['String']['input'];
receiver_payload: Scalars['String']['input'];
sender_payload: Scalars['String']['input'];
receiver_payload?: InputMaybe<Scalars['String']['input']>;
sender_payload?: InputMaybe<Scalars['String']['input']>;
};

export type SignUpInput = {
Expand Down
139 changes: 139 additions & 0 deletions src/hooks/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { useApolloClient } from '@apollo/client';
import { useEffect, useRef, useState } from 'react';
import { useLocalStorage } from 'usehooks-ts';

import { useToast } from '@/components/ui/use-toast';
import {
SendMessageDocument,
SendMessageMutation,
SendMessageMutationVariables,
} from '@/graphql/mutations/__generated__/contact.generated';
import { GetWalletContactMessagesDocument } from '@/graphql/queries/__generated__/contacts.generated';
import { toWithError } from '@/utils/async';
import { LOCALSTORAGE_KEYS } from '@/utils/constants';
import {
CryptoWorkerMessage,
CryptoWorkerResponse,
} from '@/workers/crypto/types';

export const useSendMessage = (cbk: () => void) => {
const workerRef = useRef<Worker>();
const client = useApolloClient();

const { toast } = useToast();

const [loading, setLoading] = useState<boolean>(true);

const [value] = useLocalStorage(LOCALSTORAGE_KEYS.currentWalletId, '');

useEffect(() => {
workerRef.current = new Worker(
new URL('../workers/crypto/crypto.ts', import.meta.url)
);

workerRef.current.onmessage = async event => {
const message: CryptoWorkerResponse = event.data;

switch (message.type) {
case 'encryptMessage':
const {
sender_payload,
receiver_money_address,
receiver_payload,
contact_id,
} = message.payload;

const [, error] = await toWithError(
client.mutate<SendMessageMutation, SendMessageMutationVariables>({
mutation: SendMessageDocument,
variables: {
input: {
contact_id,
receiver_money_address,
receiver_payload,
sender_payload,
},
},
refetchQueries: [
{
query: GetWalletContactMessagesDocument,
variables: {
id: value,
contact_id,
},
},
],
})
);

if (error) {
console.log(error);
toast({
variant: 'default',
title: 'Unable to send message',
description: 'No encryption pubkey found for this contact',
});
} else {
cbk();
}

break;

case 'loaded':
setLoading(false);
}

setLoading(false);
};

workerRef.current.onerror = error => {
console.error('Worker error:', error);
setLoading(false);
};

return () => {
if (workerRef.current) workerRef.current.terminate();
};
}, [client, toast, value, cbk]);

const sendMessage = async ({
contact_id,
masterKey,
protectedPrivateKey,
receiver_pubkey,
receiver_money_address,
sender_message,
receiver_message,
}: {
contact_id: string;
masterKey: string;
protectedPrivateKey: string;
receiver_pubkey: string;
receiver_money_address: string;
sender_message: string;
receiver_message: string;
}) => {
if (loading) return;

if (workerRef.current) {
setLoading(true);

const workerMessage: CryptoWorkerMessage = {
type: 'encryptMessage',
payload: {
contact_id,
protectedPrivateKey,
masterKey,
receiver_pubkey,
receiver_money_address,
sender_message,
receiver_message,
},
};

workerRef.current.postMessage(workerMessage);
}
};

return { sendMessage, loading };
};
149 changes: 43 additions & 106 deletions src/views/contacts/boxes/MessageBox.tsx
Original file line number Diff line number Diff line change
@@ -1,67 +1,38 @@
'use client';

import { CornerDownLeft, Loader2 } from 'lucide-react';
import { FC, ReactNode, useEffect, useRef, useState } from 'react';
import { FC, ReactNode, useCallback, useState } from 'react';
import { useLocalStorage } from 'usehooks-ts';

import { VaultButton } from '@/components/button/VaultButton';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { useToast } from '@/components/ui/use-toast';
import { useSendMessageMutation } from '@/graphql/mutations/__generated__/contact.generated';
import {
GetWalletContactMessagesDocument,
useGetWalletContactQuery,
} from '@/graphql/queries/__generated__/contacts.generated';
import { useGetWalletContactQuery } from '@/graphql/queries/__generated__/contacts.generated';
import { useSendMessage } from '@/hooks/message';
import { useContactStore } from '@/stores/contacts';
import { useKeyStore } from '@/stores/keys';
import { LOCALSTORAGE_KEYS } from '@/utils/constants';
import { handleApolloError } from '@/utils/error';
import {
CryptoWorkerMessage,
CryptoWorkerResponse,
} from '@/workers/crypto/types';

export const SendMessageBox: FC<{ iconOptions?: ReactNode }> = ({
iconOptions,
}) => {
const workerRef = useRef<Worker>();
const [message, setMessage] = useState<string>('');

const cbk = useCallback(() => setMessage(''), []);

const { sendMessage, loading: sendLoading } = useSendMessage(cbk);

const { toast } = useToast();

const masterKey = useKeyStore(s => s.masterKey);

const [message, setMessage] = useState<string>('');
const [formLoading, setFormLoading] = useState<boolean>(false);

const currentContact = useContactStore(s => s.currentContact);

const [value] = useLocalStorage(LOCALSTORAGE_KEYS.currentWalletId, '');

const [sendMessage, { loading: sendMessageLoading }] = useSendMessageMutation(
{
onCompleted: () => {
setMessage('');
},
onError: err => {
const messages = handleApolloError(err);

toast({
variant: 'destructive',
title: 'Error Sending Message',
description: messages.join(', '),
});
},
refetchQueries: [
{
query: GetWalletContactMessagesDocument,
variables: { id: value, contact_id: currentContact?.id || '' },
},
],
}
);

const { data, loading } = useGetWalletContactQuery({
variables: { id: value, contact_id: currentContact?.id || '' },
skip: !currentContact?.id,
Expand All @@ -76,56 +47,11 @@ export const SendMessageBox: FC<{ iconOptions?: ReactNode }> = ({
},
});

useEffect(() => {
workerRef.current = new Worker(
new URL('../../../workers/crypto/crypto.ts', import.meta.url)
);

workerRef.current.onmessage = event => {
const message: CryptoWorkerResponse = event.data;

switch (message.type) {
case 'encryptMessage':
if (!currentContact?.id) {
toast({
variant: 'destructive',
title: 'No Contact Selected',
description: 'Select a contact to send them a message',
});
} else {
const { sender_payload, receiver_money_address, receiver_payload } =
message.payload;

sendMessage({
variables: {
input: {
contact_id: currentContact.id,
receiver_money_address,
receiver_payload,
sender_payload,
},
},
});
}
break;
}

setFormLoading(false);
};

workerRef.current.onerror = error => {
console.error('Worker error:', error);
setFormLoading(false);
};

return () => {
if (workerRef.current) workerRef.current.terminate();
};
}, [sendMessage, currentContact, toast]);

const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

if (sendLoading) return;

if (!data?.wallets.find_one.contacts.find_one.encryption_pubkey) {
toast({
variant: 'default',
Expand All @@ -136,31 +62,42 @@ export const SendMessageBox: FC<{ iconOptions?: ReactNode }> = ({
return;
}

if (!masterKey) return;

if (workerRef.current) {
setFormLoading(true);

const workerMessage: CryptoWorkerMessage = {
type: 'encryptMessage',
payload: {
protectedPrivateKey:
data.wallets.find_one.secp256k1_key_pair
.protected_encryption_private_key,
masterKey,
receiver_pubkey:
data.wallets.find_one.contacts.find_one.encryption_pubkey,
receiver_money_address:
data.wallets.find_one.contacts.find_one.money_address,
msg: message,
},
};

workerRef.current.postMessage(workerMessage);
if (!masterKey) {
toast({
variant: 'destructive',
title: 'Vault Locked',
description: 'Unlock your vault to send a message',
});

return;
}

if (!currentContact?.id) {
toast({
variant: 'destructive',
title: 'No Contact Selected',
description: 'Select a contact to send them a message',
});

return;
}

sendMessage({
contact_id: currentContact.id,
protectedPrivateKey:
data.wallets.find_one.secp256k1_key_pair
.protected_encryption_private_key,
masterKey,
receiver_pubkey:
data.wallets.find_one.contacts.find_one.encryption_pubkey,
receiver_money_address:
data.wallets.find_one.contacts.find_one.money_address,
sender_message: message,
receiver_message: message,
});
};

const isLoading = loading || formLoading || sendMessageLoading;
const isLoading = loading || sendLoading;

return (
<form
Expand Down
19 changes: 11 additions & 8 deletions src/workers/crypto/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,13 @@ self.onmessage = async e => {

case 'encryptMessage': {
const {
protectedPrivateKey,
contact_id,
masterKey,
protectedPrivateKey,
receiver_money_address,
receiver_pubkey,
msg,
sender_message,
receiver_message,
} = message.payload;

const unprotectedPrivateKey = nip44.v2.decrypt(
Expand All @@ -178,21 +180,22 @@ self.onmessage = async e => {

const publicKey = getPublicKey(unprotectedPrivateKey).subarray(1, 33);

const receiver_payload = encryptMessage(
msg,
const sender_payload = encryptMessage(
sender_message,
hexToUint8Array(unprotectedPrivateKey),
receiver_pubkey.substring(2)
bufToHex(publicKey)
);

const sender_payload = encryptMessage(
msg,
const receiver_payload = encryptMessage(
receiver_message,
hexToUint8Array(unprotectedPrivateKey),
bufToHex(publicKey)
receiver_pubkey.substring(2)
);

const response: CryptoWorkerResponse = {
type: 'encryptMessage',
payload: {
contact_id,
receiver_money_address,
receiver_payload: JSON.stringify(receiver_payload),
sender_payload: JSON.stringify(sender_payload),
Expand Down
Loading

0 comments on commit 637f076

Please sign in to comment.