diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx b/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx
index 6f6f4c2039..dbeb6181d2 100644
--- a/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx
+++ b/sdk/typescript/examples/react-webpack-with-theme-example/src/App.tsx
@@ -5,9 +5,12 @@ import {
Chip,
CircularProgress,
Container,
+ FormControlLabel,
+ FormGroup,
InputAdornment,
Link,
Stack,
+ Switch,
TextField,
Tooltip,
Typography,
@@ -20,6 +23,8 @@ import CallReceivedIcon from '@mui/icons-material/CallReceived';
import PersonIcon from '@mui/icons-material/Person';
import PersonOffIcon from '@mui/icons-material/PersonOff';
import ErrorIcon from '@mui/icons-material/Error';
+import VisibilityIcon from '@mui/icons-material/Visibility';
+import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import { NymLogo } from '@nymproject/react/logo/NymLogo';
import { NymThemeProvider } from '@nymproject/mui-theme';
import { useTheme } from '@mui/material/styles';
@@ -28,9 +33,10 @@ import { DropzoneDialog } from 'react-mui-dropzone';
import UploadFileIcon from '@mui/icons-material/UploadFile';
import ArticleIcon from '@mui/icons-material/Article';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
+import { Headers } from '@nymproject/sdk';
import { ThemeToggle } from './ThemeToggle';
import { AppContextProvider, useAppContext } from './context';
-import { MixnetContextProvider, parseBinaryMessageHeaders, useMixnetContext } from './context/mixnet';
+import { BinaryMessageHeaders, MixnetContextProvider, useMixnetContext } from './context/mixnet';
export const AppTheme: React.FC = ({ children }) => {
const { mode } = useAppContext();
@@ -45,6 +51,7 @@ interface Log {
fileDownloadUrl?: string;
filesize?: number;
timestamp: Date;
+ headers?: Headers;
}
interface UploadState {
@@ -52,11 +59,48 @@ interface UploadState {
files: File[];
}
+const ClientAddress: React.FC<{ label?: string; tooltip?: string; address?: string }> = ({
+ label,
+ address,
+ tooltip,
+}) => {
+ const copy = useClipboard();
+
+ if (!address) {
+ return } />;
+ }
+
+ const addressShort = `${address.slice(0, 24)}...`;
+
+ return (
+
+
+ {label} {addressShort}
+ >
+ ) : (
+ <>{addressShort}>
+ )
+ }
+ onClick={() => {
+ if (address) {
+ copy.copy(address);
+ }
+ }}
+ icon={}
+ />
+
+ );
+};
+
export const Content: React.FC = () => {
const theme = useTheme();
const { isReady, address, connect, events, sendTextMessage, sendBinaryMessage } = useMixnetContext();
- const copy = useClipboard();
+ const [revealSenderAddress, setRevealSenderAddress] = React.useState(false);
const [sendToSelf, setSendToSelf] = React.useState(false);
const [recipient, setRecipient] = React.useState();
const handleRecipientChange = (event: React.ChangeEvent) => {
@@ -94,13 +138,8 @@ export const Content: React.FC = () => {
React.useEffect(() => {
if (isReady) {
- // // mixnet v1
- // const validatorApiUrl = 'https://validator.nymtech.net/api';
- // const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
-
- // mixnet v2
- const validatorApiUrl = 'https://qwerty-validator-api.qa.nymte.ch/api'; // "http://localhost:8081";
- const preferredGatewayIdentityKey = undefined; // '36vfvEyBzo5cWEFbnP7fqgY39kFw9PQhvwzbispeNaxL';
+ const validatorApiUrl = 'https://validator.nymtech.net/api';
+ const preferredGatewayIdentityKey = 'E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM';
connect({
clientId: 'Example Client',
@@ -117,6 +156,7 @@ export const Content: React.FC = () => {
kind: 'rx',
timestamp: new Date(),
message: e.args.payload,
+ headers: e.args.headers,
});
setLogTrigger(Date.now());
});
@@ -142,8 +182,12 @@ export const Content: React.FC = () => {
React.useEffect(() => {
if (events) {
const unsubcribe = events.subscribeToBinaryMessageReceivedEvent((e) => {
- // the headers will be JSON (see the mixnet context for how they are created), so parse them
- const headers = parseBinaryMessageHeaders(e.args.headers);
+ if (!e.args.headers) {
+ console.error('Expected headers, got undefined 😢', e.args);
+ return;
+ }
+
+ const headers = e.args.headers as BinaryMessageHeaders;
const blob = new Blob([new Uint8Array(e.args.payload)], { type: headers.mimeType });
log.current.push({
@@ -152,6 +196,7 @@ export const Content: React.FC = () => {
filename: headers.filename,
fileDownloadUrl: URL.createObjectURL(blob),
filesize: e.args.payload.length,
+ headers: e.args.headers,
});
setLogTrigger(Date.now());
});
@@ -184,7 +229,8 @@ export const Content: React.FC = () => {
message,
});
setLogTrigger(Date.now());
- await sendTextMessage({ payload: message, recipient });
+ const senderAddress = revealSenderAddress ? address : undefined;
+ await sendTextMessage({ payload: message, recipient, headers: { senderAddress } });
await Promise.all(
files.map(async (f) => {
@@ -200,7 +246,7 @@ export const Content: React.FC = () => {
return await sendBinaryMessage({
payload: new Uint8Array(buffer),
recipient,
- headers: { filename: f.name, mimeType: f.type },
+ headers: { filename: f.name, mimeType: f.type, senderAddress },
});
} catch (e) {
addErrorLog('Failed to send file', f.name);
@@ -249,20 +295,7 @@ export const Content: React.FC = () => {
) : (
<>
} label="Connected" variant="outlined" />
- {address && (
-
- {
- if (address) {
- copy.copy(address);
- }
- }}
- icon={}
- />
-
- )}
+
>
)}
@@ -347,9 +380,34 @@ export const Content: React.FC = () => {
/>
-
+
+
+
+ setRevealSenderAddress((prevState) => !prevState)}
+ />
+ }
+ label={
+ revealSenderAddress ? (
+
+
+ Reveal your address to the recipient
+
+ ) : (
+
+
+ Hide your address from the recipient
+
+ )
+ }
+ />
+
+
)}
@@ -398,6 +456,16 @@ export const Content: React.FC = () => {
)}
>
)}
+ {item.kind === 'rx' &&
+ (item.headers?.senderAddress ? (
+
+ ) : (
+
+ ))}
))}
diff --git a/sdk/typescript/examples/react-webpack-with-theme-example/src/context/mixnet.tsx b/sdk/typescript/examples/react-webpack-with-theme-example/src/context/mixnet.tsx
index 6704796b48..b234e8537d 100644
--- a/sdk/typescript/examples/react-webpack-with-theme-example/src/context/mixnet.tsx
+++ b/sdk/typescript/examples/react-webpack-with-theme-example/src/context/mixnet.tsx
@@ -1,14 +1,11 @@
import * as React from 'react';
-import { createNymMixnetClient, IWebWorkerEvents, NymClientConfig, NymMixnetClient } from '@nymproject/sdk';
+import { createNymMixnetClient, IWebWorkerEvents, NymClientConfig, NymMixnetClient, Headers } from '@nymproject/sdk';
-export interface BinaryMessageHeaders {
+export interface BinaryMessageHeaders extends Headers {
filename: string;
mimeType: string;
}
-export const parseBinaryMessageHeaders = (headers: string): BinaryMessageHeaders =>
- JSON.parse(headers) as BinaryMessageHeaders;
-
interface State {
// data
isReady: boolean;
@@ -17,7 +14,7 @@ interface State {
// methods
connect: (config: NymClientConfig) => Promise;
- sendTextMessage: (args: { payload: string; recipient: string }) => Promise;
+ sendTextMessage: (args: { payload: string; recipient: string; headers?: Headers }) => Promise;
sendBinaryMessage: (args: { payload: Uint8Array; recipient: string; headers: BinaryMessageHeaders }) => Promise;
}
@@ -62,7 +59,7 @@ export const MixnetContextProvider: React.FC = ({ children }) => {
await nym.current.client.start(config);
};
- const sendTextMessage = async (args: { payload: string; recipient: string }) => {
+ const sendTextMessage = async (args: { payload: string; recipient: string; headers?: Headers }) => {
if (!nym.current?.client) {
console.error('Nym client has not initialised. Please wrap in useEffect on `isReady` prop of this context.');
return;
@@ -76,8 +73,7 @@ export const MixnetContextProvider: React.FC = ({ children }) => {
return;
}
// convert headers to JSON
- const sendArgs = { ...args, headers: JSON.stringify(args.headers) };
- await nym.current.client.sendBinaryMessage(sendArgs);
+ await nym.current.client.sendBinaryMessage(args);
};
const value = React.useMemo(