diff --git a/.changeset/brown-pianos-sit.md b/.changeset/brown-pianos-sit.md
new file mode 100644
index 000000000..09df3b5c0
--- /dev/null
+++ b/.changeset/brown-pianos-sit.md
@@ -0,0 +1,5 @@
+---
+"fuels-wallet": patch
+---
+
+- New "Review Errors" option in Hamburger Menu when new errors are detected.
diff --git a/.changeset/dull-mugs-hunt.md b/.changeset/dull-mugs-hunt.md
new file mode 100644
index 000000000..0b43e61aa
--- /dev/null
+++ b/.changeset/dull-mugs-hunt.md
@@ -0,0 +1,5 @@
+---
+"fuels-wallet": patch
+---
+
+- Removed Error Floating Button
diff --git a/packages/app/playwright/e2e/ReportError.test.ts b/packages/app/playwright/e2e/ReportError.test.ts
index abd87f8f0..1a912524e 100644
--- a/packages/app/playwright/e2e/ReportError.test.ts
+++ b/packages/app/playwright/e2e/ReportError.test.ts
@@ -1,13 +1,7 @@
import type { Browser, BrowserContext, Page } from '@playwright/test';
import test, { chromium, expect } from '@playwright/test';
-import {
- getButtonByText,
- getByAriaLabel,
- hasText,
- reload,
- visit,
-} from '../commons';
+import { getByAriaLabel, hasText, reload, visit } from '../commons';
import { mockData } from '../mocks';
test.describe('ReportError', () => {
@@ -51,7 +45,7 @@ test.describe('ReportError', () => {
expect(errorsAfterReporting.length).toBe(0);
});
- test('should show floating error button when there is a error in the database', async () => {
+ test('should show Review Error in menu when there is a error in the database', async () => {
await visit(page, '/');
await page.evaluate(async () => {
await window.fuelDB.errors.clear();
@@ -73,10 +67,8 @@ test.describe('ReportError', () => {
});
await reload(page);
- const floatingButton = await page.waitForSelector(
- '[data-testid="ErrorFloatingButton"]',
- { state: 'visible' }
- );
+ await getByAriaLabel(page, 'Menu').click();
+ const floatingButton = page.locator(`[data-key="hasErrors"]`);
expect(floatingButton.isVisible).toBeTruthy();
});
@@ -101,11 +93,8 @@ test.describe('ReportError', () => {
});
});
await reload(page);
- (
- await page.waitForSelector('[data-testid="ErrorFloatingButton"]', {
- state: 'visible',
- })
- ).click();
+ await getByAriaLabel(page, 'Menu').click();
+ page.locator(`[data-key="hasErrors"]`).click();
await hasText(page, /Unexpected error/i);
// report error
@@ -136,11 +125,8 @@ test.describe('ReportError', () => {
});
});
await reload(page);
- (
- await page.waitForSelector('[data-testid="ErrorFloatingButton"]', {
- state: 'visible',
- })
- ).click();
+ await getByAriaLabel(page, 'Menu').click();
+ page.locator(`[data-key="hasErrors"]`).click();
await hasText(page, /Unexpected error/i);
// report error
@@ -174,11 +160,8 @@ test.describe('ReportError', () => {
});
});
await reload(page);
- (
- await page.waitForSelector('[data-testid="ErrorFloatingButton"]', {
- state: 'visible',
- })
- ).click();
+ await getByAriaLabel(page, 'Menu').click();
+ page.locator(`[data-key="hasErrors"]`).click();
await hasText(page, /Unexpected error/i);
// report error
@@ -195,11 +178,8 @@ test.describe('ReportError', () => {
console.error(new Error('Test Error'));
});
await reload(page);
- (
- await page.waitForSelector('[data-testid="ErrorFloatingButton"]', {
- state: 'visible',
- })
- ).click();
+ await getByAriaLabel(page, 'Menu').click();
+ page.locator(`[data-key="hasErrors"]`).click();
await hasText(page, /Unexpected error/i);
const errorsAfterReporting = await getPageErrors(page);
@@ -242,11 +222,8 @@ test.describe('ReportError', () => {
});
});
await reload(page);
- (
- await page.waitForSelector('[data-testid="ErrorFloatingButton"]', {
- state: 'visible',
- })
- ).click();
+ await getByAriaLabel(page, 'Menu').click();
+ page.locator(`[data-key="hasErrors"]`).click();
await hasText(page, /Unexpected error/i);
await reload(page);
const errorsAfterReporting = await getPageErrors(page);
diff --git a/packages/app/src/systems/Core/components/Layout/TopBar.tsx b/packages/app/src/systems/Core/components/Layout/TopBar.tsx
index fb2fc6689..523e950f6 100644
--- a/packages/app/src/systems/Core/components/Layout/TopBar.tsx
+++ b/packages/app/src/systems/Core/components/Layout/TopBar.tsx
@@ -10,6 +10,7 @@ import { NetworkDropdown } from '~/systems/Network/components';
import { useNetworks } from '~/systems/Network/hooks';
import { useOverlay } from '~/systems/Overlay';
+import { useReportError } from '~/systems/Error';
import { useLayoutContext } from './Layout';
export enum TopBarType {
@@ -32,6 +33,7 @@ function InternalTopBar({ onBack }: TopBarProps) {
const overlay = useOverlay();
const { isLoading, title, isHome } = useLayoutContext();
const { selectedNetwork, handlers } = useNetworks();
+ const { hasErrorsToReport } = useReportError();
return (
@@ -61,7 +63,8 @@ function InternalTopBar({ onBack }: TopBarProps) {
>
)}
-
+
+ {hasErrorsToReport && ●}
}
@@ -127,6 +130,17 @@ const styles = {
minHeight: '50px',
transition: 'none',
}),
+ menuContainer: cssObj({
+ position: 'relative',
+ }),
+ badge: cssObj({
+ position: 'absolute',
+ top: -6,
+ right: -2,
+ fontSize: 8,
+ color: '$intentsError10 !important',
+ transform: 'translate(25%, -25%)',
+ }),
topbarIcon: cssObj({
px: '$0 !important',
color: '$intentsBase8 !important',
diff --git a/packages/app/src/systems/Error/components/ErrorFloatingButton/ErrorFloatingButton.tsx b/packages/app/src/systems/Error/components/ErrorFloatingButton/ErrorFloatingButton.tsx
deleted file mode 100644
index 1e54d3275..000000000
--- a/packages/app/src/systems/Error/components/ErrorFloatingButton/ErrorFloatingButton.tsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { cssObj } from '@fuel-ui/css';
-import { Box, Icon, IconButton } from '@fuel-ui/react';
-import { useLocation, useNavigate } from 'react-router-dom';
-import { Pages } from '~/systems/Core';
-import { useReportError } from '~/systems/Error';
-
-export function ErrorFloatingButton() {
- const { errors, hasErrorsToReport } = useReportError();
- const navigate = useNavigate();
- const location = useLocation();
- const hidden =
- !hasErrorsToReport ||
- !errors.length ||
- location.pathname === Pages.errors();
-
- return (
-
- navigate(Pages.errors())}
- aria-label="Click to visualize unreviewed errrors"
- iconSize={20}
- size="sm"
- disabled={hidden}
- data-testid="ErrorFloatingButton"
- icon={Icon.is('AlertTriangle')}
- />
-
- );
-}
-
-const styles = {
- alertContainer: cssObj({
- padding: '$2',
- position: 'fixed',
- bottom: 0,
- right: 25,
- zIndex: '$10',
- borderRadius: '$full',
- transform: 'translateY(50px)',
- transition: 'transform 0.3s ease-in-out',
- '&.show': {
- transform: 'translateY(-20px)',
- },
- }),
- button: cssObj({
- borderRadius: '$full',
- borderWidth: 0,
- }),
-};
diff --git a/packages/app/src/systems/Error/components/ErrorFloatingButton/index.ts b/packages/app/src/systems/Error/components/ErrorFloatingButton/index.ts
deleted file mode 100644
index 753133da4..000000000
--- a/packages/app/src/systems/Error/components/ErrorFloatingButton/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './ErrorFloatingButton';
diff --git a/packages/app/src/systems/Error/components/index.tsx b/packages/app/src/systems/Error/components/index.tsx
index 9ddc76ac6..3d37579d3 100644
--- a/packages/app/src/systems/Error/components/index.tsx
+++ b/packages/app/src/systems/Error/components/index.tsx
@@ -1,3 +1,2 @@
export * from './Providers';
export * from './ThrowError';
-export * from './ErrorFloatingButton';
diff --git a/packages/app/src/systems/Error/hooks/useReportError.tsx b/packages/app/src/systems/Error/hooks/useReportError.tsx
index bdbaeba31..b1ad8835b 100644
--- a/packages/app/src/systems/Error/hooks/useReportError.tsx
+++ b/packages/app/src/systems/Error/hooks/useReportError.tsx
@@ -41,7 +41,7 @@ export function useReportError() {
};
return {
- hasErrorsToReport,
+ hasErrorsToReport: !!hasErrorsToReport || !!errors.length,
isLoadingSendOnce,
errors,
handlers: {
diff --git a/packages/app/src/systems/Sidebar/components/Menu/Menu.stories.tsx b/packages/app/src/systems/Sidebar/components/Menu/Menu.stories.tsx
index 5fd74f639..5fcb7d1c8 100644
--- a/packages/app/src/systems/Sidebar/components/Menu/Menu.stories.tsx
+++ b/packages/app/src/systems/Sidebar/components/Menu/Menu.stories.tsx
@@ -1,6 +1,6 @@
import { Box } from '@fuel-ui/react';
-import type { MenuItemObj, MenuProps } from './Menu';
+import type { MenuItemObj, MenuProps } from './types';
import { Menu } from './Menu';
export default {
diff --git a/packages/app/src/systems/Sidebar/components/Menu/Menu.tsx b/packages/app/src/systems/Sidebar/components/Menu/Menu.tsx
index cb976c8a4..4dfb75983 100644
--- a/packages/app/src/systems/Sidebar/components/Menu/Menu.tsx
+++ b/packages/app/src/systems/Sidebar/components/Menu/Menu.tsx
@@ -1,27 +1,12 @@
import { cssObj } from '@fuel-ui/css';
-import type { Icons } from '@fuel-ui/react';
-import { Box, Icon, Menu as RootMenu } from '@fuel-ui/react';
+import { Badge, Box, Icon, Menu as RootMenu, Text } from '@fuel-ui/react';
import { motion } from 'framer-motion';
import type { Key } from 'react';
-import { useState } from 'react';
+import { memo, useState } from 'react';
import { useMatch, useNavigate, useResolvedPath } from 'react-router-dom';
import { store } from '~/store';
import { coreStyles } from '~/systems/Core/styles';
-
-export type MenuItemObj = {
- key: string;
- icon: Icons;
- label: string;
- path?: string;
- ahref?: string;
- submenu?: MenuItemObj[];
- onPress?: () => void;
-};
-
-type MenuItemContentProps = {
- item: MenuItemObj;
- isOpened?: boolean;
-};
+import type { MenuItemContentProps, MenuItemObj, MenuProps } from './types';
function commonActions(
item: MenuItemObj,
@@ -63,6 +48,7 @@ function MenuItemContent({ item, isOpened }: MenuItemContentProps) {
return (
+ {item.badge && ●}
(null);
const navigate = useNavigate();
@@ -134,6 +116,8 @@ export function Menu({ items }: MenuProps) {
);
}
+export const Menu = memo(_Menu);
+
const styles = {
root: cssObj({
...coreStyles.scrollable('$intentsBase2'),
@@ -186,6 +170,7 @@ const styles = {
}),
routeContent: cssObj({
flex: 1,
+ position: 'relative',
}),
submenu: cssObj({
position: 'relative',
@@ -229,4 +214,12 @@ const styles = {
transform: 'translateY(-50%)',
},
}),
+ badge: cssObj({
+ position: 'absolute',
+ top: 2,
+ left: 2,
+ fontSize: 8,
+ color: '$intentsError10 !important',
+ transform: 'translate(25%, -25%)',
+ }),
};
diff --git a/packages/app/src/systems/Sidebar/components/Menu/types.ts b/packages/app/src/systems/Sidebar/components/Menu/types.ts
new file mode 100644
index 000000000..4c5dc386e
--- /dev/null
+++ b/packages/app/src/systems/Sidebar/components/Menu/types.ts
@@ -0,0 +1,21 @@
+import type { Icons } from '@fuel-ui/react';
+
+export type MenuItemObj = {
+ key: string;
+ icon: Icons;
+ label: string;
+ path?: string;
+ ahref?: string;
+ submenu?: MenuItemObj[];
+ onPress?: () => void;
+ badge?: boolean;
+};
+
+export type MenuItemContentProps = {
+ item: MenuItemObj;
+ isOpened?: boolean;
+};
+
+export type MenuProps = {
+ items: MenuItemObj[];
+};
diff --git a/packages/app/src/systems/Sidebar/components/Sidebar/Sidebar.tsx b/packages/app/src/systems/Sidebar/components/Sidebar/Sidebar.tsx
index 894b4dd30..e5c7449e0 100644
--- a/packages/app/src/systems/Sidebar/components/Sidebar/Sidebar.tsx
+++ b/packages/app/src/systems/Sidebar/components/Sidebar/Sidebar.tsx
@@ -1,15 +1,22 @@
import { cssObj } from '@fuel-ui/css';
import { Box, Drawer, Icon, IconButton, Text } from '@fuel-ui/react';
-import { forwardRef } from 'react';
+import { forwardRef, useMemo } from 'react';
import { APP_VERSION } from '~/config';
import { useOverlay } from '~/systems/Overlay';
+import { useReportError } from '~/systems/Error';
import { Menu } from '..';
import { sidebarItems } from '../../constants';
import { ThemeToggler } from '../ThemeToggler';
function SidebarContent() {
const overlay = useOverlay();
+ const { hasErrorsToReport } = useReportError();
+
+ const menuItems = useMemo(
+ () => sidebarItems(hasErrorsToReport),
+ [hasErrorsToReport]
+ );
return (
<>
@@ -25,7 +32,7 @@ function SidebarContent() {
onPress={overlay.close}
/>
-
+
Version: {APP_VERSION}{' '}
diff --git a/packages/app/src/systems/Sidebar/constants/sidebarItems.ts b/packages/app/src/systems/Sidebar/constants/sidebarItems.ts
index ef181a86f..2c4b1d392 100644
--- a/packages/app/src/systems/Sidebar/constants/sidebarItems.ts
+++ b/packages/app/src/systems/Sidebar/constants/sidebarItems.ts
@@ -1,116 +1,128 @@
import { store } from '~/store';
import { Pages } from '~/systems/Core';
-import type { MenuItemObj } from '../components';
+import type { MenuItemObj } from '../components/Menu/types';
-export const sidebarItems = (): Array => [
- {
- key: 'wallet',
- icon: 'Wallet',
- label: 'Wallet',
- path: Pages.wallet(),
- },
- {
- key: 'history',
- icon: 'History',
- label: 'Transaction History',
- path: Pages.txs(),
- },
- {
- key: 'networks',
- icon: 'BrandStackshare',
- label: 'Networks Management',
- onPress() {
- store.openNetworksList();
- },
- },
- {
- key: 'accounts',
- icon: 'Users',
- label: 'Account Management',
- onPress() {
- store.openAccountList();
- },
- },
- {
- key: 'connected-apps',
- icon: 'PlugConnected',
- label: 'Connected Apps',
- path: Pages.settingsConnectedApps(),
- },
- {
- key: 'settings',
- icon: 'Settings',
- label: 'Settings',
- submenu: [
+export const sidebarItems = (hasErrors: boolean): Array =>
+ (
+ [
{
- key: 'assets',
- icon: 'Coins',
- label: 'Assets',
- path: Pages.assets(),
+ key: 'wallet',
+ icon: 'Wallet',
+ label: 'Wallet',
+ path: Pages.wallet(),
},
{
- key: 'view-seed-phrase',
- icon: 'Lock',
- label: 'View Seed Phrase',
- onPress() {
- store.openViewSeedPhrase();
- },
+ key: 'history',
+ icon: 'History',
+ label: 'Transaction History',
+ path: Pages.txs(),
},
{
- key: 'change-password',
- icon: 'Lock',
- label: 'Change Password',
- path: Pages.settingsChangePassword(),
+ key: 'networks',
+ icon: 'BrandStackshare',
+ label: 'Networks Management',
+ onPress() {
+ store.openNetworksList();
+ },
},
{
- key: 'logout',
- icon: 'Logout',
- label: 'Logout',
+ key: 'accounts',
+ icon: 'Users',
+ label: 'Account Management',
onPress() {
- store.openAccountsLogout();
+ store.openAccountList();
},
},
- ],
- },
- {
- key: 'support',
- icon: 'HelpCircle',
- label: 'Support',
- submenu: [
{
- key: 'discord',
- icon: 'BrandDiscordFilled',
- label: 'Fuel Discord',
- ahref: 'https://discord.com/invite/xfpK4Pe',
+ key: 'connected-apps',
+ icon: 'PlugConnected',
+ label: 'Connected Apps',
+ path: Pages.settingsConnectedApps(),
},
+ hasErrors
+ ? {
+ key: 'hasErrors',
+ icon: 'AlertTriangle',
+ label: 'Review Errors',
+ path: Pages.errors(),
+ badge: true,
+ }
+ : undefined,
{
- key: 'forum',
- icon: 'MessageCircle',
- label: 'Forum',
- ahref: 'https://forum.fuel.network/c/fuel-wallet/15',
+ key: 'settings',
+ icon: 'Settings',
+ label: 'Settings',
+ submenu: [
+ {
+ key: 'assets',
+ icon: 'Coins',
+ label: 'Assets',
+ path: Pages.assets(),
+ },
+ {
+ key: 'view-seed-phrase',
+ icon: 'Lock',
+ label: 'View Seed Phrase',
+ onPress() {
+ store.openViewSeedPhrase();
+ },
+ },
+ {
+ key: 'change-password',
+ icon: 'Lock',
+ label: 'Change Password',
+ path: Pages.settingsChangePassword(),
+ },
+ {
+ key: 'logout',
+ icon: 'Logout',
+ label: 'Logout',
+ onPress() {
+ store.openAccountsLogout();
+ },
+ },
+ ],
},
{
- key: 'github',
- icon: 'BrandGithubFilled',
- label: 'Github',
- ahref: 'https://github.com/FuelLabs/fuels-wallet',
+ key: 'support',
+ icon: 'HelpCircle',
+ label: 'Support',
+ submenu: [
+ {
+ key: 'discord',
+ icon: 'BrandDiscordFilled',
+ label: 'Fuel Discord',
+ ahref: 'https://discord.com/invite/xfpK4Pe',
+ },
+ {
+ key: 'forum',
+ icon: 'MessageCircle',
+ label: 'Forum',
+ ahref: 'https://forum.fuel.network/c/fuel-wallet/15',
+ },
+ {
+ key: 'github',
+ icon: 'BrandGithubFilled',
+ label: 'Github',
+ ahref: 'https://github.com/FuelLabs/fuels-wallet',
+ },
+ {
+ key: 'bugs',
+ icon: 'Bug',
+ label: 'Report a Bug',
+ /** This page isn't created yet */
+ ahref: 'https://github.com/FuelLabs/fuels-wallet/issues/new/choose',
+ },
+ ],
},
{
- key: 'bugs',
- icon: 'Bug',
- label: 'Report a Bug',
- /** This page isn't created yet */
- ahref: 'https://github.com/FuelLabs/fuels-wallet/issues/new/choose',
+ key: 'lock-wallet',
+ icon: 'Lock',
+ label: 'Lock Wallet',
+ onPress() {
+ store.lock();
+ },
},
- ],
- },
- {
- key: 'lock-wallet',
- icon: 'Lock',
- label: 'Lock Wallet',
- onPress() {
- store.lock();
- },
- },
-];
+ ] as Array
+ ).filter((item) => item !== undefined) as Array;
diff --git a/packages/app/src/systems/Unlock/guards/UnlockGuard.tsx b/packages/app/src/systems/Unlock/guards/UnlockGuard.tsx
index 25bb9c31c..de6a20b9d 100644
--- a/packages/app/src/systems/Unlock/guards/UnlockGuard.tsx
+++ b/packages/app/src/systems/Unlock/guards/UnlockGuard.tsx
@@ -1,6 +1,5 @@
import { Outlet } from 'react-router-dom';
-import { ErrorFloatingButton } from '~/systems/Error/components/ErrorFloatingButton';
import { useUnlock } from '../hooks';
import { UnlockPage } from '../pages';
@@ -8,12 +7,7 @@ export function UnlockGuard() {
const { isUnlocked } = useUnlock();
if (isUnlocked) {
- return (
- <>
-
-
- >
- );
+ return ;
}
return ;