2024-11-08 07:32:05 -08:00
|
|
|
import * as React from 'react';
|
2025-02-06 10:12:16 -08:00
|
|
|
import { connect } from 'react-redux';
|
2023-01-19 17:19:06 +00:00
|
|
|
import Setting from '@joplin/lib/models/Setting';
|
2024-11-08 07:32:05 -08:00
|
|
|
import { AppState, AppStateRoute } from '../app.reducer';
|
|
|
|
|
import bridge from '../services/bridge';
|
2025-06-06 02:30:17 -07:00
|
|
|
import { useContext, useEffect, useMemo, useRef, useState } from 'react';
|
2024-11-08 07:32:05 -08:00
|
|
|
import { WindowIdContext } from './NewWindowOrIFrame';
|
2017-11-06 18:35:04 +00:00
|
|
|
|
2025-02-06 10:12:16 -08:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Partial refactor of code from before rule was applied
|
|
|
|
|
type ScreenProps = any;
|
|
|
|
|
|
|
|
|
|
interface AppScreen {
|
|
|
|
|
screen: React.ComponentType<ScreenProps>;
|
|
|
|
|
title?: ()=> string;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-19 17:19:06 +00:00
|
|
|
interface Props {
|
2024-11-08 07:32:05 -08:00
|
|
|
route: AppStateRoute;
|
2025-02-06 10:12:16 -08:00
|
|
|
screens: Record<string, AppScreen>;
|
2024-11-08 07:32:05 -08:00
|
|
|
|
|
|
|
|
style: React.CSSProperties;
|
|
|
|
|
className?: string;
|
2023-01-19 17:19:06 +00:00
|
|
|
}
|
|
|
|
|
|
2025-02-06 10:12:16 -08:00
|
|
|
const useWindowTitleManager = (screenInfo: AppScreen) => {
|
|
|
|
|
const windowTitle = useMemo(() => {
|
|
|
|
|
const devMarker = Setting.value('env') === 'dev' ? ` (DEV - ${Setting.value('profileDir')})` : '';
|
|
|
|
|
const windowTitle = [`Joplin${devMarker}`];
|
|
|
|
|
if (screenInfo?.title) {
|
|
|
|
|
windowTitle.push(screenInfo.title());
|
|
|
|
|
}
|
|
|
|
|
return windowTitle.join(' - ');
|
|
|
|
|
}, [screenInfo]);
|
2024-11-08 07:32:05 -08:00
|
|
|
|
2025-02-06 10:12:16 -08:00
|
|
|
const windowId = useContext(WindowIdContext);
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
bridge().windowById(windowId)?.setTitle(windowTitle);
|
|
|
|
|
}, [windowTitle, windowId]);
|
|
|
|
|
};
|
2024-11-08 07:32:05 -08:00
|
|
|
|
2025-02-06 10:12:16 -08:00
|
|
|
const useWindowRefocusManager = (route: AppStateRoute) => {
|
|
|
|
|
const windowId = useContext(WindowIdContext);
|
2024-11-08 07:32:05 -08:00
|
|
|
|
2025-02-06 10:12:16 -08:00
|
|
|
const prevRouteName = useRef<string|null>(null);
|
|
|
|
|
const routeName = route?.routeName;
|
2024-11-08 07:32:05 -08:00
|
|
|
useEffect(() => {
|
|
|
|
|
// When a navigation happens in an unfocused window, show the window to the user.
|
|
|
|
|
// This might happen if, for example, a secondary window triggers a navigation in
|
|
|
|
|
// the main window.
|
2025-02-06 10:12:16 -08:00
|
|
|
if (routeName && routeName !== prevRouteName.current) {
|
2024-11-08 07:32:05 -08:00
|
|
|
bridge().switchToWindow(windowId);
|
2019-03-03 00:31:41 +00:00
|
|
|
}
|
2024-11-08 07:32:05 -08:00
|
|
|
|
2025-02-06 10:12:16 -08:00
|
|
|
prevRouteName.current = routeName;
|
|
|
|
|
}, [routeName, windowId]);
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-06 02:30:17 -07:00
|
|
|
const useContainerSize = (container: HTMLElement|null) => {
|
2025-06-10 16:14:08 -07:00
|
|
|
const [size, setSize] = useState({ width: container?.clientWidth ?? 0, height: container?.clientHeight ?? 0 });
|
2025-06-06 02:30:17 -07:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!container) return () => {};
|
|
|
|
|
|
|
|
|
|
const observer = new ResizeObserver(() => {
|
|
|
|
|
setSize({
|
|
|
|
|
width: container.clientWidth,
|
|
|
|
|
height: container.clientHeight,
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
observer.observe(container);
|
|
|
|
|
return () => {
|
|
|
|
|
observer.disconnect();
|
|
|
|
|
};
|
|
|
|
|
}, [container]);
|
|
|
|
|
|
|
|
|
|
return size;
|
|
|
|
|
};
|
|
|
|
|
|
2025-02-06 10:12:16 -08:00
|
|
|
const NavigatorComponent: React.FC<Props> = props => {
|
|
|
|
|
const route = props.route;
|
|
|
|
|
const screenInfo = props.screens[route?.routeName];
|
2025-06-06 02:30:17 -07:00
|
|
|
const [container, setContainer] = useState<HTMLElement|null>(null);
|
2025-02-06 10:12:16 -08:00
|
|
|
|
|
|
|
|
useWindowTitleManager(screenInfo);
|
|
|
|
|
useWindowRefocusManager(route);
|
2025-06-06 02:30:17 -07:00
|
|
|
const size = useContainerSize(container);
|
2024-11-08 07:32:05 -08:00
|
|
|
|
|
|
|
|
if (!route) throw new Error('Route must not be null');
|
|
|
|
|
|
|
|
|
|
const screenProps = route.props ? route.props : {};
|
|
|
|
|
const Screen = screenInfo.screen;
|
|
|
|
|
|
|
|
|
|
return (
|
2025-06-06 02:30:17 -07:00
|
|
|
<div ref={setContainer} style={props.style} className={props.className}>
|
|
|
|
|
<Screen style={size} {...screenProps} />
|
2024-11-08 07:32:05 -08:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
2017-11-06 18:35:04 +00:00
|
|
|
|
2023-01-19 17:19:06 +00:00
|
|
|
const Navigator = connect((state: AppState) => {
|
2019-07-29 14:13:23 +02:00
|
|
|
return {
|
|
|
|
|
route: state.route,
|
|
|
|
|
};
|
|
|
|
|
})(NavigatorComponent);
|
2017-11-06 18:35:04 +00:00
|
|
|
|
2023-01-19 17:19:06 +00:00
|
|
|
export default Navigator;
|