Skip to content

Commit

Permalink
feat: sse
Browse files Browse the repository at this point in the history
  • Loading branch information
apotdevin committed Jun 22, 2024
1 parent ff4e9b4 commit 72c5b08
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 7 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@apollo/client": "^3.10.5",
"@apollo/experimental-nextjs-app-support": "^0.11.2",
"@hookform/resolvers": "^3.6.0",
"@microsoft/fetch-event-source": "^2.0.1",
"@noble/ciphers": "^0.5.3",
"@noble/hashes": "^1.4.0",
"@noble/secp256k1": "^2.1.0",
Expand Down
17 changes: 16 additions & 1 deletion src/app/app/contacts/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
import { cookies } from 'next/headers';

import { EventHandler } from '@/components/events/Events';
import { Contacts } from '@/views/contacts/Contacts';

export default function Page() {
return <Contacts />;
const eventsUrl = process.env.EVENTS_URL;

const cookieStore = cookies();
const accessToken = cookieStore.get('amboss_banco_access_token')?.value;

return (
<>
{accessToken && eventsUrl ? (
<EventHandler accessToken={accessToken} eventsUrl={eventsUrl} />
) : null}
<Contacts />
</>
);
}
82 changes: 82 additions & 0 deletions src/components/events/Events.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use client';

import { useApolloClient } from '@apollo/client';
import {
EventStreamContentType,
fetchEventSource,
} from '@microsoft/fetch-event-source';
import { FC, useCallback, useEffect } from 'react';

import { useToast } from '../ui/use-toast';

class RetriableError extends Error {}
class FatalError extends Error {}

export const EventHandler: FC<{
accessToken: string;
eventsUrl: string;
}> = ({ accessToken, eventsUrl }) => {
const client = useApolloClient();
const { toast } = useToast();

const showToast = useCallback(
(sender: string | undefined) => {
if (!sender) return;
toast({
title: 'New Message',
description: sender
? `You have a new message from ${sender}`
: undefined,
});
},
[toast]
);

useEffect(() => {
const startSource = async () => {
try {
await fetchEventSource(`${eventsUrl}/contacts`, {
headers: { Authorization: `Bearer ${accessToken}` },
async onopen(response) {
if (
response.ok &&
response.headers.get('content-type') === EventStreamContentType
) {
return;
} else if (
response.status >= 400 &&
response.status < 500 &&
response.status !== 429
) {
throw new FatalError();
} else {
throw new RetriableError();
}
},
onmessage(ev) {
try {
const parsed = JSON.parse(ev.data);
showToast(parsed.sender_money_address);
} catch (error) {}

client.reFetchObservableQueries();
},
onclose() {
throw new RetriableError();
},
onerror(err) {
if (err instanceof FatalError) {
throw err;
} else {
return 2000;
}
},
});
} catch (error) {}
};

startSource();
}, [accessToken, client, showToast, eventsUrl]);

return null;
};
2 changes: 2 additions & 0 deletions src/graphql/queries/__generated__/contacts.generated.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/graphql/queries/contacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const GetWalletContactMessages = gql`
id
contact_is_sender
payload
created_at
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/graphql/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export type BroadcastLiquidTransactionInput = {
export type ContactMessage = {
__typename?: 'ContactMessage';
contact_is_sender: Scalars['Boolean']['output'];
created_at: Scalars['String']['output'];
id: Scalars['String']['output'];
payload: Scalars['String']['output'];
};
Expand Down
14 changes: 10 additions & 4 deletions src/views/contacts/Messages.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { format } from 'date-fns';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useLocalStorage } from 'usehooks-ts';

Expand All @@ -18,12 +19,13 @@ type Message = {
id: string;
contact_is_sender: boolean;
message: string;
created_at: string;
};

export const Messages = () => {
const workerRef = useRef<Worker>();

const [unecryptedMessage, setUnencryptedMessage] = useState<Message[]>([]);
const [decryptedMessage, setDecryptedMessage] = useState<Message[]>([]);

const [workerLoaded, setWorkerLoaded] = useState<boolean>(false);

Expand All @@ -40,14 +42,15 @@ export const Messages = () => {

const messages = useMemo(() => {
if (!data?.wallets.find_one.contacts.find_one.messages.length) return [];
if (unecryptedMessage.length) return unecryptedMessage;
if (decryptedMessage.length) return decryptedMessage;

return data.wallets.find_one.contacts.find_one.messages.map(m => ({
id: m.id,
contact_is_sender: m.contact_is_sender,
message: m.payload,
created_at: m.created_at,
}));
}, [unecryptedMessage, data]);
}, [decryptedMessage, data]);

useEffect(() => {
if (!masterKey) return;
Expand Down Expand Up @@ -82,7 +85,7 @@ export const Messages = () => {

switch (message.type) {
case 'decryptMessages':
setUnencryptedMessage(message.payload);
setDecryptedMessage(message.payload);
break;

case 'loaded':
Expand Down Expand Up @@ -123,6 +126,9 @@ export const Messages = () => {
{m.message.substring(0, 1) === '{'
? 'Encrypted message'
: m.message}
<p className="mt-2 text-xs text-muted-foreground">
{format(new Date(m.created_at), 'yyyy.MM.dd - HH:mm')}
</p>
</div>
))}
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/views/wallet/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,13 @@ export const WalletSettings: FC<{ walletId: string }> = ({ walletId }) => {
disabled={loading || !!mnemonic || !masterKey}
onClick={() => handleDecrypt()}
>
Unencrypt
Decrypt
</Button>
</div>
</div>

<div className="mt-2">
<Label htmlFor="mnemonic">Unencrypted</Label>
<Label htmlFor="mnemonic">Decrypted</Label>
<div className="flex gap-2">
<Input
id="mnemonic"
Expand Down
2 changes: 2 additions & 0 deletions src/workers/crypto/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type CryptoWorkerMessage =
id: string;
contact_is_sender: boolean;
payload: string;
created_at: string;
}[];
};
};
Expand Down Expand Up @@ -96,6 +97,7 @@ export type CryptoWorkerResponse =
id: string;
contact_is_sender: boolean;
message: string;
created_at: string;
}[];
}
| { type: 'loaded' }
Expand Down

0 comments on commit 72c5b08

Please sign in to comment.