Skip to content
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
1 change: 1 addition & 0 deletions apps/user_dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"react": "^19.2.1",
"react-dom": "^19.2.1",
"react-hook-form": "^7.65.0",
"react-toastify": "^11.0.5",
"utility-types": "^3.11.0",
"zustand": "^5.0.6"
},
Expand Down
9 changes: 9 additions & 0 deletions apps/user_dashboard/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { Bounce, ToastContainer } from "react-toastify";
import { ToastCloseButton } from "@oko-wallet-common-ui/toast/toast";

import styles from "./page.module.scss";
import { Authorized } from "@oko-wallet-user-dashboard/components/authorized/authorized";
import { DashboardBody } from "@oko-wallet-user-dashboard/components/dashboard_body/dashboard_body";
Expand All @@ -23,6 +26,12 @@ export default function Home() {
</DashboardBody>
</div>
</div>
<ToastContainer
stacked
transition={Bounce}
toastClassName="custom-toast"
closeButton={ToastCloseButton}
/>
</Authorized>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Typography } from "@oko-wallet/oko-common-ui/typography";
import { CopyOutlinedIcon } from "@oko-wallet/oko-common-ui/icons/copy_outlined";
import { CheckCircleOutlinedIcon } from "@oko-wallet/oko-common-ui/icons/check_circle_outlined";

import { displayToast } from "@oko-wallet-user-dashboard/components/toast";
import styles from "./address_chip.module.scss";

