Skip to content

Commit

Permalink
refactor: fix startup re-renders (tari-project#1485)
Browse files Browse the repository at this point in the history
Description
---

Motivation and Context
---
- memoised our app & setup components
- removed unnecessary `useEffects` where possible
- split out the setters for `setupProgress` data so we can check if the
data from the BE has in fact changed before setting the item (this
caused a ton of re-renders in the `SetupProgress` components (and
subsequently all it's children and parents before memo-ising)

How Has This Been Tested?
---

locally with `react-devtools` profiling, and mac activity monitor. bot
on esme and nextnet.



**profile from `main` as of when tari-project#1487 was merged**:
  

[main_1487_profiling-data.07-02-2025.13-52-24.json](https://github.com/user-attachments/files/18706584/main_1487_profiling-data.07-02-2025.13-52-24.json)

**profile from this branch, up to date with `main` (at the same point as
above with tari-project#1487):**


[startup_mem-uptodate-profiling-data.07-02-2025.14-00-56.json](https://github.com/user-attachments/files/18706590/startup_mem-uptodate-profiling-data.07-02-2025.14-00-56.json)


---


**all the `SetupProgress` renders on `main`**:



https://github.com/user-attachments/assets/74cd9008-138f-4b32-a00e-a803a98e1539



**all the `SetupProgress` renders on here after :D**:



https://github.com/user-attachments/assets/93618d0e-acbe-42e3-b63e-ee3b73a3d1cc


---



| before (on main) | after |
| :---: | :---: |
| <img width="1840" alt="frame 1 render"
src="https://github.com/user-attachments/assets/1ed60d8c-98e3-4bc7-9542-a0e21eb1a7e5"
/> | <img width="1840" alt="frame 1 renders"
src="https://github.com/user-attachments/assets/130cfc0f-3473-4fdc-b29d-519ed6879fd3"
/> |
| <img width="1840" alt="frame 1 render2"
src="https://github.com/user-attachments/assets/88ca3d2b-9db5-4d3f-9e16-1e3d7c79b78b"
/> | <img width="1840" alt="frame1 renders2"
src="https://github.com/user-attachments/assets/a72c36c4-fe01-42cf-8b24-3db0a489f62e"
/> |
| <img width="1728" alt="infonav renders "
src="https://github.com/user-attachments/assets/1cc8520f-9e38-4909-9a7b-c002f7297d69"
/> | <img width="1728" alt="infonav renders"
src="https://github.com/user-attachments/assets/16992644-60c2-406f-bc00-2ad1bc2ffe41"
/> |
| <img width="1728" alt="mainview renders"
src="https://github.com/user-attachments/assets/9ed214f5-78ff-4349-8790-d2f6c3452681"
/> | <img width="1728" alt="mainview renders"
src="https://github.com/user-attachments/assets/61fb299a-d122-45ed-aa3c-a561a72dbd72"
/> |



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a configuration variable to reliably display the
application version.

- **Style**
- Updated canvas opacity and refined the progress indicator for a
smoother visual experience.

- **Refactor**
- Optimized numerous UI components and dialogs with memoization and
consolidated state logic for enhanced performance and clarity.

- **Chores**
  - Improved logging and timeout management to boost overall stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
shanimal08 authored Feb 11, 2025
1 parent b366257 commit d0ba475
Show file tree
Hide file tree
Showing 30 changed files with 280 additions and 234 deletions.
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_TARI_UNIVERSE_VERSION=$npm_package_version
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@
<body>
<main id="main">
<div id="root"></div>
<canvas id="canvas"></canvas>
<canvas id="canvas" style="opacity: 0"></canvas>
<script type="module" src="src/main.tsx"></script>
<script>
let time;
Expand Down
98 changes: 49 additions & 49 deletions src/App/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/react';
import { useEffect } from 'react';
import { memo, useMemo } from 'react';
import { AppContentContainer } from '@app/App/App.styles';
import { useShuttingDown } from '@app/hooks';

Expand All @@ -18,68 +18,68 @@ import ThemeProvider from '../theme/ThemeProvider.tsx';
import { useIsAppReady } from '@app/hooks/app/isAppReady.ts';
import Splashscreen from '@app/containers/phase/Splashscreen/Splashscreen.tsx';

export default function App() {
const CurrentAppSection = memo(function CurrentAppSection() {
const isAppReady = useIsAppReady();
const isShuttingDown = useShuttingDown();
const isSettingUp = useAppStateStore((s) => !s.setupComplete);
const setError = useAppStateStore((s) => s.setError);
const setIsWebglNotSupported = useUIStore((s) => s.setIsWebglNotSupported);
const { t } = useTranslation('common', { useSuspense: false });

useEffect(() => {
if (!window.WebGL2RenderingContext && !window.WebGLRenderingContext) {
Sentry.captureMessage('WebGL not supported by the browser', { extra: { userAgent: navigator.userAgent } });
setIsWebglNotSupported(true);
setError(t('webgl-not-supported'));
const currentSection = useMemo(() => {
const showSetup = isSettingUp && !isShuttingDown && isAppReady;
const showMainView = !isSettingUp && !isShuttingDown && isAppReady;
if (!isAppReady) {
return (
<AppContentContainer key="splashscreen" initial="hidden">
<Splashscreen />
</AppContentContainer>
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
const canvasElement = document.getElementById('canvas');
if (canvasElement) {
canvasElement.style.opacity = isShuttingDown || isSettingUp ? '0' : '1';
if (showSetup) {
return (
<AppContentContainer key="setup" initial="hidden">
<Setup />
</AppContentContainer>
);
}
}, [isShuttingDown, isSettingUp]);

const showSetup = isSettingUp && !isShuttingDown && isAppReady;
const showMainView = !isSettingUp && !isShuttingDown && isAppReady;
if (showMainView) {
return (
<AppContentContainer key="main" initial="dashboardInitial">
<MainView />
</AppContentContainer>
);
}

if (isShuttingDown) {
return (
<AppContentContainer key="shutdown" initial="hidden">
<ShuttingDownScreen />
</AppContentContainer>
);
}
}, [isAppReady, isSettingUp, isShuttingDown]);

return <AnimatePresence mode="popLayout">{currentSection}</AnimatePresence>;
});

export default function App() {
const setError = useAppStateStore((s) => s.setError);
const setIsWebglNotSupported = useUIStore((s) => s.setIsWebglNotSupported);

const { t } = useTranslation('common', { useSuspense: false });

if (!window.WebGL2RenderingContext && !window.WebGLRenderingContext) {
Sentry.captureMessage('WebGL not supported by the browser', { extra: { userAgent: navigator.userAgent } });
setIsWebglNotSupported(true);
setError(t('webgl-not-supported'));
}
return (
<ThemeProvider>
<GlobalReset />
<GlobalStyle />
<LazyMotion features={domAnimation} strict>
{/*
* added to reduce bundle size
* see https://www.framer.com/motion/guide-reduce-bundle-size/#synchronous-loading
* strict prop for using `m` instead of `motion`- see https://www.framer.com/motion/guide-reduce-bundle-size/#how-to-reduce-the-size-of-the-motion-component
*/}
<FloatingElements />
<AnimatePresence mode="popLayout">
{!isAppReady ? (
<AppContentContainer key="splashscreen" initial="hidden">
<Splashscreen />
</AppContentContainer>
) : null}

{showSetup ? (
<AppContentContainer key="setup" initial="hidden">
<Setup />
</AppContentContainer>
) : null}

{showMainView ? (
<AppContentContainer key="main" initial="dashboardInitial">
<MainView />
</AppContentContainer>
) : null}

{isShuttingDown && isAppReady ? (
<AppContentContainer key="shutdown" initial="hidden">
<ShuttingDownScreen />
</AppContentContainer>
) : null}
</AnimatePresence>
<CurrentAppSection />
</LazyMotion>
</ThemeProvider>
);
Expand Down
8 changes: 5 additions & 3 deletions src/components/AdminUI/AdminUI.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* eslint-disable i18next/no-literal-string */
import { MenuWrapper, MenuContent, ToggleButton } from './styles';
import { useFloating, offset, shift, flip, useClick, useInteractions, useDismiss } from '@floating-ui/react';
import { useState } from 'react';
import { memo, useState } from 'react';
import { ThemeGroup } from './groups/ThemeGroup';
import { DialogsGroup } from './groups/DialogsGroup';
import { GreenModalsGroup } from './groups/GreenModalsGroup';
import { ToastsGroup } from './groups/ToastsGroup';
import { OtherUIGroup } from './groups/OtherUIGroup';
import { AnimatePresence } from 'motion/react';

export default function AdminUI() {
const AdminUI = memo(function AdminUI() {
const [isOpen, setIsOpen] = useState(false);

const { refs, floatingStyles, context } = useFloating({
Expand Down Expand Up @@ -47,4 +47,6 @@ export default function AdminUI() {
</AnimatePresence>
</>
);
}
});

export default AdminUI;
16 changes: 9 additions & 7 deletions src/components/ToastStack/ToastStack.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { useState } from 'react';
import { memo, useCallback, useState } from 'react';
import { AnimatePresence } from 'motion/react';
import { Toast } from './Toast/Toast';
import { useToastStore } from './useToastStore';
import { useAppStateStore } from '@app/store/appStateStore';
import { Inside, Wrapper } from './styles';

export const ToastStack = () => {
const ToastStack = memo(function ToastStack() {
const { toasts } = useToastStore();
const isSettingUp = useAppStateStore((s) => !s.setupComplete);
const [isHovered, setIsHovered] = useState(false);

const handleMouseEnter = () => {
const handleMouseEnter = useCallback(() => {
setIsHovered(true);
};
}, []);

const handleMouseLeave = () => {
const handleMouseLeave = useCallback(() => {
setIsHovered(false);
};
}, []);

const reversedToasts = [...toasts].reverse();

Expand All @@ -42,4 +42,6 @@ export const ToastStack = () => {
</Wrapper>
</>
);
};
});

export default ToastStack;
35 changes: 22 additions & 13 deletions src/components/elements/LinearProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ const Wrapper = styled.div<{ $variant?: 'primary' | 'small' | 'tiny' }>`
width: 100%;
background: ${({ theme, $variant }) =>
$variant !== 'primary' ? convertHexToRGBA(theme.palette.contrast, 0.1) : theme.palette.base};
border-radius: 50px;
overflow: hidden;
align-items: center;
display: flex;
${({ $variant }) => {
switch ($variant) {
case 'tiny': {
Expand All @@ -37,15 +35,18 @@ const Wrapper = styled.div<{ $variant?: 'primary' | 'small' | 'tiny' }>`
}};
`;

const Bar = styled(m.div)<{ $variant?: 'primary' | 'small' | 'tiny' }>`
const BarSVG = styled.svg`
width: 100%;
height: 100%;
border-radius: 50px;
background: ${({ theme }) => theme.palette.contrast};
height: ${({ $variant }) => ($variant ? '5px' : '10px')};
will-change: width;
`;
const BarLine = styled(m.line)<{ $variant?: 'primary' | 'small' | 'tiny' }>`
stroke-width: ${({ $variant }) => ($variant === 'primary' ? '5px' : '10px')};
stroke: ${({ theme }) => theme.palette.contrast};
`;

export function LinearProgress({
value = 10,
value = 0,
variant = 'primary',
duration,
onAnimationComplete,
Expand All @@ -57,12 +58,20 @@ export function LinearProgress({
}) {
return (
<Wrapper $variant={variant}>
<Bar
initial={{ width: 0 }}
animate={{ width: `${value}%`, transition: { duration: duration || 0.5, ease: 'linear' } }}
$variant={variant}
onAnimationComplete={onAnimationComplete}
/>
<BarSVG strokeLinecap="round">
<BarLine
x1="0%"
y1="50%"
y2="50%"
fill="transparent"
strokeLinecap="round"
$variant={variant}
initial={{ x2: '0%' }}
animate={{ x2: `${value}%` }}
transition={{ duration: duration || 0.5, ease: 'linear' }}
onAnimationComplete={onAnimationComplete}
/>
</BarSVG>
</Wrapper>
);
}
8 changes: 5 additions & 3 deletions src/containers/floating/AutoUpdateDialog/AutoUpdateDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Typography } from '@app/components/elements/Typography';

import { UpdatedStatus } from './UpdatedStatus';
import { ButtonsWrapper } from './AutoUpdateDialog.styles';
import { useCallback, useEffect, useState } from 'react';
import { memo, useCallback, useEffect, useState } from 'react';
import { listen } from '@tauri-apps/api/event';
import { invoke } from '@tauri-apps/api/core';

Expand All @@ -23,7 +23,7 @@ interface AskForUpdatePayload {
version: string;
}

export default function AutoUpdateDialog() {
const AutoUpdateDialog = memo(function AutoUpdateDialog() {
const { t } = useTranslation('setup-view', { useSuspense: false });
const open = useUIStore((s) => s.dialogToShow === 'autoUpdate');
const setDialogToShow = useUIStore((s) => s.setDialogToShow);
Expand Down Expand Up @@ -97,4 +97,6 @@ export default function AutoUpdateDialog() {
</DialogContent>
</Dialog>
);
}
});

export default AutoUpdateDialog;
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { IoAlertCircleOutline, IoCheckmarkOutline, IoCopyOutline } from 'react-i
import { Trans, useTranslation } from 'react-i18next';
import { useAppStateStore } from '@app/store/appStateStore';
import { invoke } from '@tauri-apps/api/core';
import { useCallback, useState } from 'react';
import { memo, useCallback, useState } from 'react';
import { CircularProgress } from '@app/components/elements/CircularProgress';
import { SendLogsDialog } from '@app/components/dialogs/SendLogsDialog.tsx';
import { useUIStore } from '@app/store/useUIStore.ts';
Expand All @@ -16,7 +16,7 @@ import { SquaredButton } from '@app/components/elements/buttons/SquaredButton.ts
import { IconButton } from '@app/components/elements/buttons/IconButton';
import { TextButton } from '@app/components/elements/buttons/TextButton.tsx';

const CriticalErrorDialog = () => {
const CriticalErrorDialog = memo(function CriticalErrorDialog() {
const { t } = useTranslation(['common', 'settings'], { useSuspense: false });
const setDialogToShow = useUIStore((s) => s.setDialogToShow);
const { isCopied, copyToClipboard } = useCopyToClipboard();
Expand Down Expand Up @@ -81,6 +81,6 @@ const CriticalErrorDialog = () => {
</DialogContent>
</Dialog>
);
};
});

export default CriticalErrorDialog;
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { Typography } from '@app/components/elements/Typography';
import { useAppStateStore } from '@app/store/appStateStore';
import { invoke } from '@tauri-apps/api/core';

import { useCallback, useState } from 'react';
import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';

export const CriticalProblemDialog = () => {
const CriticalProblemDialog = memo(function CriticalProblemDialog() {
const { t } = useTranslation('common', { useSuspense: false });
const criticalProblem = useAppStateStore((s) => s.criticalProblem);
const [isExiting, setIsExiting] = useState(false);
Expand Down Expand Up @@ -45,4 +45,6 @@ export const CriticalProblemDialog = () => {
</DialogContent>
</Dialog>
);
};
});

export default CriticalProblemDialog;
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { useAppStateStore } from '@app/store/appStateStore';
import { setShowExternalDependenciesDialog, useUIStore } from '@app/store/useUIStore';
import { ExternalDependencyStatus } from '@app/types/app-status';
import { invoke } from '@tauri-apps/api/core';
import { useCallback, useState } from 'react';
import { memo, useCallback, useState } from 'react';
import { ExternalDependencyCard } from './ExternalDependencyCard';
import { useTranslation } from 'react-i18next';

export const ExternalDependenciesDialog = () => {
const ExternalDependenciesDialog = memo(function ExternalDependenciesDialog() {
const { t } = useTranslation('external-dependency-dialog', { useSuspense: false });
const showExternalDependenciesDialog = useUIStore((s) => s.showExternalDependenciesDialog);
const externalDependencies = useAppStateStore((s) => s.externalDependencies);
Expand Down Expand Up @@ -77,4 +77,5 @@ export const ExternalDependenciesDialog = () => {
</DialogContent>
</Dialog>
);
};
});
export default ExternalDependenciesDialog;
13 changes: 8 additions & 5 deletions src/containers/floating/FloatingElements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@ import { FloatingTree } from '@floating-ui/react';
import SettingsModal from './Settings/SettingsModal.tsx';
import StagedSecurity from './StagedSecurity/StagedSecurity.tsx';
import AutoUpdateDialog from './AutoUpdateDialog/AutoUpdateDialog.tsx';
import { ExternalDependenciesDialog } from './ExternalDependenciesDialog/ExternalDependenciesDialog.tsx';
import ExternalDependenciesDialog from './ExternalDependenciesDialog/ExternalDependenciesDialog.tsx';
import CriticalErrorDialog from './CriticalErrorDialog/CriticalErrorDialog.tsx';
import PaperWalletModal from './PaperWalletModal/PaperWalletModal.tsx';
import ShareRewardModal from './ShareRewardModal/ShareRewardModal';
import AdminUI from '@app/components/AdminUI/AdminUI.tsx';
import { ToastStack } from '@app/components/ToastStack/ToastStack.tsx';
import { CriticalProblemDialog } from './CriticalProblemDialog/CriticalProblemDialog.tsx';
import ToastStack from '@app/components/ToastStack/ToastStack.tsx';
import CriticalProblemDialog from './CriticalProblemDialog/CriticalProblemDialog.tsx';
import ShellOfSecrets from '../main/ShellOfSecrets/ShellOfSecrets.tsx';
import ReleaseNotesDialog from './ReleaseNotesDialog/ReleaseNotesDialog.tsx';
import LudicrousCofirmationDialog from './LudicrousCofirmationDialog/LudicrousCofirmationDialog.tsx';
import { memo } from 'react';

const environment = import.meta.env.MODE;

export default function FloatingElements() {
const FloatingElements = memo(function FloatingElements() {
return (
<FloatingTree>
<SettingsModal />
Expand All @@ -34,4 +35,6 @@ export default function FloatingElements() {
{environment === 'development' && <AdminUI />}
</FloatingTree>
);
}
});

export default FloatingElements;
Loading

0 comments on commit d0ba475

Please sign in to comment.