1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-10 22:11:50 +02:00

Desktop: Fixes #12214: Change how the main content size is determined (#12388)

This commit is contained in:
Henry Heino
2025-06-06 02:30:17 -07:00
committed by GitHub
parent 8e8ab3bd80
commit ab17625ed8
8 changed files with 33 additions and 66 deletions

View File

@@ -4,7 +4,7 @@ import appReducer, { createAppDefaultState } from './app.reducer';
describe('app.reducer', () => { describe('app.reducer', () => {
it('should handle DIALOG_OPEN', async () => { it('should handle DIALOG_OPEN', async () => {
const state: AppState = createAppDefaultState({}, {}); const state: AppState = createAppDefaultState({});
let newState = appReducer(state, { let newState = appReducer(state, {
type: 'DIALOG_OPEN', 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', () => { it('showing a dialog in one window should hide dialogs with the same ID in background windows', () => {
const state: AppState = { const state: AppState = {
...createAppDefaultState({}, {}), ...createAppDefaultState({}),
backgroundWindows: { backgroundWindows: {
testWindow: { testWindow: {
...createAppDefaultWindowState(), ...createAppDefaultWindowState(),

View File

@@ -54,8 +54,6 @@ export interface AppState extends State, AppWindowState {
route: AppStateRoute; route: AppStateRoute;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
navHistory: any[]; navHistory: any[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
windowContentSize: any;
watchedNoteFiles: string[]; watchedNoteFiles: string[];
lastEditorScrollPercents: EditorScrollPercents; lastEditorScrollPercents: EditorScrollPercents;
focusedField: string; 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 // 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 { return {
...defaultState, ...defaultState,
...createAppDefaultWindowState(), ...createAppDefaultWindowState(),
@@ -91,7 +89,6 @@ export function createAppDefaultState(windowContentSize: any, resourceEditWatche
props: {}, props: {},
}, },
navHistory: [], navHistory: [],
windowContentSize, // bridge().windowContentSize(),
watchedNoteFiles: [], watchedNoteFiles: [],
lastEditorScrollPercents: {}, lastEditorScrollPercents: {},
visibleDialogs: {}, // empty object if no dialog is visible. Otherwise contains the list of visible dialogs. 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; break;
case 'WINDOW_CONTENT_SIZE_SET':
newState = { ...state };
newState.windowContentSize = action.size;
break;
case 'NOTE_VISIBLE_PANES_TOGGLE': case 'NOTE_VISIBLE_PANES_TOGGLE':
{ {

View File

@@ -65,10 +65,7 @@ const pluginClasses = [
require('./plugins/GotoAnything').default, require('./plugins/GotoAnything').default,
]; ];
const appDefaultState = createAppDefaultState( const appDefaultState = createAppDefaultState(resourceEditWatcherDefaultState);
bridge().windowContentSize(),
resourceEditWatcherDefaultState,
);
class Application extends BaseApplication { class Application extends BaseApplication {

View File

@@ -313,13 +313,6 @@ export class Bridge {
return new BrowserWindow(options); 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) { public windowSetSize(width: number, height: number) {
if (!this.mainWindow()) return; if (!this.mainWindow()) return;
return this.mainWindow().setSize(width, height); return this.mainWindow().setSize(width, height);

View File

@@ -38,7 +38,7 @@ describe('exportDeletionLog', () => {
let state: AppState = undefined; let state: AppState = undefined;
beforeAll(() => { beforeAll(() => {
state = createAppDefaultState({}, {}); state = createAppDefaultState({});
jest.useFakeTimers(); jest.useFakeTimers();
jest.setSystemTime(new Date('2024-09-18T12:00:00Z').getTime()); jest.setSystemTime(new Date('2024-09-18T12:00:00Z').getTime());
}); });

View File

@@ -3,7 +3,7 @@ import { connect } from 'react-redux';
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import { AppState, AppStateRoute } from '../app.reducer'; import { AppState, AppStateRoute } from '../app.reducer';
import bridge from '../services/bridge'; import bridge from '../services/bridge';
import { useContext, useEffect, useMemo, useRef } from 'react'; import { useContext, useEffect, useMemo, useRef, useState } from 'react';
import { WindowIdContext } from './NewWindowOrIFrame'; import { WindowIdContext } from './NewWindowOrIFrame';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of code from before rule was applied // 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]); }, [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> = props => { const NavigatorComponent: React.FC<Props> = props => {
const route = props.route; const route = props.route;
const screenInfo = props.screens[route?.routeName]; const screenInfo = props.screens[route?.routeName];
const [container, setContainer] = useState<HTMLElement|null>(null);
useWindowTitleManager(screenInfo); useWindowTitleManager(screenInfo);
useWindowRefocusManager(route); useWindowRefocusManager(route);
const size = useContainerSize(container);
if (!route) throw new Error('Route must not be null'); if (!route) throw new Error('Route must not be null');
const screenProps = route.props ? route.props : {}; const screenProps = route.props ? route.props : {};
const Screen = screenInfo.screen; const Screen = screenInfo.screen;
const screenStyle = {
width: props.style.width,
height: props.style.height,
};
return ( return (
<div style={props.style} className={props.className}> <div ref={setContainer} style={props.style} className={props.className}>
<Screen style={screenStyle} {...screenProps} /> <Screen style={size} {...screenProps} />
</div> </div>
); );
}; };

View File

@@ -8,13 +8,11 @@ import OneDriveLoginScreen from './OneDriveLoginScreen';
import DropboxLoginScreen from './DropboxLoginScreen'; import DropboxLoginScreen from './DropboxLoginScreen';
import ErrorBoundary from './ErrorBoundary'; import ErrorBoundary from './ErrorBoundary';
import { themeStyle } from '@joplin/lib/theme'; import { themeStyle } from '@joplin/lib/theme';
import { Size } from './ResizableLayout/utils/types';
import MenuBar from './MenuBar'; import MenuBar from './MenuBar';
import { _ } from '@joplin/lib/locale'; import { _ } from '@joplin/lib/locale';
const { createRoot } = require('react-dom/client'); const { createRoot } = require('react-dom/client');
const { connect, Provider } = require('react-redux'); const { connect, Provider } = require('react-redux');
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import shim from '@joplin/lib/shim';
import ClipperServer from '@joplin/lib/ClipperServer'; import ClipperServer from '@joplin/lib/ClipperServer';
import DialogTitle from './DialogTitle'; import DialogTitle from './DialogTitle';
import DialogButtonRow, { ButtonSpec, ClickEvent, ClickEventHandler } from './DialogButtonRow'; import DialogButtonRow, { ButtonSpec, ClickEvent, ClickEventHandler } from './DialogButtonRow';
@@ -28,7 +26,6 @@ import JoplinCloudLoginScreen from './JoplinCloudLoginScreen';
import InteropService from '@joplin/lib/services/interop/InteropService'; import InteropService from '@joplin/lib/services/interop/InteropService';
import WindowCommandsAndDialogs from './WindowCommandsAndDialogs/WindowCommandsAndDialogs'; import WindowCommandsAndDialogs from './WindowCommandsAndDialogs/WindowCommandsAndDialogs';
import { defaultWindowId, stateUtils, WindowState } from '@joplin/lib/reducer'; import { defaultWindowId, stateUtils, WindowState } from '@joplin/lib/reducer';
import bridge from '../services/bridge';
import EditorWindow from './NoteEditor/EditorWindow'; import EditorWindow from './NoteEditor/EditorWindow';
import SsoLoginScreen from './SsoLoginScreen/SsoLoginScreen'; import SsoLoginScreen from './SsoLoginScreen/SsoLoginScreen';
import SamlShared from '@joplin/lib/components/shared/SamlShared'; import SamlShared from '@joplin/lib/components/shared/SamlShared';
@@ -41,7 +38,6 @@ interface Props {
profileConfigCurrentProfileId: string; profileConfigCurrentProfileId: string;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied // eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
dispatch: Function; dispatch: Function;
size: Size;
zoomFactor: number; zoomFactor: number;
needApiAuth: boolean; needApiAuth: boolean;
dialogs: AppStateDialog[]; dialogs: AppStateDialog[];
@@ -62,31 +58,9 @@ const GlobalStyle = createGlobalStyle`
} }
`; `;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied const navigatorStyle = { width: '100vw', height: '100vh' };
let wcsTimeoutId_: any = null;
async function initialize() { 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({ store.dispatch({
type: 'EDITOR_CODE_VIEW_CHANGE', type: 'EDITOR_CODE_VIEW_CHANGE',
value: Setting.value('editor.codeView'), value: Setting.value('editor.codeView'),
@@ -180,11 +154,6 @@ class RootComponent extends React.Component<Props, any> {
} }
public render() { 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 theme = themeStyle(this.props.themeId);
const screens = { const screens = {
@@ -219,7 +188,6 @@ class RootComponent extends React.Component<Props, any> {
const mapStateToProps = (state: AppState) => { const mapStateToProps = (state: AppState) => {
return { return {
size: state.windowContentSize,
zoomFactor: state.settings.windowContentZoomFactor / 100, zoomFactor: state.settings.windowContentZoomFactor / 100,
appState: state.appState, appState: state.appState,
themeId: state.settings.theme, themeId: state.settings.theme,

View File

@@ -39,7 +39,7 @@ describe('PerFolderSortOrderService', () => {
beforeEach(() => { beforeEach(() => {
PerFolderSortOrderService.initialize(); PerFolderSortOrderService.initialize();
Setting.setValue('notes.perFolderSortOrderEnabled', true); Setting.setValue('notes.perFolderSortOrderEnabled', true);
updateAppState(createAppDefaultState({}, {})); updateAppState(createAppDefaultState({}));
switchToFolder(folderId1); switchToFolder(folderId1);
}); });
afterEach(() => { afterEach(() => {