From ab17625ed8cd2602ececdb55afd8ce14ad433538 Mon Sep 17 00:00:00 2001 From: Henry Heino <46334387+personalizedrefrigerator@users.noreply.github.com> Date: Fri, 6 Jun 2025 02:30:17 -0700 Subject: [PATCH] Desktop: Fixes #12214: Change how the main content size is determined (#12388) --- packages/app-desktop/app.reducer.test.ts | 4 +-- packages/app-desktop/app.reducer.ts | 11 +----- packages/app-desktop/app.ts | 5 +-- packages/app-desktop/bridge.ts | 7 ---- .../commands/exportDeletionLog.test.ts | 2 +- packages/app-desktop/gui/Navigator.tsx | 34 ++++++++++++++----- packages/app-desktop/gui/Root.tsx | 34 +------------------ .../PerFolderSortOrderService.test.ts | 2 +- 8 files changed, 33 insertions(+), 66 deletions(-) diff --git a/packages/app-desktop/app.reducer.test.ts b/packages/app-desktop/app.reducer.test.ts index 8f6f87e8bc..4e6626a9e8 100644 --- a/packages/app-desktop/app.reducer.test.ts +++ b/packages/app-desktop/app.reducer.test.ts @@ -4,7 +4,7 @@ import appReducer, { createAppDefaultState } from './app.reducer'; describe('app.reducer', () => { it('should handle DIALOG_OPEN', async () => { - const state: AppState = createAppDefaultState({}, {}); + const state: AppState = createAppDefaultState({}); let newState = appReducer(state, { type: 'DIALOG_OPEN', @@ -49,7 +49,7 @@ describe('app.reducer', () => { it('showing a dialog in one window should hide dialogs with the same ID in background windows', () => { const state: AppState = { - ...createAppDefaultState({}, {}), + ...createAppDefaultState({}), backgroundWindows: { testWindow: { ...createAppDefaultWindowState(), diff --git a/packages/app-desktop/app.reducer.ts b/packages/app-desktop/app.reducer.ts index 04cf5ac11e..1b621619bc 100644 --- a/packages/app-desktop/app.reducer.ts +++ b/packages/app-desktop/app.reducer.ts @@ -54,8 +54,6 @@ export interface AppState extends State, AppWindowState { route: AppStateRoute; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied navHistory: any[]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - windowContentSize: any; watchedNoteFiles: string[]; lastEditorScrollPercents: EditorScrollPercents; focusedField: string; @@ -81,7 +79,7 @@ export const createAppDefaultWindowState = (): AppWindowState => { }; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied -export function createAppDefaultState(windowContentSize: any, resourceEditWatcherDefaultState: any): AppState { +export function createAppDefaultState(resourceEditWatcherDefaultState: any): AppState { return { ...defaultState, ...createAppDefaultWindowState(), @@ -91,7 +89,6 @@ export function createAppDefaultState(windowContentSize: any, resourceEditWatche props: {}, }, navHistory: [], - windowContentSize, // bridge().windowContentSize(), watchedNoteFiles: [], lastEditorScrollPercents: {}, visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs. @@ -166,12 +163,6 @@ export default function(state: AppState, action: any) { } break; - case 'WINDOW_CONTENT_SIZE_SET': - - newState = { ...state }; - newState.windowContentSize = action.size; - break; - case 'NOTE_VISIBLE_PANES_TOGGLE': { diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index d2c541f823..17efcf03d6 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -65,10 +65,7 @@ const pluginClasses = [ require('./plugins/GotoAnything').default, ]; -const appDefaultState = createAppDefaultState( - bridge().windowContentSize(), - resourceEditWatcherDefaultState, -); +const appDefaultState = createAppDefaultState(resourceEditWatcherDefaultState); class Application extends BaseApplication { diff --git a/packages/app-desktop/bridge.ts b/packages/app-desktop/bridge.ts index f23ecd91fa..bfac73f59b 100644 --- a/packages/app-desktop/bridge.ts +++ b/packages/app-desktop/bridge.ts @@ -313,13 +313,6 @@ export class Bridge { return new BrowserWindow(options); } - // Note: This provides the size of the main window. Prefer CSS where possible. - public windowContentSize() { - if (!this.mainWindow()) return { width: 0, height: 0 }; - const s = this.mainWindow().getContentSize(); - return { width: s[0], height: s[1] }; - } - public windowSetSize(width: number, height: number) { if (!this.mainWindow()) return; return this.mainWindow().setSize(width, height); diff --git a/packages/app-desktop/commands/exportDeletionLog.test.ts b/packages/app-desktop/commands/exportDeletionLog.test.ts index fcae872f8b..c7c450de49 100644 --- a/packages/app-desktop/commands/exportDeletionLog.test.ts +++ b/packages/app-desktop/commands/exportDeletionLog.test.ts @@ -38,7 +38,7 @@ describe('exportDeletionLog', () => { let state: AppState = undefined; beforeAll(() => { - state = createAppDefaultState({}, {}); + state = createAppDefaultState({}); jest.useFakeTimers(); jest.setSystemTime(new Date('2024-09-18T12:00:00Z').getTime()); }); diff --git a/packages/app-desktop/gui/Navigator.tsx b/packages/app-desktop/gui/Navigator.tsx index 4aa3acf9d7..ddaff17d85 100644 --- a/packages/app-desktop/gui/Navigator.tsx +++ b/packages/app-desktop/gui/Navigator.tsx @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import Setting from '@joplin/lib/models/Setting'; import { AppState, AppStateRoute } from '../app.reducer'; import bridge from '../services/bridge'; -import { useContext, useEffect, useMemo, useRef } from 'react'; +import { useContext, useEffect, useMemo, useRef, useState } from 'react'; import { WindowIdContext } from './NewWindowOrIFrame'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of code from before rule was applied @@ -55,26 +55,44 @@ const useWindowRefocusManager = (route: AppStateRoute) => { }, [routeName, windowId]); }; +const useContainerSize = (container: HTMLElement|null) => { + const [size, setSize] = useState({ width: container?.clientWidth, height: container?.clientHeight }); + + useEffect(() => { + if (!container) return () => {}; + + const observer = new ResizeObserver(() => { + setSize({ + width: container.clientWidth, + height: container.clientHeight, + }); + }); + observer.observe(container); + return () => { + observer.disconnect(); + }; + }, [container]); + + return size; +}; + const NavigatorComponent: React.FC = props => { const route = props.route; const screenInfo = props.screens[route?.routeName]; + const [container, setContainer] = useState(null); useWindowTitleManager(screenInfo); useWindowRefocusManager(route); + const size = useContainerSize(container); if (!route) throw new Error('Route must not be null'); const screenProps = route.props ? route.props : {}; const Screen = screenInfo.screen; - const screenStyle = { - width: props.style.width, - height: props.style.height, - }; - return ( -
- +
+
); }; diff --git a/packages/app-desktop/gui/Root.tsx b/packages/app-desktop/gui/Root.tsx index 10ba8c2a45..d35a571b1a 100644 --- a/packages/app-desktop/gui/Root.tsx +++ b/packages/app-desktop/gui/Root.tsx @@ -8,13 +8,11 @@ import OneDriveLoginScreen from './OneDriveLoginScreen'; import DropboxLoginScreen from './DropboxLoginScreen'; import ErrorBoundary from './ErrorBoundary'; import { themeStyle } from '@joplin/lib/theme'; -import { Size } from './ResizableLayout/utils/types'; import MenuBar from './MenuBar'; import { _ } from '@joplin/lib/locale'; const { createRoot } = require('react-dom/client'); const { connect, Provider } = require('react-redux'); import Setting from '@joplin/lib/models/Setting'; -import shim from '@joplin/lib/shim'; import ClipperServer from '@joplin/lib/ClipperServer'; import DialogTitle from './DialogTitle'; import DialogButtonRow, { ButtonSpec, ClickEvent, ClickEventHandler } from './DialogButtonRow'; @@ -28,7 +26,6 @@ import JoplinCloudLoginScreen from './JoplinCloudLoginScreen'; import InteropService from '@joplin/lib/services/interop/InteropService'; import WindowCommandsAndDialogs from './WindowCommandsAndDialogs/WindowCommandsAndDialogs'; import { defaultWindowId, stateUtils, WindowState } from '@joplin/lib/reducer'; -import bridge from '../services/bridge'; import EditorWindow from './NoteEditor/EditorWindow'; import SsoLoginScreen from './SsoLoginScreen/SsoLoginScreen'; import SamlShared from '@joplin/lib/components/shared/SamlShared'; @@ -41,7 +38,6 @@ interface Props { profileConfigCurrentProfileId: string; // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied dispatch: Function; - size: Size; zoomFactor: number; needApiAuth: boolean; dialogs: AppStateDialog[]; @@ -62,31 +58,9 @@ const GlobalStyle = createGlobalStyle` } `; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied -let wcsTimeoutId_: any = null; +const navigatorStyle = { width: '100vw', height: '100vh' }; async function initialize() { - bridge().activeWindow().on('resize', () => { - if (wcsTimeoutId_) shim.clearTimeout(wcsTimeoutId_); - - wcsTimeoutId_ = shim.setTimeout(() => { - store.dispatch({ - type: 'WINDOW_CONTENT_SIZE_SET', - size: bridge().windowContentSize(), - }); - wcsTimeoutId_ = null; - }, 10); - }); - - // Need to dispatch this to make sure the components are - // displayed at the right size. The windowContentSize is - // also set in the store default state, but at that point - // the window might not be at its final size. - store.dispatch({ - type: 'WINDOW_CONTENT_SIZE_SET', - size: bridge().windowContentSize(), - }); - store.dispatch({ type: 'EDITOR_CODE_VIEW_CHANGE', value: Setting.value('editor.codeView'), @@ -180,11 +154,6 @@ class RootComponent extends React.Component { } public render() { - const navigatorStyle = { - width: this.props.size.width / this.props.zoomFactor, - height: this.props.size.height / this.props.zoomFactor, - }; - const theme = themeStyle(this.props.themeId); const screens = { @@ -219,7 +188,6 @@ class RootComponent extends React.Component { const mapStateToProps = (state: AppState) => { return { - size: state.windowContentSize, zoomFactor: state.settings.windowContentZoomFactor / 100, appState: state.appState, themeId: state.settings.theme, diff --git a/packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.ts b/packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.ts index d0718f06c4..00bee4d4f4 100644 --- a/packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.ts +++ b/packages/app-desktop/services/sortOrder/PerFolderSortOrderService.test.ts @@ -39,7 +39,7 @@ describe('PerFolderSortOrderService', () => { beforeEach(() => { PerFolderSortOrderService.initialize(); Setting.setValue('notes.perFolderSortOrderEnabled', true); - updateAppState(createAppDefaultState({}, {})); + updateAppState(createAppDefaultState({})); switchToFolder(folderId1); }); afterEach(() => {