diff --git a/electron/main.js b/electron/main.js
index 7809487e3..c30d7b3e2 100644
--- a/electron/main.js
+++ b/electron/main.js
@@ -554,6 +554,19 @@ ipcMain.on('open-path', (_, filePath) => {
shell.openPath(filePath);
});
+function getSanitizedLogs({ name, filePath, data }) {
+ const logs = filePath ? fs.readFileSync(filePath, 'utf-8') : data;
+ const tempDir = os.tmpdir();
+
+ const usernameRegex = /\/Users\/([^/]+)/g;
+ const sanitizedData = logs.replace(usernameRegex, '/Users/*****');
+
+ const sanitizedLogsFilePath = path.join(tempDir, name);
+ fs.writeFileSync(sanitizedLogsFilePath, sanitizedData);
+
+ return sanitizedLogsFilePath;
+}
+
// EXPORT LOGS
ipcMain.handle('save-logs', async (_, data) => {
// version.txt
@@ -631,16 +644,3 @@ ipcMain.handle('save-logs', async (_, data) => {
return result;
});
-
-function getSanitizedLogs({ name, filePath, data }) {
- const logs = filePath ? fs.readFileSync(filePath, 'utf-8') : data;
- const tempDir = os.tmpdir();
-
- const usernameRegex = /\/Users\/([^/]+)/g;
- const sanitizedData = logs.replace(usernameRegex, '/Users/*****');
-
- const sanitizedLogsFilePath = path.join(tempDir, name);
- fs.writeFileSync(sanitizedLogsFilePath, sanitizedData);
-
- return sanitizedLogsFilePath;
-}
diff --git a/frontend/components/HelpAndSupport/HelpAndSupport.tsx b/frontend/components/HelpAndSupport/HelpAndSupport.tsx
index e26cedd63..431d1994e 100644
--- a/frontend/components/HelpAndSupport/HelpAndSupport.tsx
+++ b/frontend/components/HelpAndSupport/HelpAndSupport.tsx
@@ -2,14 +2,11 @@ import { CloseOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import { Button, Card, Flex, message, Typography } from 'antd';
import { useCallback, useEffect, useState } from 'react';
-import { DeploymentStatus } from '@/client';
import { FAQ_URL, SUPPORT_URL } from '@/constants';
import { UNICODE_SYMBOLS } from '@/constants/unicode';
import { PageState } from '@/enums';
-import { useBalance, usePageState, useServices } from '@/hooks';
+import { useLogs, usePageState } from '@/hooks';
import { useElectronApi } from '@/hooks/useElectronApi';
-import { useMasterSafe } from '@/hooks/useMasterSafe';
-import { useStore } from '@/hooks/useStore';
import { CardTitle } from '../common/CardTitle';
import { CardSection } from '../styled/CardSection';
@@ -27,118 +24,50 @@ const SettingsTitle = () => (
/>
);
+const LogsSavedMessage = ({ onClick }: { onClick: () => void }) => {
+ return (
+
+ Logs saved
+
+
+ );
+};
+
export const HelpAndSupport = () => {
const { goto } = usePageState();
- const { saveLogs, openPath } = useElectronApi();
-
- const { storeState } = useStore();
- const {
- serviceStatus,
- services,
- hasInitialLoaded: isServiceLoaded,
- } = useServices();
- const {
- isBalanceLoaded,
- totalEthBalance,
- totalOlasBalance,
- wallets,
- walletBalances,
- totalOlasStakedBalance,
- } = useBalance();
+ const { openPath, saveLogs } = useElectronApi();
- const {
- backupSafeAddress,
- masterSafeAddress,
- masterEoaAddress,
- masterSafeOwners,
- } = useMasterSafe();
+ const logs = useLogs();
const [isLoading, setIsLoading] = useState(false);
- const [canSaveLogs, setCanSafeLogs] = useState(false);
+ const [canSaveLogs, setCanSaveLogs] = useState(false);
- const onSaveLogs = useCallback(() => {
- setIsLoading(true);
- setCanSafeLogs(true);
- }, []);
-
- const handleSaveLogs = useCallback(() => {
- return saveLogs?.({
- store: storeState,
- debugData: {
- services: {
- services:
- services?.map((item) => ({
- ...item,
- keys: item.keys.map((key) => key.address),
- })) ?? 'undefined',
- serviceStatus: serviceStatus
- ? DeploymentStatus[serviceStatus]
- : 'undefined',
- },
- addresses: [
- { backupSafeAddress: backupSafeAddress ?? 'undefined' },
- { masterSafeAddress: masterSafeAddress ?? 'undefined' },
- { masterEoaAddress: masterEoaAddress ?? 'undefined' },
- { masterSafeOwners: masterSafeOwners ?? 'undefined' },
- ],
- balances: [
- { wallets: wallets ?? 'undefined' },
- { walletBalances: walletBalances ?? 'undefined' },
- { totalOlasStakedBalance: totalOlasStakedBalance ?? 'undefined' },
- { totalEthBalance: totalEthBalance ?? 'undefined' },
- { totalOlasBalance: totalOlasBalance ?? 'undefined' },
- ],
- },
- }).then((result) => {
- if (result.success) {
- message.success({
- content: (
-
- Logs saved to:
-
-
- ),
- duration: 10,
- });
- } else {
- message.error('Save logs failed or cancelled');
- }
- });
- }, [
- backupSafeAddress,
- masterEoaAddress,
- masterSafeAddress,
- masterSafeOwners,
- openPath,
- saveLogs,
- serviceStatus,
- services,
- storeState,
- totalEthBalance,
- totalOlasBalance,
- totalOlasStakedBalance,
- walletBalances,
- wallets,
- ]);
+ const onSaveLogs = useCallback(() => setCanSaveLogs(true), []);
useEffect(() => {
- // only save logs when all needed data is loaded
- if (canSaveLogs && isBalanceLoaded && isServiceLoaded) {
- handleSaveLogs()?.finally(() => {
- setIsLoading(false);
- setCanSafeLogs(false);
- });
+ if (canSaveLogs && logs && !isLoading) {
+ setIsLoading(true);
+ saveLogs?.(logs)
+ .then((result) => {
+ if (result.success) {
+ message.success({
+ content: (
+ openPath?.(result.dirPath)} />
+ ),
+ duration: 10,
+ });
+ } else {
+ message.error('Save logs failed or cancelled');
+ }
+ })
+ .finally(() => {
+ setIsLoading(false);
+ setCanSaveLogs(false);
+ });
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [canSaveLogs, isBalanceLoaded, isServiceLoaded]);
+ }, [canSaveLogs, isLoading, logs, openPath, saveLogs]);
return (
{
type="primary"
ghost
size="large"
- loading={isLoading}
+ loading={isLoading || canSaveLogs}
onClick={onSaveLogs}
>
Export logs
diff --git a/frontend/context/ElectronApiProvider.tsx b/frontend/context/ElectronApiProvider.tsx
index 497374597..64013aff5 100644
--- a/frontend/context/ElectronApiProvider.tsx
+++ b/frontend/context/ElectronApiProvider.tsx
@@ -28,7 +28,7 @@ type ElectronApiContextProps = {
saveLogs?: (data: {
store?: ElectronStore;
debugData?: Record;
- }) => Promise<{ success?: boolean; dirPath?: string }>;
+ }) => Promise<{ success: true; dirPath: string } | { success: false }>;
openPath?: (filePath: string) => void;
};
diff --git a/frontend/hooks/index.ts b/frontend/hooks/index.ts
index 98d7f6ca3..3f43ba7ba 100644
--- a/frontend/hooks/index.ts
+++ b/frontend/hooks/index.ts
@@ -1,4 +1,5 @@
export * from './useBalance';
+export * from './useLogs';
export * from './usePageState';
export * from './useServices';
export * from './useServiceTemplates';
diff --git a/frontend/hooks/useLogs.ts b/frontend/hooks/useLogs.ts
new file mode 100644
index 000000000..b72e59c3b
--- /dev/null
+++ b/frontend/hooks/useLogs.ts
@@ -0,0 +1,96 @@
+import { useMemo } from 'react';
+
+import { DeploymentStatus } from '@/client';
+
+import { useBalance } from './useBalance';
+import { useMasterSafe } from './useMasterSafe';
+import { useServices } from './useServices';
+import { useStore } from './useStore';
+import { useWallet } from './useWallet';
+
+const useAddressesLogs = () => {
+ const { wallets, masterEoaAddress, masterSafeAddress } = useWallet();
+
+ const { backupSafeAddress, masterSafeOwners } = useMasterSafe();
+
+ return {
+ isLoaded: wallets?.length !== 0 && !!masterSafeOwners,
+ data: [
+ { backupSafeAddress: backupSafeAddress ?? 'undefined' },
+ { masterSafeAddress: masterSafeAddress ?? 'undefined' },
+ { masterEoaAddress: masterEoaAddress ?? 'undefined' },
+ { masterSafeOwners: masterSafeOwners ?? 'undefined' },
+ ],
+ };
+};
+
+const useBalancesLogs = () => {
+ const {
+ isBalanceLoaded,
+ totalEthBalance,
+ totalOlasBalance,
+ wallets,
+ walletBalances,
+ totalOlasStakedBalance,
+ } = useBalance();
+
+ return {
+ isLoaded: isBalanceLoaded,
+ data: [
+ { wallets: wallets ?? 'undefined' },
+ { walletBalances: walletBalances ?? 'undefined' },
+ { totalOlasStakedBalance: totalOlasStakedBalance ?? 'undefined' },
+ { totalEthBalance: totalEthBalance ?? 'undefined' },
+ { totalOlasBalance: totalOlasBalance ?? 'undefined' },
+ ],
+ };
+};
+
+const useServicesLogs = () => {
+ const { serviceStatus, services, hasInitialLoaded } = useServices();
+
+ return {
+ isLoaded: hasInitialLoaded,
+ data: {
+ serviceStatus: serviceStatus
+ ? DeploymentStatus[serviceStatus]
+ : 'undefined',
+ services:
+ services?.map((item) => ({
+ ...item,
+ keys: item.keys.map((key) => key.address),
+ })) ?? 'undefined',
+ },
+ };
+};
+
+export const useLogs = () => {
+ const { storeState } = useStore();
+
+ const { isLoaded: isServicesLoaded, data: services } = useServicesLogs();
+ const { isLoaded: isBalancesLoaded, data: balances } = useBalancesLogs();
+ const { isLoaded: isAddressesLoaded, data: addresses } = useAddressesLogs();
+
+ const logs = useMemo(() => {
+ if (isServicesLoaded && isBalancesLoaded && isAddressesLoaded) {
+ return {
+ store: storeState,
+ debugData: {
+ services,
+ addresses,
+ balances,
+ },
+ };
+ }
+ }, [
+ addresses,
+ balances,
+ isAddressesLoaded,
+ isBalancesLoaded,
+ isServicesLoaded,
+ services,
+ storeState,
+ ]);
+
+ return logs;
+};