Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make @umami/state package platform agnostic #2329

Merged
merged 5 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const RestoreLedger = ({
toast({ description: "Account successfully created!", status: "success" });
closeModal();
}, LEDGER_TIMEOUT),
error => {
(error: { name: string; message: any }) => {
if (error.name === "PublicKeyRetrievalError") {
return {
description: "Request rejected. Please unlock your Ledger and open the Tezos app",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const useSignWithBeacon = (

return openWith(<SuccessStep hash={opHash} />);
},
error => ({
(error: { message: any }) => ({
description: `Failed to confirm Beacon operation: ${error.message}`,
})
);
Expand Down
21 changes: 14 additions & 7 deletions apps/desktop/src/providers/UmamiTheme.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { ChakraProvider, ColorModeScript } from "@chakra-ui/react";
import { ChakraProvider, ColorModeScript, useToast } from "@chakra-ui/react";
import { ToastProvider } from "@umami/utils";

import theme from "../style/theme";

export const UmamiTheme = (props: any) => (
<ChakraProvider theme={theme}>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
{props.children}
</ChakraProvider>
);
export const UmamiTheme = (props: any) => {
const toast = useToast();

return (
<ToastProvider toast={toast}>
<ChakraProvider theme={theme}>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
{props.children}
</ChakraProvider>
</ToastProvider>
);
};
2 changes: 1 addition & 1 deletion apps/desktop/src/utils/beacon/useHandleBeaconMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const useHandleBeaconMessage = () => {

return openWith(modal, { onClose });
},
error => ({
(error: { message: any }) => ({
description: `Error while processing Beacon request: ${error.message}`,
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const useSignWithBeacon = ({

return openWith(<SuccessStep hash={opHash} />);
},
error => ({
(error: { message: any }) => ({
description: `Failed to confirm Beacon operation: ${error.message}`,
})
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const useSignWithWalletConnect = ({
await walletKit.respondSessionRequest({ topic: requestId.topic, response });
return openWith(<SuccessStep hash={opHash} />);
},
error => ({
(error: { message: any }) => ({
description: `Failed to confirm WalletConnect operation: ${error.message}`,
})
);
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/beacon/useHandleBeaconMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export const useHandleBeaconMessage = () => {

return openWith(modal, { onClose });
},
error => ({
(error: { message: any }) => ({
description: `Error while processing Beacon request: ${error.message}`,
})
);
Expand Down
30 changes: 15 additions & 15 deletions apps/web/src/providers/UmamiTheme.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ChakraProvider, ColorModeScript } from "@chakra-ui/react";
import { ChakraProvider, ColorModeScript, useToast } from "@chakra-ui/react";
import { Global, css } from "@emotion/react";
import { ToastProvider } from "@umami/utils";
import { type PropsWithChildren } from "react";
import "focus-visible/dist/focus-visible";

Expand All @@ -17,17 +18,16 @@ const GlobalStyles = css`
}
`;

export const UmamiTheme = ({ children }: PropsWithChildren) => (
<ChakraProvider
theme={theme}
toastOptions={{
defaultOptions: {
render: CustomToast,
},
}}
>
<Global styles={GlobalStyles} />
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
{children}
</ChakraProvider>
);
export const UmamiTheme = ({ children }: PropsWithChildren) => {
const toast = useToast({ render: CustomToast });

return (
<ChakraProvider theme={theme}>
<ToastProvider toast={toast}>
<Global styles={GlobalStyles} />
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
{children}
</ToastProvider>
</ChakraProvider>
);
};
5 changes: 5 additions & 0 deletions apps/web/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ jest.doMock("@chakra-ui/react", () => ({
useColorMode: () => ({ colorMode: "light", toggleColorMode: jest.fn() }),
}));

jest.mock("@umami/utils", () => ({
...jest.requireActual("@umami/utils"),
useCustomToast: () => mockToast,
}));

jest.mock("./utils/persistor", () => ({
pause: jest.fn(),
}));
Expand Down
1 change: 1 addition & 0 deletions packages/data-polling/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@umami/state": "workspace:^",
"@umami/tezos": "workspace:^",
"@umami/tzkt": "workspace:^",
"@umami/test-utils": "workspace:^",
"date-fns": "^4.1.0",
"framer-motion": "^11.15.0",
"lodash": "^4.17.21",
Expand Down
2 changes: 2 additions & 0 deletions packages/data-polling/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { webcrypto } from "crypto";
import { TextDecoder, TextEncoder } from "util";

import { mockToast } from "@umami/state";
import { mockLocalStorage } from "@umami/test-utils";

beforeEach(() => {
Object.defineProperties(global, {
crypto: { value: webcrypto, writable: true },
TextDecoder: { value: TextDecoder, writable: true },
TextEncoder: { value: TextEncoder, writable: true },
localStorage: { value: mockLocalStorage(), writable: true },
});
});

Expand Down
7 changes: 3 additions & 4 deletions packages/data-polling/src/testUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import * as testLib from "@testing-library/react";
import { type UmamiStore, makeStore } from "@umami/state";
import { type PropsWithChildren, act } from "react";
import { Provider } from "react-redux";
import { makeStore, UmamiStore } from "@umami/state";
import { PropsWithChildren, act } from "react";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const customRenderHook = <
Result,
Expand Down
3 changes: 0 additions & 3 deletions packages/state/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@
},
"dependencies": {
"@airgap/beacon-wallet": "^4.3.1",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@reduxjs/toolkit": "^2.5.0",
"@reown/walletkit": "^1.0.1",
"@tanstack/react-query": "^5.62.11",
Expand Down
23 changes: 16 additions & 7 deletions packages/state/src/hooks/beacon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import {
type NetworkType,
Serializer,
} from "@airgap/beacon-wallet";
import { useToast } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { type RawPkh } from "@umami/tezos";
import { useCustomToast } from "@umami/utils";
import { uniq } from "lodash";
import { useDispatch } from "react-redux";

Expand Down Expand Up @@ -102,7 +102,7 @@ export const useRemoveBeaconPeersByAccounts = () => {

export const useAddPeer = () => {
const { refresh } = useBeaconPeers();
const toast = useToast();
const toast = useCustomToast();

return (payload: string) =>
new Serializer()
Expand All @@ -111,11 +111,20 @@ export const useAddPeer = () => {
.then(peer => WalletClient.addPeer(peer as ExtendedPeerInfo))
.then(() => refresh())
.catch(e => {
toast({
description:
"Beacon sync code in the clipboard is invalid. Please copy a beacon sync code from the dApp",
status: "error",
});
const description =
"Beacon sync code in the clipboard is invalid. Please copy a beacon sync code from the dApp";
const type = "error";

toast.show
? toast.show({
type,
text1: description,
})
: toast({
description,
status: type,
});

console.error(e);
});
};
4 changes: 2 additions & 2 deletions packages/state/src/hooks/useAsyncActionHandler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ describe("useAsyncActionHandler", () => {
view.result.current.handleAsyncActionUnsafe(() => Promise.reject(new Error("test error")))
)
).rejects.toThrow("test error");
expect(mockToast).toHaveBeenCalledTimes(2);
expect(mockToast).toHaveBeenCalledTimes(1);
});

it("Unsafe propagates the error and shows the toast once on first handling", async () => {
Expand All @@ -181,7 +181,7 @@ describe("useAsyncActionHandler", () => {
status: "error",
isClosable: true,
});
expect(mockToast).toHaveBeenCalledTimes(2);
expect(mockToast).toHaveBeenCalledTimes(1);
});

it("Unsafe propagates the error and shows no toast on second handling", async () => {
Expand Down
18 changes: 4 additions & 14 deletions packages/state/src/hooks/useAsyncActionHandler.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { type UseToastOptions, useToast } from "@chakra-ui/react";
import { getErrorContext } from "@umami/utils";
import { type ToastOptions, getErrorContext, useCustomToast } from "@umami/utils";
import { useCallback, useRef, useState } from "react";

import { useAppDispatch } from "./useAppDispatch";
import { errorsActions } from "../slices";
import { mockToast } from "../testUtils";

/**
* Hook for gracefully handling async actions.
Expand All @@ -22,13 +20,13 @@ import { mockToast } from "../testUtils";
export const useAsyncActionHandler = () => {
const [isLoading, setIsLoading] = useState(false);
const isLoadingRef = useRef(isLoading);
const toast = useToast();
const toast = useCustomToast();
const dispatch = useAppDispatch();

const handleAsyncActionUnsafe = useCallback(
async <T>(
fn: () => Promise<T>,
toastOptions?: UseToastOptions | ((error: any) => UseToastOptions)
toastOptions?: ToastOptions | ((error: any) => ToastOptions)
): Promise<T | void> => {
if (isLoadingRef.current) {
return;
Expand All @@ -53,14 +51,6 @@ export const useAsyncActionHandler = () => {
...(typeof toastOptions === "function" ? toastOptions(error) : toastOptions),
});

// TODO: fix this dirty hack
mockToast({
description: errorContext.description,
status: "error",
isClosable: true,
...(typeof toastOptions === "function" ? toastOptions(error) : toastOptions),
});

dispatch(errorsActions.add(errorContext));
}
throw error;
Expand All @@ -76,7 +66,7 @@ export const useAsyncActionHandler = () => {
const handleAsyncAction = useCallback(
async <T>(
fn: () => Promise<T>,
toastOptions?: UseToastOptions | ((error: any) => UseToastOptions)
toastOptions?: ToastOptions | ((error: any) => ToastOptions)
): Promise<T | void> => handleAsyncActionUnsafe(fn, toastOptions).catch(() => {}),
[handleAsyncActionUnsafe]
);
Expand Down
6 changes: 3 additions & 3 deletions packages/state/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ Object.defineProperties(global, {
fetch: { value: jest.fn(), writable: true },
});

jest.mock("@chakra-ui/react", () => ({
...jest.requireActual("@chakra-ui/react"),
useToast: () => mockToast,
jest.mock("@umami/utils", () => ({
...jest.requireActual("@umami/utils"),
useCustomToast: () => mockToast,
}));

jest.mock("./beacon/WalletClient", () => ({
Expand Down
13 changes: 6 additions & 7 deletions packages/state/src/testUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import * as testLib from "@testing-library/react";
import { type Account } from "@umami/core";
import { type Multisig } from "@umami/multisig";
import { type RawPkh } from "@umami/tezos";
import { type PropsWithChildren, act } from "react";
import { Provider } from "react-redux";

import { accountsActions, multisigsActions } from "./slices";
import { makeStore, UmamiStore } from "./store";
import * as testLib from "@testing-library/react";
import { type UmamiStore, makeStore } from "./store";

export const addTestAccount = (store: UmamiStore, account: Account | Multisig) => {
if (!("type" in account) || account.type === "multisig") {
Expand All @@ -18,11 +22,6 @@ export const addTestAccounts = (store: UmamiStore, accounts: (Account | Multisig
accounts.forEach(account => addTestAccount(store, account));
};

import { PropsWithChildren, act } from "react";
import { Provider } from "react-redux";
import { RawPkh } from "@umami/tezos";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const customRenderHook = <
Result,
Props,
Expand Down
1 change: 1 addition & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./ErrorContext";
export * from "./providers";
27 changes: 27 additions & 0 deletions packages/utils/src/providers/ToastProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { type PropsWithChildren, createContext, useContext } from "react";

// TODO: fix this type when we have a better toast implementation
export type ToastOptions = any;

export type Toast = {
show?: (options: ToastOptions) => void;
[key: string]: any;
} & ((options: ToastOptions) => void);

type ToastContextType = {
toast: Toast;
};

const ToastContext = createContext<ToastContextType | undefined>(undefined);

export const useCustomToast = () => {
const toastContext = useContext(ToastContext);
if (!toastContext) {
throw new Error("useCustomToast must be used within a ToastProvider");
}
return toastContext.toast;
};

export const ToastProvider = ({ children, toast }: PropsWithChildren<{ toast: Toast }>) => (
<ToastContext.Provider value={{ toast }}>{children}</ToastContext.Provider>
);
1 change: 1 addition & 0 deletions packages/utils/src/providers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ToastProvider";
Loading
Loading