mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
LiveFile: fix window refocus sync
This commit is contained in:
@@ -146,7 +146,7 @@ export const useLiveFileStore = create<LiveFileState & LiveFileActions>()(persis
|
||||
[fileId]: {
|
||||
...file,
|
||||
isLoading: false,
|
||||
error: `Error loading File: ${error?.message || typeof error === 'string' ? error : 'Unknown error'}`,
|
||||
error: `Error reading: ${error?.message || typeof error === 'string' ? error : 'Unknown error'}`,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { fileOpen } from 'browser-fs-access';
|
||||
import { focusManager } from '@tanstack/react-query';
|
||||
import { cleanupEfficiency, makeDiff } from '@sanity/diff-match-patch';
|
||||
|
||||
import { Alert, Box, Button, ColorPaletteProp, IconButton } from '@mui/joy';
|
||||
@@ -8,6 +7,7 @@ import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
||||
import WarningRoundedIcon from '@mui/icons-material/WarningRounded';
|
||||
|
||||
import { TooltipOutlined } from '~/common/components/TooltipOutlined';
|
||||
import { WindowFocusObserver } from '~/common/util/windowUtils';
|
||||
|
||||
import type { LiveFileId } from './liveFile.types';
|
||||
import { LiveFileChooseIcon, LiveFileIcon, LiveFileReloadIcon, LiveFileSaveIcon } from './liveFile.icons';
|
||||
@@ -217,16 +217,16 @@ export function useLiveFileComparison(
|
||||
const liveFileActionBox = React.useMemo(() => {
|
||||
if (!status && !fileHasContent) return null;
|
||||
|
||||
const statusColor: ColorPaletteProp = status?.mtype === 'error' ? 'danger'
|
||||
: status?.mtype === 'success' ? 'success'
|
||||
: status?.mtype === 'changes' ? 'neutral'
|
||||
: 'neutral';
|
||||
const statusColor: ColorPaletteProp =
|
||||
status?.mtype === 'error' ? 'warning'
|
||||
: status?.mtype === 'success' ? 'success'
|
||||
: status?.mtype === 'changes' ? 'neutral'
|
||||
: 'neutral';
|
||||
|
||||
return (
|
||||
<Alert
|
||||
variant='plain'
|
||||
color={statusColor}
|
||||
startDecorator={status?.mtype === 'error' ? <WarningRoundedIcon /> : undefined}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexFlow: 'row wrap',
|
||||
@@ -237,11 +237,17 @@ export function useLiveFileComparison(
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', gap: 0.5, alignItems: 'center' }}>
|
||||
|
||||
{/* Pair Button */}
|
||||
{isPairingValid && (
|
||||
<IconButton size='sm' onClick={handleSyncButtonClicked}>
|
||||
<LiveFileIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
|
||||
{/* Alert Decorator (startDecorator will have it messy) */}
|
||||
{status?.mtype === 'error' && <WarningRoundedIcon sx={{ mr: 1 }} />}
|
||||
|
||||
{' '}<span>{status?.message}</span>
|
||||
</Box>
|
||||
|
||||
@@ -299,7 +305,7 @@ export function useLiveFileComparison(
|
||||
// Auto-click on 'refresh' on window focus
|
||||
|
||||
React.useEffect(() => {
|
||||
return focusManager.subscribe(async (focused) => {
|
||||
return WindowFocusObserver.getInstance().subscribe(async (focused) => {
|
||||
if (focused && shallUpdateOnRefocus)
|
||||
await handleUpdateFileContent();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { isBrowser } from './pwaUtils';
|
||||
|
||||
export class WindowFocusObserver {
|
||||
private static instance: WindowFocusObserver | null = null;
|
||||
private listeners: Set<(isWindowFocused: boolean) => void> = new Set();
|
||||
private isWindowFocused: boolean = true;
|
||||
private refCount: number = 0;
|
||||
|
||||
// singleton
|
||||
|
||||
public static getInstance(): WindowFocusObserver {
|
||||
if (!WindowFocusObserver.instance)
|
||||
WindowFocusObserver.instance = new WindowFocusObserver();
|
||||
return WindowFocusObserver.instance;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
if (!isBrowser) return;
|
||||
this.isWindowFocused = document.hasFocus();
|
||||
window.addEventListener('focus', this._handleWindowFocus);
|
||||
window.addEventListener('blur', this._handleWindowBlur);
|
||||
}
|
||||
|
||||
// clients
|
||||
|
||||
get windowFocusState(): boolean {
|
||||
return this.isWindowFocused;
|
||||
}
|
||||
|
||||
public subscribe(listener: (isWindowFocused: boolean) => void): () => void {
|
||||
this.listeners.add(listener);
|
||||
this.refCount++;
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
this.refCount--;
|
||||
if (this.refCount === 0) {
|
||||
this._cleanup();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// private methods
|
||||
|
||||
private _cleanup(): void {
|
||||
if (!isBrowser) return;
|
||||
window.removeEventListener('focus', this._handleWindowFocus);
|
||||
window.removeEventListener('blur', this._handleWindowBlur);
|
||||
this.listeners.clear();
|
||||
WindowFocusObserver.instance = null;
|
||||
}
|
||||
|
||||
private _handleWindowFocus = (): void => {
|
||||
this._updateWindowFocusState(true);
|
||||
};
|
||||
|
||||
private _handleWindowBlur = (): void => {
|
||||
this._updateWindowFocusState(false);
|
||||
};
|
||||
|
||||
private _updateWindowFocusState(newState: boolean): void {
|
||||
if (this.isWindowFocused !== newState) {
|
||||
this.isWindowFocused = newState;
|
||||
this.listeners.forEach(listener => listener(this.isWindowFocused));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// // React to window visibility changes.
|
||||
// export function useWindowFocus(): boolean {
|
||||
// const [isWindowFocused, setIsWindowFocused] = React.useState<boolean>(() =>
|
||||
// WindowFocusObserver.getInstance().windowFocusState,
|
||||
// );
|
||||
// React.useEffect(() => WindowFocusObserver.getInstance().subscribe(setIsWindowFocused), []);
|
||||
// return isWindowFocused;
|
||||
// }
|
||||
Reference in New Issue
Block a user