@dashadmin/dash-utils 1.3.73 → 1.3.74-srcfree.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/LazyAppLoader.d.ts +17 -0
- package/dist/components/MinimalLayout.d.ts +6 -0
- package/dist/components/RouterComponent.d.ts +22 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/contexts/IPCMessageBrokerContext.d.ts +42 -0
- package/dist/hooks/useCapacitorAppStateRefresh.d.ts +2 -0
- package/dist/hooks/useCapacitorKeyboard.d.ts +19 -0
- package/dist/hooks/useDeviceStorageSync.d.ts +4 -0
- package/dist/hooks/useDraggable.d.ts +24 -0
- package/dist/hooks/useMenuStateInit.d.ts +10 -0
- package/dist/hooks/useMobileDetection.d.ts +20 -0
- package/dist/hooks/useNotifications.d.ts +51 -0
- package/dist/hooks/useQuickSearch.d.ts +6 -0
- package/dist/hooks/useWindowSize.d.ts +6 -0
- package/dist/index.d.ts +30 -0
- package/dist/utils/authProviderUtils.d.ts +37 -0
- package/dist/utils/dashBootstrapApp.d.ts +9 -0
- package/dist/utils/dashDtorage.d.ts +8 -0
- package/dist/utils/dashIPCService.d.ts +93 -0
- package/dist/utils/dataProviderUtils.d.ts +44 -0
- package/dist/utils/electronStoreSync.d.ts +3 -0
- package/dist/utils/envUtils.d.ts +1 -0
- package/dist/utils/environmentBodyClasses.d.ts +11 -0
- package/dist/utils/isDev.d.ts +5 -0
- package/dist/utils/notificationPermission.d.ts +5 -0
- package/dist/utils/platformDetection.d.ts +13 -0
- package/dist/utils/priceFormatter.d.ts +2 -0
- package/dist/utils/resourceLoader.d.ts +8 -0
- package/dist/utils/speechService.d.ts +7 -0
- package/dist/utils/storedTheme.d.ts +11 -0
- package/dist/utils/updateDomCssVariables.d.ts +6 -0
- package/dist/utils/validation.d.ts +12 -0
- package/package.json +13 -10
- package/src/components/LazyAppLoader.tsx +0 -143
- package/src/components/MinimalLayout.tsx +0 -22
- package/src/components/RouterComponent.tsx +0 -169
- package/src/components/index.ts +0 -16
- package/src/contexts/IPCMessageBrokerContext.tsx +0 -303
- package/src/hooks/useCapacitorAppStateRefresh.ts +0 -34
- package/src/hooks/useCapacitorKeyboard.ts +0 -213
- package/src/hooks/useDeviceStorageSync.ts +0 -70
- package/src/hooks/useDraggable.ts +0 -133
- package/src/hooks/useMenuStateInit.ts +0 -85
- package/src/hooks/useMobileDetection.ts +0 -165
- package/src/hooks/useNotifications.ts +0 -220
- package/src/hooks/useQuickSearch.ts +0 -42
- package/src/hooks/useWindowSize.tsx +0 -36
- package/src/index.ts +0 -202
- package/src/utils/authProviderUtils.ts +0 -170
- package/src/utils/dashBootstrapApp.ts +0 -104
- package/src/utils/dashDtorage.tsx +0 -51
- package/src/utils/dashDtorage.tsx.old +0 -78
- package/src/utils/dashIPCService.ts +0 -204
- package/src/utils/dataProviderUtils.ts +0 -221
- package/src/utils/electronStoreSync.ts +0 -34
- package/src/utils/envUtils.tsx +0 -27
- package/src/utils/environmentBodyClasses.ts +0 -72
- package/src/utils/isDev.ts +0 -65
- package/src/utils/notificationPermission.ts +0 -57
- package/src/utils/platformDetection.ts +0 -98
- package/src/utils/priceFormatter.ts +0 -36
- package/src/utils/resourceLoader.ts +0 -107
- package/src/utils/speechService.ts +0 -112
- package/src/utils/storedTheme.ts +0 -60
- package/src/utils/updateDomCssVariables.tsx +0 -181
- package/src/utils/validation.ts +0 -147
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lazy App Loader Utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides utilities for creating lazy-loaded app components with
|
|
5
|
-
* error handling and initialization delays.
|
|
6
|
-
*/
|
|
7
|
-
import React from 'react';
|
|
8
|
-
|
|
9
|
-
export interface LazyAppLoaderConfig {
|
|
10
|
-
/**
|
|
11
|
-
* Dynamic import function that returns the module with the app component
|
|
12
|
-
* Example: () => import('./DASHAppLoader')
|
|
13
|
-
*/
|
|
14
|
-
importFn: () => Promise<{ default: React.ComponentType<any> }>;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Delay in milliseconds before loading the component (default: 150ms)
|
|
18
|
-
* This helps ensure all modules are properly initialized
|
|
19
|
-
*/
|
|
20
|
-
initDelay?: number;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Custom fallback component to show if loading fails
|
|
24
|
-
* If not provided, a default error message is shown
|
|
25
|
-
*/
|
|
26
|
-
fallbackComponent?: React.ComponentType<any>;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Error handler callback
|
|
30
|
-
*/
|
|
31
|
-
onError?: (error: Error) => void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Default fallback component shown when app loading fails
|
|
36
|
-
*/
|
|
37
|
-
export const DefaultLoadFailedComponent: React.FC<{ message?: string }> = ({
|
|
38
|
-
message = 'Failed to load application'
|
|
39
|
-
}) => (
|
|
40
|
-
<div style={{
|
|
41
|
-
display: 'flex',
|
|
42
|
-
justifyContent: 'center',
|
|
43
|
-
alignItems: 'center',
|
|
44
|
-
height: '100vh',
|
|
45
|
-
background: 'linear-gradient(to bottom, var(--bodybg-primary), var(--bodybg-secondary))',
|
|
46
|
-
color: '#ff4444',
|
|
47
|
-
flexDirection: 'column',
|
|
48
|
-
gap: '16px'
|
|
49
|
-
}}>
|
|
50
|
-
<div style={{ fontSize: '18px' }}>{message}</div>
|
|
51
|
-
<button
|
|
52
|
-
onClick={() => window.location.reload()}
|
|
53
|
-
style={{
|
|
54
|
-
padding: '10px 20px',
|
|
55
|
-
backgroundColor: '#007bff',
|
|
56
|
-
color: 'white',
|
|
57
|
-
border: 'none',
|
|
58
|
-
borderRadius: '4px',
|
|
59
|
-
cursor: 'pointer'
|
|
60
|
-
}}
|
|
61
|
-
>
|
|
62
|
-
Reload Page
|
|
63
|
-
</button>
|
|
64
|
-
</div>
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Create a lazy-loaded app component with error handling
|
|
69
|
-
*
|
|
70
|
-
* @param config - Configuration options for the lazy loader
|
|
71
|
-
* @returns A React lazy component
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* ```tsx
|
|
75
|
-
* const AppComponent = createLazyAppComponent({
|
|
76
|
-
* importFn: () => import('./DASHAppLoader'),
|
|
77
|
-
* initDelay: 150,
|
|
78
|
-
* onError: (error) => console.error('Failed to load app:', error)
|
|
79
|
-
* });
|
|
80
|
-
*
|
|
81
|
-
* // Then use with Suspense:
|
|
82
|
-
* <React.Suspense fallback={<InitialLoader />}>
|
|
83
|
-
* <AppComponent />
|
|
84
|
-
* </React.Suspense>
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
export const createLazyAppComponent = (
|
|
88
|
-
config: LazyAppLoaderConfig
|
|
89
|
-
): React.LazyExoticComponent<React.ComponentType<any>> => {
|
|
90
|
-
const {
|
|
91
|
-
importFn,
|
|
92
|
-
initDelay = 150,
|
|
93
|
-
fallbackComponent: FallbackComponent,
|
|
94
|
-
onError
|
|
95
|
-
} = config;
|
|
96
|
-
|
|
97
|
-
return React.lazy(() => {
|
|
98
|
-
return new Promise<{ default: React.ComponentType<any> }>((resolve) => {
|
|
99
|
-
setTimeout(() => {
|
|
100
|
-
importFn()
|
|
101
|
-
.then((module) => {
|
|
102
|
-
resolve({ default: module.default });
|
|
103
|
-
})
|
|
104
|
-
.catch((error: Error) => {
|
|
105
|
-
console.error('Failed to load app component:', error);
|
|
106
|
-
onError?.(error);
|
|
107
|
-
|
|
108
|
-
// Use custom fallback or default
|
|
109
|
-
const ErrorComponent: React.FC = FallbackComponent
|
|
110
|
-
? () => React.createElement(FallbackComponent)
|
|
111
|
-
: () => React.createElement(DefaultLoadFailedComponent, {
|
|
112
|
-
message: `Failed to load: ${error.message}`
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
resolve({ default: ErrorComponent });
|
|
116
|
-
});
|
|
117
|
-
}, initDelay);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Simple version of createLazyAppComponent with minimal configuration
|
|
124
|
-
*
|
|
125
|
-
* @param importFn - Dynamic import function
|
|
126
|
-
* @param initDelay - Optional delay in milliseconds (default: 150)
|
|
127
|
-
* @returns A React lazy component
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```tsx
|
|
131
|
-
* const AppComponent = createSimpleLazyApp(
|
|
132
|
-
* () => import('./DASHAppLoader')
|
|
133
|
-
* );
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
|
-
export const createSimpleLazyApp = (
|
|
137
|
-
importFn: () => Promise<{ default: React.ComponentType<any> }>,
|
|
138
|
-
initDelay: number = 150
|
|
139
|
-
): React.LazyExoticComponent<React.ComponentType<any>> => {
|
|
140
|
-
return createLazyAppComponent({ importFn, initDelay });
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
export default createLazyAppComponent;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MinimalLayout Component
|
|
3
|
-
*
|
|
4
|
-
* A minimal layout wrapper that simply renders its children.
|
|
5
|
-
* Useful as a pass-through layout when you want React-Admin
|
|
6
|
-
* to use a different layout than the default.
|
|
7
|
-
*/
|
|
8
|
-
import React from 'react';
|
|
9
|
-
|
|
10
|
-
export interface MinimalLayoutProps {
|
|
11
|
-
children?: React.ReactNode;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* MinimalLayout - A pass-through layout component
|
|
16
|
-
* Simply renders children without any wrapper elements
|
|
17
|
-
*/
|
|
18
|
-
export const MinimalLayout: React.FC<MinimalLayoutProps> = ({ children }) => {
|
|
19
|
-
return <>{children}</>;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export default MinimalLayout;
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DashRouterComponent
|
|
3
|
-
*
|
|
4
|
-
* A smart router component that automatically chooses between
|
|
5
|
-
* HashRouter (for Electron/desktop) and BrowserRouter (for web).
|
|
6
|
-
*
|
|
7
|
-
* Environment detection is automatic when props are not provided.
|
|
8
|
-
*/
|
|
9
|
-
import React, { Suspense } from 'react';
|
|
10
|
-
import { BrowserRouter, HashRouter, Routes } from 'react-router-dom';
|
|
11
|
-
import { getEnv } from '../utils/envUtils';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Props for DashRouterComponent
|
|
15
|
-
*/
|
|
16
|
-
export interface DashRouterComponentProps {
|
|
17
|
-
/**
|
|
18
|
-
* Direct children - used when publicRoutes is not provided
|
|
19
|
-
* @deprecated Use publicRoutes prop instead for cleaner API
|
|
20
|
-
*/
|
|
21
|
-
children?: React.ReactNode;
|
|
22
|
-
/** Force a specific router type */
|
|
23
|
-
forceType?: 'hash' | 'browser';
|
|
24
|
-
/**
|
|
25
|
-
* Base path for BrowserRouter
|
|
26
|
-
* @default getEnv('BASE_PATH') || '/'
|
|
27
|
-
*/
|
|
28
|
-
basename?: string;
|
|
29
|
-
/**
|
|
30
|
-
* Platform type from environment (optional - auto-detected from VITE_PLATFORM_TYPE)
|
|
31
|
-
* @default getEnv('PLATFORM_TYPE')
|
|
32
|
-
*/
|
|
33
|
-
platformType?: string;
|
|
34
|
-
/**
|
|
35
|
-
* Platform name from environment (optional - auto-detected from VITE_PLATFORM)
|
|
36
|
-
* @default getEnv('PLATFORM')
|
|
37
|
-
*/
|
|
38
|
-
platform?: string;
|
|
39
|
-
/** Loading fallback component shown during lazy loading */
|
|
40
|
-
loader?: React.ReactNode;
|
|
41
|
-
/**
|
|
42
|
-
* Public routes to render inside Routes component.
|
|
43
|
-
* Can be a function that returns routes (recommended) or pre-created ReactNode.
|
|
44
|
-
* Using a function ensures routes are created inside Router context.
|
|
45
|
-
*/
|
|
46
|
-
publicRoutes?: React.ReactNode | (() => React.ReactNode);
|
|
47
|
-
/** Optional className for the wrapper div */
|
|
48
|
-
className?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** @deprecated Use DashRouterComponentProps instead */
|
|
52
|
-
export type RouterComponentProps = DashRouterComponentProps;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Determine if HashRouter should be used based on environment
|
|
56
|
-
*/
|
|
57
|
-
export const shouldUseHashRouter = (platformType?: string, platform?: string): boolean => {
|
|
58
|
-
return platformType === 'desktop' || platform === 'electron';
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Inner component that renders routes inside Router context
|
|
63
|
-
* This ensures route components with hooks are created INSIDE the Router
|
|
64
|
-
*/
|
|
65
|
-
const RouterContent: React.FC<{
|
|
66
|
-
publicRoutes?: React.ReactNode | (() => React.ReactNode);
|
|
67
|
-
loader?: React.ReactNode;
|
|
68
|
-
className: string;
|
|
69
|
-
children?: React.ReactNode;
|
|
70
|
-
}> = ({ publicRoutes, loader, className, children }) => {
|
|
71
|
-
if (!publicRoutes) {
|
|
72
|
-
// Legacy: direct children
|
|
73
|
-
return <>{children}</>;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Resolve routes - call function if it's a function, otherwise use directly
|
|
77
|
-
// This is called INSIDE the Router context, so useNavigate etc. will work
|
|
78
|
-
let routes: React.ReactNode;
|
|
79
|
-
if (typeof publicRoutes === 'function') {
|
|
80
|
-
routes = (publicRoutes as () => React.ReactNode)();
|
|
81
|
-
} else {
|
|
82
|
-
routes = publicRoutes;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<div className={className}>
|
|
87
|
-
<Suspense fallback={loader || <div>Loading...</div>}>
|
|
88
|
-
<Routes>
|
|
89
|
-
{routes}
|
|
90
|
-
</Routes>
|
|
91
|
-
</Suspense>
|
|
92
|
-
</div>
|
|
93
|
-
);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* DashRouterComponent - Smart router that chooses the appropriate router type
|
|
98
|
-
*
|
|
99
|
-
* Automatically detects platform environment when props are not provided:
|
|
100
|
-
* - platformType: from VITE_PLATFORM_TYPE env var
|
|
101
|
-
* - platform: from VITE_PLATFORM env var
|
|
102
|
-
* - basename: from VITE_BASE_PATH env var (defaults to '/')
|
|
103
|
-
*/
|
|
104
|
-
export const DashRouterComponent: React.FC<DashRouterComponentProps> = ({
|
|
105
|
-
children,
|
|
106
|
-
forceType,
|
|
107
|
-
basename,
|
|
108
|
-
platformType,
|
|
109
|
-
platform,
|
|
110
|
-
loader,
|
|
111
|
-
publicRoutes,
|
|
112
|
-
className = 'dash-public-app',
|
|
113
|
-
}) => {
|
|
114
|
-
// Get environment values internally if not provided as props
|
|
115
|
-
const effectivePlatformType = platformType ?? getEnv('PLATFORM_TYPE');
|
|
116
|
-
const effectivePlatform = platform ?? getEnv('PLATFORM');
|
|
117
|
-
const effectiveBasename = basename ?? getEnv('BASE_PATH') ?? '/';
|
|
118
|
-
|
|
119
|
-
// Render content using RouterContent component (which runs inside Router context)
|
|
120
|
-
const content = (
|
|
121
|
-
<RouterContent
|
|
122
|
-
publicRoutes={publicRoutes}
|
|
123
|
-
loader={loader}
|
|
124
|
-
className={className}
|
|
125
|
-
>
|
|
126
|
-
{children}
|
|
127
|
-
</RouterContent>
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// If forceType is specified, use that
|
|
131
|
-
if (forceType === 'hash') {
|
|
132
|
-
return <HashRouter basename="">{content}</HashRouter>;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (forceType === 'browser') {
|
|
136
|
-
return <BrowserRouter basename={effectiveBasename}>{content}</BrowserRouter>;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Auto-detect based on platform
|
|
140
|
-
if (shouldUseHashRouter(effectivePlatformType, effectivePlatform)) {
|
|
141
|
-
return <HashRouter basename="">{content}</HashRouter>;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return <BrowserRouter basename={effectiveBasename}>{content}</BrowserRouter>;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Create a DashRouterComponent with pre-configured environment settings
|
|
149
|
-
*/
|
|
150
|
-
export const createRouterComponent = (envVars: {
|
|
151
|
-
PLATFORM_TYPE?: string;
|
|
152
|
-
PLATFORM?: string;
|
|
153
|
-
}): React.FC<{ children: React.ReactNode }> => {
|
|
154
|
-
const RouterWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
|
155
|
-
<DashRouterComponent
|
|
156
|
-
platformType={envVars.PLATFORM_TYPE}
|
|
157
|
-
platform={envVars.PLATFORM}
|
|
158
|
-
>
|
|
159
|
-
{children}
|
|
160
|
-
</DashRouterComponent>
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
return RouterWrapper;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
/** @deprecated Use DashRouterComponent instead */
|
|
167
|
-
export const RouterComponent = DashRouterComponent;
|
|
168
|
-
|
|
169
|
-
export default DashRouterComponent;
|
package/src/components/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Components Index
|
|
3
|
-
*
|
|
4
|
-
* Export all utility components from dash-utils
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export { MinimalLayout, type MinimalLayoutProps } from './MinimalLayout';
|
|
8
|
-
export {
|
|
9
|
-
DashRouterComponent,
|
|
10
|
-
RouterComponent, // Backward compatibility alias
|
|
11
|
-
shouldUseHashRouter,
|
|
12
|
-
createRouterComponent,
|
|
13
|
-
type DashRouterComponentProps,
|
|
14
|
-
type RouterComponentProps // Backward compatibility alias
|
|
15
|
-
} from './RouterComponent';
|
|
16
|
-
|
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
import { useState, useRef, useEffect, createContext, useContext } from "react";
|
|
2
|
-
import { DashIPCServiceType } from "../utils/dashIPCService";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* IPCMessageBrokerContext
|
|
6
|
-
*
|
|
7
|
-
* This context provides access to messages from the Electron main process,
|
|
8
|
-
* including log messages and subprocess outputs.
|
|
9
|
-
*
|
|
10
|
-
* ## Usage
|
|
11
|
-
*
|
|
12
|
-
* 1. Wrap your component tree with the IPCMessageBrokerProvider:
|
|
13
|
-
* ```tsx
|
|
14
|
-
* // In your app root or where you need IPC message access
|
|
15
|
-
* <IPCMessageBrokerProvider>
|
|
16
|
-
* <YourComponent />
|
|
17
|
-
* </IPCMessageBrokerProvider>
|
|
18
|
-
* ```
|
|
19
|
-
*
|
|
20
|
-
* 2. Use the hook to access messages in any child component:
|
|
21
|
-
* ```tsx
|
|
22
|
-
* import { useIPCMessageBroker } from 'dash-utils';
|
|
23
|
-
*
|
|
24
|
-
* const MyComponent = () => {
|
|
25
|
-
* const { logMessages, subprocessOutputs, lastMessage, idleStatus } = useIPCMessageBroker();
|
|
26
|
-
*
|
|
27
|
-
* return (
|
|
28
|
-
* <div>
|
|
29
|
-
* <h3>Last Message: {lastMessage}</h3>
|
|
30
|
-
*
|
|
31
|
-
* <h3>Log Messages:</h3>
|
|
32
|
-
* <ul>
|
|
33
|
-
* {logMessages.map((log, index) => (
|
|
34
|
-
* <li key={index} className={`log-${log.level}`}>
|
|
35
|
-
* [{log.timestamp}] {log.level}: {log.message}
|
|
36
|
-
* </li>
|
|
37
|
-
* ))}
|
|
38
|
-
* </ul>
|
|
39
|
-
*
|
|
40
|
-
* <h3>Subprocess Output:</h3>
|
|
41
|
-
* <ul>
|
|
42
|
-
* {subprocessOutputs.map((output, index) => (
|
|
43
|
-
* <li key={index} className={`output-${output.type}`}>
|
|
44
|
-
* [{output.timestamp}] {output.type}: {output.message}
|
|
45
|
-
* </li>
|
|
46
|
-
* ))}
|
|
47
|
-
* </ul>
|
|
48
|
-
* </div>
|
|
49
|
-
* );
|
|
50
|
-
* };
|
|
51
|
-
* ```
|
|
52
|
-
*
|
|
53
|
-
* 3. The context also manages idle status and automatic resuscitation of
|
|
54
|
-
* background processes if they become idle for too long.
|
|
55
|
-
*
|
|
56
|
-
* Note: This context works in both Electron and non-Electron environments.
|
|
57
|
-
* In non-Electron environments, it gracefully degrades and still provides the context API.
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
export interface LogMessage {
|
|
61
|
-
level: string;
|
|
62
|
-
message: string;
|
|
63
|
-
timestamp: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface SubprocessOutput {
|
|
67
|
-
type: string;
|
|
68
|
-
message: string;
|
|
69
|
-
timestamp: string;
|
|
70
|
-
pid?: number;
|
|
71
|
-
exitCode?: number;
|
|
72
|
-
error?: string;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export interface IPCMessageBrokerContextType {
|
|
76
|
-
lastMessage: string | undefined;
|
|
77
|
-
idleStatus: boolean;
|
|
78
|
-
logMessages: LogMessage[];
|
|
79
|
-
subprocessOutputs: SubprocessOutput[];
|
|
80
|
-
isElectronAvailable: boolean;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface IPCMessageBrokerConfig {
|
|
84
|
-
/** Time in milliseconds before idle status is set (default: 180000 = 3 minutes) */
|
|
85
|
-
idleTime?: number;
|
|
86
|
-
/** Time in milliseconds before resuscitation is triggered (default: 360000 = 6 minutes) */
|
|
87
|
-
resuscitationTime?: number;
|
|
88
|
-
/** Maximum number of log messages to keep in memory (default: 100) */
|
|
89
|
-
maxLogMessages?: number;
|
|
90
|
-
/** Maximum number of subprocess outputs to keep in memory (default: 100) */
|
|
91
|
-
maxSubprocessOutputs?: number;
|
|
92
|
-
/** Maximum initialization attempts (default: 5) */
|
|
93
|
-
maxInitAttempts?: number;
|
|
94
|
-
/** Delay between initialization attempts in ms (default: 500) */
|
|
95
|
-
initRetryDelay?: number;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const DEFAULT_CONFIG: Required<IPCMessageBrokerConfig> = {
|
|
99
|
-
idleTime: 180 * 1000,
|
|
100
|
-
resuscitationTime: 360 * 1000,
|
|
101
|
-
maxLogMessages: 100,
|
|
102
|
-
maxSubprocessOutputs: 100,
|
|
103
|
-
maxInitAttempts: 5,
|
|
104
|
-
initRetryDelay: 500,
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
export const IPCMessageBrokerContext = createContext<IPCMessageBrokerContextType>({
|
|
108
|
-
lastMessage: undefined,
|
|
109
|
-
idleStatus: false,
|
|
110
|
-
logMessages: [],
|
|
111
|
-
subprocessOutputs: [],
|
|
112
|
-
isElectronAvailable: false
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
/** Custom hook for accessing the IPC Message Broker context */
|
|
116
|
-
export const useIPCMessageBroker = () => useContext(IPCMessageBrokerContext);
|
|
117
|
-
// Global type declaration for DashIPCService
|
|
118
|
-
declare global {
|
|
119
|
-
interface Window {
|
|
120
|
-
DashIPCService?: DashIPCServiceType;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
interface IPCMessageBrokerProviderProps {
|
|
125
|
-
children: React.ReactNode;
|
|
126
|
-
config?: IPCMessageBrokerConfig;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export const IPCMessageBrokerProvider = ({
|
|
130
|
-
children,
|
|
131
|
-
config: userConfig
|
|
132
|
-
}: IPCMessageBrokerProviderProps) => {
|
|
133
|
-
const config = { ...DEFAULT_CONFIG, ...userConfig };
|
|
134
|
-
|
|
135
|
-
const [lastMessage, setLastMessage] = useState<string>();
|
|
136
|
-
const [idleStatus, setIdleStatus] = useState(false);
|
|
137
|
-
const [idleTimeout, setIdleTimeout] = useState<ReturnType<typeof setTimeout> | null>(null);
|
|
138
|
-
const [logMessages, setLogMessages] = useState<LogMessage[]>([]);
|
|
139
|
-
const [subprocessOutputs, setSubprocessOutputs] = useState<SubprocessOutput[]>([]);
|
|
140
|
-
const [isElectronAvailable, setIsElectronAvailable] = useState<boolean>(false);
|
|
141
|
-
const initAttempts = useRef(0);
|
|
142
|
-
|
|
143
|
-
const resetIdleStatus = () => {
|
|
144
|
-
setIdleStatus(false);
|
|
145
|
-
if (idleTimeout) {
|
|
146
|
-
clearTimeout(idleTimeout);
|
|
147
|
-
}
|
|
148
|
-
setIdleTimeout(setTimeout(() => {
|
|
149
|
-
setIdleStatus(true);
|
|
150
|
-
}, config.idleTime));
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
// Safely call a method if it exists
|
|
154
|
-
const safeMethodCall = <T extends (...args: any[]) => any>(
|
|
155
|
-
obj: any,
|
|
156
|
-
methodName: string,
|
|
157
|
-
fallback: T | null = null
|
|
158
|
-
): T | null => {
|
|
159
|
-
if (obj && typeof obj[methodName] === 'function') {
|
|
160
|
-
return obj[methodName].bind(obj);
|
|
161
|
-
}
|
|
162
|
-
console.warn(`Method ${methodName} is not available on DashIPCService`);
|
|
163
|
-
return fallback;
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
// Setup IPC listeners with retry mechanism
|
|
167
|
-
const setupIPCListeners = () => {
|
|
168
|
-
if (typeof window === 'undefined') return false;
|
|
169
|
-
|
|
170
|
-
// Check if DashIPCService is available
|
|
171
|
-
if (!window.DashIPCService) {
|
|
172
|
-
console.log(`DashIPCService not available yet. Attempt ${initAttempts.current + 1}/${config.maxInitAttempts}`);
|
|
173
|
-
initAttempts.current += 1;
|
|
174
|
-
|
|
175
|
-
if (initAttempts.current < config.maxInitAttempts) {
|
|
176
|
-
setTimeout(setupIPCListeners, config.initRetryDelay);
|
|
177
|
-
} else {
|
|
178
|
-
console.warn('Failed to initialize DashIPCService after multiple attempts');
|
|
179
|
-
}
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
console.log('DashIPCService is available, setting up listeners');
|
|
184
|
-
setIsElectronAvailable(true);
|
|
185
|
-
|
|
186
|
-
const handleMessage = (message: string) => {
|
|
187
|
-
setLastMessage(message);
|
|
188
|
-
resetIdleStatus();
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const handleLogMessage = (logData: LogMessage) => {
|
|
192
|
-
console.log('Log message received:', logData);
|
|
193
|
-
setLogMessages(prev => [...prev.slice(-(config.maxLogMessages - 1)), logData]);
|
|
194
|
-
resetIdleStatus();
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
const handleSubprocessOutput = (data: SubprocessOutput) => {
|
|
198
|
-
console.log('Subprocess output received:', data);
|
|
199
|
-
setSubprocessOutputs(prev => [...prev.slice(-(config.maxSubprocessOutputs - 1)), data]);
|
|
200
|
-
resetIdleStatus();
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// Safely set up event listeners
|
|
204
|
-
const onServiceMessage = safeMethodCall(window.DashIPCService, 'onServiceMessage');
|
|
205
|
-
const onElectronLog = safeMethodCall(window.DashIPCService, 'onElectronLog');
|
|
206
|
-
const onSubprocessOutput = safeMethodCall(window.DashIPCService, 'onSubprocessOutput');
|
|
207
|
-
const onStdout = safeMethodCall(window.DashIPCService, 'onStdout');
|
|
208
|
-
|
|
209
|
-
// Set up listeners if methods exist
|
|
210
|
-
if (onServiceMessage) {
|
|
211
|
-
onServiceMessage((data: any) => {
|
|
212
|
-
handleMessage(data);
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (onElectronLog) {
|
|
217
|
-
onElectronLog((data: LogMessage) => {
|
|
218
|
-
handleLogMessage(data);
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (onSubprocessOutput) {
|
|
223
|
-
onSubprocessOutput((data: SubprocessOutput) => {
|
|
224
|
-
handleSubprocessOutput(data);
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Fallback to stdout if onElectronLog is not available
|
|
229
|
-
if (!onElectronLog && onStdout) {
|
|
230
|
-
onStdout((message: string) => {
|
|
231
|
-
// Try to parse stdout messages as log entries
|
|
232
|
-
try {
|
|
233
|
-
if (message.includes('{') && message.includes('}')) {
|
|
234
|
-
const jsonStart = message.indexOf('{');
|
|
235
|
-
const jsonEnd = message.lastIndexOf('}') + 1;
|
|
236
|
-
const jsonStr = message.substring(jsonStart, jsonEnd);
|
|
237
|
-
const data = JSON.parse(jsonStr);
|
|
238
|
-
|
|
239
|
-
if (data.level && data.message) {
|
|
240
|
-
handleLogMessage({
|
|
241
|
-
level: data.level,
|
|
242
|
-
message: data.message,
|
|
243
|
-
timestamp: data.timestamp || new Date().toISOString()
|
|
244
|
-
});
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
} catch {
|
|
249
|
-
// Not JSON or not a log structure, use as plain message
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Fallback: treat as a generic log message
|
|
253
|
-
handleLogMessage({
|
|
254
|
-
level: 'info',
|
|
255
|
-
message: message,
|
|
256
|
-
timestamp: new Date().toISOString()
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Start initial idle timer
|
|
262
|
-
resetIdleStatus();
|
|
263
|
-
return true;
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
useEffect(() => {
|
|
267
|
-
setupIPCListeners();
|
|
268
|
-
|
|
269
|
-
return () => {
|
|
270
|
-
if (idleTimeout) {
|
|
271
|
-
clearTimeout(idleTimeout);
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
}, []);
|
|
275
|
-
|
|
276
|
-
useEffect(() => {
|
|
277
|
-
if (idleStatus && window.DashIPCService && typeof window.DashIPCService.action === 'function') {
|
|
278
|
-
const resuscitateTimeout = setTimeout(() => {
|
|
279
|
-
window.DashIPCService!.action!('resuscitate', null);
|
|
280
|
-
}, config.resuscitationTime - config.idleTime);
|
|
281
|
-
|
|
282
|
-
return () => {
|
|
283
|
-
clearTimeout(resuscitateTimeout);
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
}, [idleStatus, config.resuscitationTime, config.idleTime]);
|
|
287
|
-
|
|
288
|
-
const contextValue: IPCMessageBrokerContextType = {
|
|
289
|
-
lastMessage,
|
|
290
|
-
idleStatus,
|
|
291
|
-
logMessages,
|
|
292
|
-
subprocessOutputs,
|
|
293
|
-
isElectronAvailable
|
|
294
|
-
};
|
|
295
|
-
|
|
296
|
-
return (
|
|
297
|
-
<IPCMessageBrokerContext.Provider value={contextValue}>
|
|
298
|
-
{children}
|
|
299
|
-
</IPCMessageBrokerContext.Provider>
|
|
300
|
-
);
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
export default IPCMessageBrokerProvider;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Hook to handle Capacitor app state changes
|
|
5
|
-
* Triggers a refresh callback when the app resumes from background
|
|
6
|
-
*
|
|
7
|
-
* @param onResume - Callback function to execute when app resumes
|
|
8
|
-
*/
|
|
9
|
-
export const useCapacitorAppStateRefresh = (onResume?: () => void) => {
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
const CapacitorApp = (window as any)?.Capacitor?.Plugins?.App;
|
|
12
|
-
console.log('[useCapacitorAppStateRefresh] Checking for Capacitor App plugin:', !!CapacitorApp);
|
|
13
|
-
|
|
14
|
-
if (!CapacitorApp) return;
|
|
15
|
-
|
|
16
|
-
const handler = (state: { isActive: boolean }) => {
|
|
17
|
-
console.log('[useCapacitorAppStateRefresh] App state changed:', state);
|
|
18
|
-
if (state.isActive) {
|
|
19
|
-
console.log('[useCapacitorAppStateRefresh] App is resuming, triggering callback');
|
|
20
|
-
onResume?.();
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
CapacitorApp.addListener('appStateChange', handler);
|
|
25
|
-
console.log('[useCapacitorAppStateRefresh] Added appStateChange listener');
|
|
26
|
-
|
|
27
|
-
return () => {
|
|
28
|
-
CapacitorApp.removeAllListeners?.();
|
|
29
|
-
console.log('[useCapacitorAppStateRefresh] Removed all Capacitor App listeners');
|
|
30
|
-
};
|
|
31
|
-
}, [onResume]);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export default useCapacitorAppStateRefresh;
|