interface AddressChipProps {
Expand Down Expand Up @@ -51,6 +52,11 @@ export const AddressChip: FC<AddressChipProps> = ({
try {
await navigator.clipboard.writeText(address);
setIsCopied(true);

displayToast({
variant: "success",
title: "Copied",
});
setTimeout(() => setIsCopied(false), 1500);
} catch (error) {
console.error("Failed to copy address:", error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import styles from "./token_item.module.scss";
import type { ViewToken } from "@oko-wallet-user-dashboard/store_legacy/huge-queries";
import { useRootStore } from "@oko-wallet-user-dashboard/state/store";
import { AddressQrModal } from "@oko-wallet-user-dashboard/components/address_qr_modal/address_qr_modal";
import { displayToast } from "@oko-wallet-user-dashboard/components/toast";

interface TokenItemProps {
viewToken: ViewToken;
Expand Down Expand Up @@ -47,6 +48,11 @@ export const TokenItem: FunctionComponent<TokenItemProps> = observer(
try {
await navigator.clipboard.writeText(address);
setIsCopied(true);

displayToast({
variant: "success",
title: "Copied",
});
setTimeout(() => setIsCopied(false), 1500);
} catch (error) {
console.error("Failed to copy address:", error);
Expand Down
5 changes: 3 additions & 2 deletions apps/user_dashboard/src/components/my_assets/my_assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { type FC } from "react";
import { Typography } from "@oko-wallet/oko-common-ui/typography";
import { SettingIcon } from "@oko-wallet/oko-common-ui/icons/setting_icon";
import { Button } from "@oko-wallet/oko-common-ui/button";

import styles from "./my_assets.module.scss";
import { ShowHideChainsModal } from "@oko-wallet-user-dashboard/components/show_hide_chains_modal/show_hide_chains_modal";
Expand All @@ -18,12 +19,12 @@ export const MyAssets: FC = () => {

<ShowHideChainsModal
renderTrigger={({ onOpen }) => (
<div className={styles.showHideChains} onClick={onOpen}>
<Button variant="ghost" onClick={onOpen}>
<SettingIcon size={16} color="var(--fg-quaternary)" />
<Typography size="sm" weight="semibold" color="tertiary">
Show/Hide Chains
</Typography>
</div>
</Button>
)}
/>
</div>
Expand Down
33 changes: 33 additions & 0 deletions apps/user_dashboard/src/components/toast/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Toast, type ToastVariant } from "@oko-wallet/oko-common-ui/toast";
import { type ToastOptions, toast } from "react-toastify";

export function displayToast({
variant,
title,
description,
toastOptions,
}: {
variant: ToastVariant;
title: string;
description?: string;
toastOptions?: Partial<ToastOptions>;
}) {
toastOptions = {
...{
position: "top-right",
autoClose: 5000,
hideProgressBar: true,
closeOnClick: false,
pauseOnHover: true,
draggable: false,
progress: undefined,
pauseOnFocusLoss: false,
},
...(toastOptions ?? {}),
};

toast(
<Toast title={title} description={description} variant={variant} />,
toastOptions,
);
}
1 change: 1 addition & 0 deletions ui/oko_common_ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"./styles/*": "./src/styles/*",
"./table": "./src/table/table.tsx",
"./theme": "./src/theme/theme_provider.tsx",
"./toast": "./src/toast/toast.tsx",
"./toggle": "./src/toggle/toggle.tsx",
"./tooltip": "./src/tooltip/tooltip.tsx",
"./typography": "./src/typography/typography.tsx",
Expand Down
4 changes: 4 additions & 0 deletions ui/oko_common_ui/src/button/button.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
pointer-events: none;
}

&:hover:not(.disabled) {
background: var(--bg-primary-hover);
}

&.disabled {
color: var(--fg-disabled);
border: none;
Expand Down
58 changes: 58 additions & 0 deletions ui/oko_common_ui/src/toast/toast.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
:global(.custom-toast) {
display: flex;
border-radius: 12px;
border: 1px solid var(--border-primary);
background-color: var(--white);
box-shadow: var(--shadow-lg);
min-height: fit-content;
padding: 16px;
}

.successIcon {
color: var(--fg-success-primary);
position: relative;
width: 20px;
height: 20px;

.successIconOuterRing1 {
position: absolute;
top: -4px;
left: -4px;
width: calc(100% + 8px);
height: calc(100% + 8px);
border-radius: 9999px;
border: 2px solid;
border-color: rgba(#079455, 0.3);
}

.successIconOuterRing2 {
position: absolute;
top: -9px;
left: -9px;
width: calc(100% + 18px);
height: calc(100% + 18px);
border-radius: 9999px;
border: 2px solid;
border-color: rgba(#079455, 0.1);
}
}

.toastInner {
display: flex;
align-items: center;
justify-content: flex-start;

gap: 16px;
width: 100%;
}

.closeButton {
padding: 0;
margin: 0;
border: none;
background: none;
cursor: pointer;

width: 20px;
height: 20px;
}
73 changes: 73 additions & 0 deletions ui/oko_common_ui/src/toast/toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"use client";

import { type FC, type MouseEvent } from "react";

import { CheckCircleOutlinedIcon } from "@oko-wallet-common-ui/icons/check_circle_outlined";
import { ErrorIcon } from "@oko-wallet-common-ui/icons/error_icon";
import { WarningIcon } from "@oko-wallet-common-ui/icons/warning_icon";
import { InfoCircleIcon } from "@oko-wallet-common-ui/icons/info_circle";
import { Typography } from "@oko-wallet-common-ui/typography/typography";
import { XCloseIcon } from "@oko-wallet-common-ui/icons/x_close";
import styles from "./toast.module.scss";

export type ToastVariant = "success" | "error" | "warning" | "info";
export type ToastItemProps = {
title?: string;
description?: string;
variant: ToastVariant;
};

const SuccessToastIcon: FC = () => {
return (
<div className={styles.successIcon}>
<div className={styles.successIconOuterRing1} />
<div className={styles.successIconOuterRing2} />
<CheckCircleOutlinedIcon size={20} />
</div>
);
};

interface ToastIconProps {
variant: ToastVariant;
}
const ToastIcon: FC<ToastIconProps> = ({ variant }) => {
switch (variant) {
case "success":
return <SuccessToastIcon />;
case "error":
return <ErrorIcon size={20} />;
case "warning":
return <WarningIcon size={20} />;
default:
return <InfoCircleIcon size={20} />;
}
};

export const Toast: FC<ToastItemProps> = ({ title, description, variant }) => {
return (
<div
className={styles.toastInner}
role={variant === "error" || variant === "warning" ? "alert" : "status"}
>
<ToastIcon variant={variant} />
{title && (
<Typography size="sm" weight="semibold" color="primary">
{title}
</Typography>
)}
{/* TODO: add description, now there is no description in the design*/}
{/* {description && <div className={styles.description}>{description}</div>} */}
</div>
);
};

interface ToastCloseButtonProps {
closeToast: (e: MouseEvent<HTMLElement>) => void;
}
export const ToastCloseButton: FC<ToastCloseButtonProps> = ({ closeToast }) => {
return (
<button className={styles.closeButton} onClick={closeToast}>
<XCloseIcon color="var(--fg-quaternary)" size={20} />
</button>
);
};
13 changes: 13 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11331,6 +11331,7 @@ __metadata:
react: "npm:^19.2.1"
react-dom: "npm:^19.2.1"
react-hook-form: "npm:^7.65.0"
react-toastify: "npm:^11.0.5"
sass: "npm:^1.94.2"
tsx: "npm:^4.20.3"
typescript: "npm:^5.8.3"
Expand Down Expand Up @@ -36408,6 +36409,18 @@ __metadata:
languageName: node
linkType: hard

"react-toastify@npm:^11.0.5":
version: 11.0.5
resolution: "react-toastify@npm:11.0.5"
dependencies:
clsx: "npm:^2.1.1"
peerDependencies:
react: ^18 || ^19
react-dom: ^18 || ^19
checksum: 10c0/50f5b81323ebb1957b2efd0963fac24aa1407155d16ab756ffd6d0f42f8af17e796b3958a9fce13e9d1b945d6c3a5a9ebf13529478474d8a2af4bf1dd0db67d2
languageName: node
linkType: hard

"react-tooltip@npm:^5.28.0":
version: 5.30.0
resolution: "react-tooltip@npm:5.30.0"
Expand Down