From f8af8f95ac4ab2bdacc58cf2bf1e9073da8b11ae Mon Sep 17 00:00:00 2001
From: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>
Date: Fri, 26 Apr 2024 14:03:01 -0700
Subject: [PATCH] Fix loading screen not showing when opening a budget file
 (#2663)

* Fix app loading screen

* Release note

* Code review feedback
---
 .../desktop-client/src/components/App.tsx     | 73 ++++++++++++-------
 .../src/components/AppBackground.tsx          |  2 +-
 .../src/components/manager/BudgetList.jsx     | 56 +++++++-------
 packages/loot-core/src/types/file.d.ts        |  2 +-
 upcoming-release-notes/2663.md                |  6 ++
 5 files changed, 82 insertions(+), 57 deletions(-)
 create mode 100644 upcoming-release-notes/2663.md

diff --git a/packages/desktop-client/src/components/App.tsx b/packages/desktop-client/src/components/App.tsx
index 692f1986c..fa06630dd 100644
--- a/packages/desktop-client/src/components/App.tsx
+++ b/packages/desktop-client/src/components/App.tsx
@@ -6,8 +6,15 @@ import {
   type FallbackProps,
 } from 'react-error-boundary';
 import { HotkeysProvider } from 'react-hotkeys-hook';
-import { useSelector } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
+import {
+  closeBudget,
+  loadBudget,
+  loadGlobalPrefs,
+  setAppState,
+  sync,
+} from 'loot-core/client/actions';
 import * as Platform from 'loot-core/src/client/platform';
 import { type State } from 'loot-core/src/client/state-types';
 import {
@@ -15,7 +22,6 @@ import {
   send,
 } from 'loot-core/src/platform/client/fetch';
 
-import { useActions } from '../hooks/useActions';
 import { useLocalPref } from '../hooks/useLocalPref';
 import { installPolyfills } from '../polyfills';
 import { ResponsiveProvider } from '../ResponsiveProvider';
@@ -38,37 +44,49 @@ type AppInnerProps = {
 function AppInner({ budgetId, cloudFileId }: AppInnerProps) {
   const [initializing, setInitializing] = useState(true);
   const { showBoundary: showErrorBoundary } = useErrorBoundary();
-  const stateLoadingText = useSelector((state: State) => state.app.loadingText);
-  const [loadingText = stateLoadingText, setLoadingText] = useState<
-    string | null
-  >(null);
-  const { loadBudget, closeBudget, loadGlobalPrefs } = useActions();
+  const loadingText = useSelector((state: State) => state.app.loadingText);
+  const dispatch = useDispatch();
 
   async function init() {
     const socketName = await global.Actual.getServerSocket();
 
-    setLoadingText('Initializing the connection to the local database...');
+    dispatch(
+      setAppState({
+        loadingText: 'Initializing the connection to the local database...',
+      }),
+    );
     await initConnection(socketName);
 
     // Load any global prefs
-    setLoadingText('Loading global preferences...');
-    await loadGlobalPrefs();
+    dispatch(
+      setAppState({
+        loadingText: 'Loading global preferences...',
+      }),
+    );
+    await dispatch(loadGlobalPrefs());
 
     // Open the last opened budget, if any
-    setLoadingText('Opening last budget...');
+    dispatch(
+      setAppState({
+        loadingText: 'Opening last budget...',
+      }),
+    );
     const budgetId = await send('get-last-opened-backup');
     if (budgetId) {
-      setLoadingText('Loading the last budget file...');
-      await loadBudget(budgetId);
+      await dispatch(loadBudget(budgetId, 'Loading the last budget file...'));
 
       // Check to see if this file has been remotely deleted (but
       // don't block on this in case they are offline or something)
-      setLoadingText('Retrieving remote files...');
+      dispatch(
+        setAppState({
+          loadingText: 'Retrieving remote files...',
+        }),
+      );
       send('get-remote-files').then(files => {
         if (files) {
           const remoteFile = files.find(f => f.fileId === cloudFileId);
           if (remoteFile && remoteFile.deleted) {
-            closeBudget();
+            dispatch(closeBudget());
           }
         }
       });
@@ -79,7 +97,11 @@ function AppInner({ budgetId, cloudFileId }: AppInnerProps) {
     async function initAll() {
       await Promise.all([installPolyfills(), init()]);
       setInitializing(false);
-      setLoadingText(null);
+      dispatch(
+        setAppState({
+          loadingText: null,
+        }),
+      );
     }
 
     initAll().catch(showErrorBoundary);
@@ -91,18 +113,13 @@ function AppInner({ budgetId, cloudFileId }: AppInnerProps) {
 
   return (
     <>
-      {initializing ? (
+      {(initializing || !budgetId) && (
         <AppBackground initializing={initializing} loadingText={loadingText} />
-      ) : budgetId ? (
+      )}
+      {budgetId ? (
         <FinancesApp />
       ) : (
-        <>
-          <AppBackground
-            initializing={initializing}
-            loadingText={loadingText}
-          />
-          <ManagementApp isLoading={loadingText != null} />
-        </>
+        <ManagementApp isLoading={loadingText != null} />
       )}
 
       <UpdateNotification />
@@ -123,10 +140,10 @@ function ErrorFallback({ error }: FallbackProps) {
 export function App() {
   const [budgetId] = useLocalPref('id');
   const [cloudFileId] = useLocalPref('cloudFileId');
-  const { sync } = useActions();
   const [hiddenScrollbars, setHiddenScrollbars] = useState(
     hasHiddenScrollbars(),
   );
+  const dispatch = useDispatch();
 
   useEffect(() => {
     function checkScrollbars() {
@@ -141,7 +158,7 @@ export function App() {
       if (!isSyncing) {
         console.debug('triggering sync because of visibility change');
         isSyncing = true;
-        await sync();
+        await dispatch(sync());
         isSyncing = false;
       }
     }
@@ -153,7 +170,7 @@ export function App() {
       window.removeEventListener('focus', checkScrollbars);
       window.removeEventListener('visibilitychange', onVisibilityChange);
     };
-  }, [sync]);
+  }, [dispatch]);
 
   return (
     <HotkeysProvider initiallyActiveScopes={['*']}>
diff --git a/packages/desktop-client/src/components/AppBackground.tsx b/packages/desktop-client/src/components/AppBackground.tsx
index daff02ad6..698581aec 100644
--- a/packages/desktop-client/src/components/AppBackground.tsx
+++ b/packages/desktop-client/src/components/AppBackground.tsx
@@ -22,7 +22,7 @@ export function AppBackground({
   const transitions = useTransition(loadingText, {
     from: { opacity: 0, transform: 'translateY(-100px)' },
     enter: { opacity: 1, transform: 'translateY(0)' },
-    leave: { opacity: 0, transform: 'translateY(-100px)' },
+    leave: { opacity: 0, transform: 'translateY(100px)' },
     unique: true,
   });
 
diff --git a/packages/desktop-client/src/components/manager/BudgetList.jsx b/packages/desktop-client/src/components/manager/BudgetList.jsx
index 4ca30a418..33cee3764 100644
--- a/packages/desktop-client/src/components/manager/BudgetList.jsx
+++ b/packages/desktop-client/src/components/manager/BudgetList.jsx
@@ -1,10 +1,16 @@
 import React, { useState, useRef } from 'react';
-import { useSelector } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
-import * as actions from 'loot-core/src/client/actions';
+import {
+  createBudget,
+  downloadBudget,
+  getUserData,
+  loadAllFiles,
+  loadBudget,
+  pushModal,
+} from 'loot-core/client/actions';
 import { isNonProductionEnvironment } from 'loot-core/src/shared/environment';
 
-import { useActions } from '../../hooks/useActions';
 import { AnimatedLoading } from '../../icons/AnimatedLoading';
 import {
   SvgCloudCheck,
@@ -13,6 +19,7 @@ import {
   SvgFileDouble,
 } from '../../icons/v1';
 import { SvgCloudUnknown, SvgKey, SvgRefreshArrow } from '../../icons/v2';
+import { useResponsive } from '../../ResponsiveProvider';
 import { styles, theme } from '../../style';
 import { tokens } from '../../tokens';
 import { Button } from '../common/Button';
@@ -247,22 +254,19 @@ function RefreshButton({ onRefresh }) {
 
 export function BudgetList() {
   const files = useSelector(state => state.budgets.allFiles || []);
-
-  const {
-    getUserData,
-    loadAllFiles,
-    pushModal,
-    loadBudget,
-    createBudget,
-    downloadBudget,
-  } = useActions();
-
+  const dispatch = useDispatch();
   const [creating, setCreating] = useState(false);
+  const { isNarrowWidth } = useResponsive();
+  const narrowButtonStyle = isNarrowWidth
+    ? {
+        height: styles.mobileMinHeight,
+      }
+    : {};
 
   const onCreate = ({ testMode } = {}) => {
     if (!creating) {
       setCreating(true);
-      createBudget({ testMode });
+      dispatch(createBudget({ testMode }));
     }
   };
 
@@ -294,47 +298,45 @@ export function BudgetList() {
         >
           <RefreshButton
             onRefresh={() => {
-              getUserData();
-              loadAllFiles();
+              dispatch(getUserData());
+              dispatch(loadAllFiles());
             }}
           />
         </View>
       </View>
       <BudgetTable
         files={files}
-        actions={actions}
         onSelect={file => {
           if (file.state === 'remote') {
-            downloadBudget(file.cloudFileId);
+            dispatch(downloadBudget(file.cloudFileId));
           } else {
-            loadBudget(file.id);
+            dispatch(loadBudget(file.id, `Loading ${file.name}...`));
           }
         }}
-        onDelete={file => pushModal('delete-budget', { file })}
+        onDelete={file => dispatch(pushModal('delete-budget', { file }))}
       />
       <View
         style={{
           flexDirection: 'row',
           justifyContent: 'flex-end',
           padding: 25,
-          paddingLeft: 5,
+          gap: 15,
         }}
       >
         <Button
           type="bare"
           style={{
-            marginLeft: 10,
+            ...narrowButtonStyle,
             color: theme.pageTextLight,
           }}
-          onClick={e => {
-            e.preventDefault();
-            pushModal('import');
+          onClick={() => {
+            dispatch(pushModal('import'));
           }}
         >
           Import file
         </Button>
 
-        <Button type="primary" onClick={onCreate} style={{ marginLeft: 15 }}>
+        <Button type="primary" onClick={onCreate} style={narrowButtonStyle}>
           Create new file
         </Button>
 
@@ -343,7 +345,7 @@ export function BudgetList() {
             type="primary"
             isSubmit={false}
             onClick={() => onCreate({ testMode: true })}
-            style={{ marginLeft: 15 }}
+            style={narrowButtonStyle}
           >
             Create test file
           </Button>
diff --git a/packages/loot-core/src/types/file.d.ts b/packages/loot-core/src/types/file.d.ts
index 30301b1fb..9302a7c76 100644
--- a/packages/loot-core/src/types/file.d.ts
+++ b/packages/loot-core/src/types/file.d.ts
@@ -30,7 +30,7 @@ export type RemoteFile = {
   cloudFileId: string;
   groupId: string;
   name: string;
-  enccryptKeyId?: string;
+  encryptKeyId?: string;
   hasKey: boolean;
   state: 'remote';
 };
diff --git a/upcoming-release-notes/2663.md b/upcoming-release-notes/2663.md
new file mode 100644
index 000000000..bd63ad395
--- /dev/null
+++ b/upcoming-release-notes/2663.md
@@ -0,0 +1,6 @@
+---
+category: Bugfix
+authors: [joel-jeremy]
+---
+
+Fix app loading screen not showing when opening a budget file.
-- 
GitLab