diff options
23 files changed, 209 insertions, 141 deletions
diff --git a/packages/main/src/infrastructure/electron/impl/ElectronMainWindow.ts b/packages/main/src/infrastructure/electron/impl/ElectronMainWindow.ts index edc6592..b0db115 100644 --- a/packages/main/src/infrastructure/electron/impl/ElectronMainWindow.ts +++ b/packages/main/src/infrastructure/electron/impl/ElectronMainWindow.ts | |||
@@ -153,6 +153,9 @@ export default class ElectronMainWindow implements MainWindow { | |||
153 | } | 153 | } |
154 | if (serviceView instanceof ElectronServiceView) { | 154 | if (serviceView instanceof ElectronServiceView) { |
155 | this.browserWindow.setBrowserView(serviceView.browserView); | 155 | this.browserWindow.setBrowserView(serviceView.browserView); |
156 | // If this `BrowserView` hasn't been attached previously, | ||
157 | // we must update its bounds _after_ attaching for the resizing to take effect. | ||
158 | serviceView.updateBounds(); | ||
156 | return; | 159 | return; |
157 | } | 160 | } |
158 | throw new TypeError( | 161 | throw new TypeError( |
diff --git a/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts b/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts index 2e64269..3118efc 100644 --- a/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts +++ b/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts | |||
@@ -18,7 +18,6 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import type { BrowserViewBounds } from '@sophie/shared'; | ||
22 | import { BrowserView } from 'electron'; | 21 | import { BrowserView } from 'electron'; |
23 | 22 | ||
24 | import type Service from '../../../stores/Service'; | 23 | import type Service from '../../../stores/Service'; |
@@ -39,7 +38,7 @@ export default class ElectronServiceView implements ServiceView { | |||
39 | readonly browserView: BrowserView; | 38 | readonly browserView: BrowserView; |
40 | 39 | ||
41 | constructor( | 40 | constructor( |
42 | service: Service, | 41 | private readonly service: Service, |
43 | resources: Resources, | 42 | resources: Resources, |
44 | partition: ElectronPartition, | 43 | partition: ElectronPartition, |
45 | private readonly parent: ElectronViewFactory, | 44 | private readonly parent: ElectronViewFactory, |
@@ -56,6 +55,13 @@ export default class ElectronServiceView implements ServiceView { | |||
56 | }); | 55 | }); |
57 | 56 | ||
58 | this.browserView.setBackgroundColor('#fff'); | 57 | this.browserView.setBackgroundColor('#fff'); |
58 | this.browserView.setAutoResize({ | ||
59 | width: true, | ||
60 | height: true, | ||
61 | }); | ||
62 | // Util we first attach `browserView` to a `BrowserWindow`, | ||
63 | // `setBounds` calls will be ignored, so there's no point in callind `updateBounds` here. | ||
64 | // It will be called by `ElectronMainWindow` when we first attach this service to it. | ||
59 | 65 | ||
60 | const { webContents } = this.browserView; | 66 | const { webContents } = this.browserView; |
61 | 67 | ||
@@ -191,13 +197,17 @@ export default class ElectronServiceView implements ServiceView { | |||
191 | this.browserView.webContents.toggleDevTools(); | 197 | this.browserView.webContents.toggleDevTools(); |
192 | } | 198 | } |
193 | 199 | ||
194 | setBounds(bounds: BrowserViewBounds): void { | 200 | updateBounds(): void { |
195 | this.browserView.setBounds(bounds); | 201 | const { x, y, width, height, hasBounds } = this.service; |
202 | if (!hasBounds) { | ||
203 | return; | ||
204 | } | ||
205 | this.browserView.setBounds({ x, y, width, height }); | ||
196 | } | 206 | } |
197 | 207 | ||
198 | dispose(): void { | 208 | dispose(): void { |
199 | this.parent.unregisterServiceView(this.webContentsId); | ||
200 | setImmediate(() => { | 209 | setImmediate(() => { |
210 | this.parent.unregisterServiceView(this.webContentsId); | ||
201 | // Undocumented electron API, see e.g., https://github.com/electron/electron/issues/29626 | 211 | // Undocumented electron API, see e.g., https://github.com/electron/electron/issues/29626 |
202 | ( | 212 | ( |
203 | this.browserView.webContents as unknown as { destroy(): void } | 213 | this.browserView.webContents as unknown as { destroy(): void } |
diff --git a/packages/main/src/infrastructure/electron/types.ts b/packages/main/src/infrastructure/electron/types.ts index 1321048..92ca9ad 100644 --- a/packages/main/src/infrastructure/electron/types.ts +++ b/packages/main/src/infrastructure/electron/types.ts | |||
@@ -18,8 +18,6 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import type { BrowserViewBounds } from '@sophie/shared'; | ||
22 | |||
23 | import type MainStore from '../../stores/MainStore'; | 21 | import type MainStore from '../../stores/MainStore'; |
24 | import type Profile from '../../stores/Profile'; | 22 | import type Profile from '../../stores/Profile'; |
25 | import type Service from '../../stores/Service'; | 23 | import type Service from '../../stores/Service'; |
@@ -67,7 +65,7 @@ export interface ServiceView { | |||
67 | 65 | ||
68 | toggleDeveloperTools(): void; | 66 | toggleDeveloperTools(): void; |
69 | 67 | ||
70 | setBounds(bounds: BrowserViewBounds): void; | 68 | updateBounds(): void; |
71 | 69 | ||
72 | dispose(): void; | 70 | dispose(): void; |
73 | } | 71 | } |
diff --git a/packages/main/src/reactions/loadServices.ts b/packages/main/src/reactions/loadServices.ts index 4ef6131..f56ac62 100644 --- a/packages/main/src/reactions/loadServices.ts +++ b/packages/main/src/reactions/loadServices.ts | |||
@@ -18,7 +18,7 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { autorun, reaction } from 'mobx'; | 21 | import { reaction } from 'mobx'; |
22 | import { addDisposer } from 'mobx-state-tree'; | 22 | import { addDisposer } from 'mobx-state-tree'; |
23 | 23 | ||
24 | import type { | 24 | import type { |
@@ -94,7 +94,6 @@ export default function loadServices( | |||
94 | throw new Error(`Missing Partition ${profileId}`); | 94 | throw new Error(`Missing Partition ${profileId}`); |
95 | } | 95 | } |
96 | view = viewFactory.createServiceView(service, partition); | 96 | view = viewFactory.createServiceView(service, partition); |
97 | view.setBounds(store.browserViewBounds); | ||
98 | servicesToViews.set(serviceId, view); | 97 | servicesToViews.set(serviceId, view); |
99 | service.setServiceView(view); | 98 | service.setServiceView(view); |
100 | const { urlToLoad } = service; | 99 | const { urlToLoad } = service; |
@@ -133,12 +132,7 @@ export default function loadServices( | |||
133 | }, | 132 | }, |
134 | ); | 133 | ); |
135 | 134 | ||
136 | const resizeDisposer = autorun(() => { | ||
137 | store.visibleService?.serviceView?.setBounds(store.browserViewBounds); | ||
138 | }); | ||
139 | |||
140 | addDisposer(store, () => { | 135 | addDisposer(store, () => { |
141 | resizeDisposer(); | ||
142 | disposer(); | 136 | disposer(); |
143 | store.mainWindow?.setServiceView(undefined); | 137 | store.mainWindow?.setServiceView(undefined); |
144 | servicesToViews.forEach((serviceView, serviceId) => { | 138 | servicesToViews.forEach((serviceView, serviceId) => { |
diff --git a/packages/main/src/stores/MainStore.ts b/packages/main/src/stores/MainStore.ts index d717bed..9affbd0 100644 --- a/packages/main/src/stores/MainStore.ts +++ b/packages/main/src/stores/MainStore.ts | |||
@@ -18,9 +18,9 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import type { Action, BrowserViewBounds } from '@sophie/shared'; | 21 | import type { Action } from '@sophie/shared'; |
22 | import type { ResourceKey } from 'i18next'; | 22 | import type { ResourceKey } from 'i18next'; |
23 | import { applySnapshot, flow, Instance, types } from 'mobx-state-tree'; | 23 | import { flow, Instance, types } from 'mobx-state-tree'; |
24 | 24 | ||
25 | import type I18nStore from '../i18n/I18nStore'; | 25 | import type I18nStore from '../i18n/I18nStore'; |
26 | import type { UseTranslationResult } from '../i18n/I18nStore'; | 26 | import type { UseTranslationResult } from '../i18n/I18nStore'; |
@@ -37,15 +37,6 @@ const log = getLogger('MainStore'); | |||
37 | 37 | ||
38 | const MainStore = types | 38 | const MainStore = types |
39 | .model('MainStore', { | 39 | .model('MainStore', { |
40 | browserViewBounds: types.optional( | ||
41 | types.model('BrowserViewBounds', { | ||
42 | x: 0, | ||
43 | y: 0, | ||
44 | width: 0, | ||
45 | height: 0, | ||
46 | }), | ||
47 | {}, | ||
48 | ), | ||
49 | shared: types.optional(SharedStore, {}), | 40 | shared: types.optional(SharedStore, {}), |
50 | i18n: types.frozen<I18nStore | undefined>(), | 41 | i18n: types.frozen<I18nStore | undefined>(), |
51 | }) | 42 | }) |
@@ -83,9 +74,6 @@ const MainStore = types | |||
83 | }), | 74 | }), |
84 | ) | 75 | ) |
85 | .actions((self) => ({ | 76 | .actions((self) => ({ |
86 | setBrowserViewBounds(bounds: BrowserViewBounds): void { | ||
87 | applySnapshot(self.browserViewBounds, bounds); | ||
88 | }, | ||
89 | setMainWindow(mainWindow: MainWindow | undefined): void { | 77 | setMainWindow(mainWindow: MainWindow | undefined): void { |
90 | self.mainWindow = mainWindow; | 78 | self.mainWindow = mainWindow; |
91 | }, | 79 | }, |
@@ -121,9 +109,6 @@ const MainStore = types | |||
121 | .actions((self) => ({ | 109 | .actions((self) => ({ |
122 | dispatch(action: Action): void { | 110 | dispatch(action: Action): void { |
123 | switch (action.action) { | 111 | switch (action.action) { |
124 | case 'set-browser-view-bounds': | ||
125 | self.setBrowserViewBounds(action.browserViewBounds); | ||
126 | break; | ||
127 | case 'set-selected-service-id': | 112 | case 'set-selected-service-id': |
128 | self.settings.setSelectedServiceId(action.serviceId); | 113 | self.settings.setSelectedServiceId(action.serviceId); |
129 | break; | 114 | break; |
diff --git a/packages/main/src/stores/Service.ts b/packages/main/src/stores/Service.ts index 1d46dc9..8ba8098 100644 --- a/packages/main/src/stores/Service.ts +++ b/packages/main/src/stores/Service.ts | |||
@@ -25,6 +25,7 @@ import { | |||
25 | defineServiceModel, | 25 | defineServiceModel, |
26 | ServiceAction, | 26 | ServiceAction, |
27 | type ServiceStateSnapshotIn, | 27 | type ServiceStateSnapshotIn, |
28 | type BrowserViewBounds, | ||
28 | } from '@sophie/shared'; | 29 | } from '@sophie/shared'; |
29 | import { type Instance, getSnapshot, cast, flow } from 'mobx-state-tree'; | 30 | import { type Instance, getSnapshot, cast, flow } from 'mobx-state-tree'; |
30 | 31 | ||
@@ -41,8 +42,18 @@ const Service = defineServiceModel(ServiceSettings) | |||
41 | .volatile( | 42 | .volatile( |
42 | (): { | 43 | (): { |
43 | serviceView: ServiceView | undefined; | 44 | serviceView: ServiceView | undefined; |
45 | x: number; | ||
46 | y: number; | ||
47 | width: number; | ||
48 | height: number; | ||
49 | hasBounds: boolean; | ||
44 | } => ({ | 50 | } => ({ |
45 | serviceView: undefined, | 51 | serviceView: undefined, |
52 | x: 0, | ||
53 | y: 0, | ||
54 | width: 0, | ||
55 | height: 0, | ||
56 | hasBounds: false, | ||
46 | }), | 57 | }), |
47 | ) | 58 | ) |
48 | .views((self) => ({ | 59 | .views((self) => ({ |
@@ -59,10 +70,20 @@ const Service = defineServiceModel(ServiceSettings) | |||
59 | })) | 70 | })) |
60 | .views((self) => ({ | 71 | .views((self) => ({ |
61 | get shouldBeVisible(): boolean { | 72 | get shouldBeVisible(): boolean { |
62 | return self.shouldBeLoaded && !self.hasError; | 73 | // Do not attach service views for which we don't know the appropriate frame size, |
74 | // because they will just appear in a random location until the frame size is determined. | ||
75 | return self.shouldBeLoaded && !self.hasError && self.hasBounds; | ||
63 | }, | 76 | }, |
64 | })) | 77 | })) |
65 | .actions((self) => ({ | 78 | .actions((self) => ({ |
79 | setBrowserViewBounds(bounds: BrowserViewBounds): void { | ||
80 | self.x = bounds.x; | ||
81 | self.y = bounds.y; | ||
82 | self.width = bounds.width; | ||
83 | self.height = bounds.height; | ||
84 | self.hasBounds = true; | ||
85 | self.serviceView?.updateBounds(); | ||
86 | }, | ||
66 | setServiceView(serviceView: ServiceView | undefined): void { | 87 | setServiceView(serviceView: ServiceView | undefined): void { |
67 | self.serviceView = serviceView; | 88 | self.serviceView = serviceView; |
68 | }, | 89 | }, |
@@ -263,6 +284,9 @@ const Service = defineServiceModel(ServiceSettings) | |||
263 | .actions((self) => ({ | 284 | .actions((self) => ({ |
264 | dispatch(action: ServiceAction): void { | 285 | dispatch(action: ServiceAction): void { |
265 | switch (action.action) { | 286 | switch (action.action) { |
287 | case 'set-browser-view-bounds': | ||
288 | self.setBrowserViewBounds(action.browserViewBounds); | ||
289 | break; | ||
266 | case 'back': | 290 | case 'back': |
267 | self.goBack(); | 291 | self.goBack(); |
268 | break; | 292 | break; |
diff --git a/packages/renderer/src/components/App.tsx b/packages/renderer/src/components/App.tsx index d381abf..2f728e7 100644 --- a/packages/renderer/src/components/App.tsx +++ b/packages/renderer/src/components/App.tsx | |||
@@ -23,12 +23,8 @@ import { observer } from 'mobx-react-lite'; | |||
23 | import React, { useCallback, useEffect } from 'react'; | 23 | import React, { useCallback, useEffect } from 'react'; |
24 | import { useTranslation } from 'react-i18next'; | 24 | import { useTranslation } from 'react-i18next'; |
25 | 25 | ||
26 | import BrowserViewPlaceholder from './BrowserViewPlaceholder'; | 26 | import ServicePanel from './ServicePanel'; |
27 | import { useStore } from './StoreProvider'; | 27 | import { useStore } from './StoreProvider'; |
28 | import InsecureConnectionBanner from './banner/InsecureConnectionBanner'; | ||
29 | import NewWindowBanner from './banner/NewWindowBanner'; | ||
30 | import ErrorPage from './errorPage/ErrorPage'; | ||
31 | import LocationBar from './locationBar/LocationBar'; | ||
32 | import Sidebar from './sidebar/Sidebar'; | 28 | import Sidebar from './sidebar/Sidebar'; |
33 | 29 | ||
34 | function App({ devMode }: { devMode: boolean }): JSX.Element { | 30 | function App({ devMode }: { devMode: boolean }): JSX.Element { |
@@ -37,6 +33,7 @@ function App({ devMode }: { devMode: boolean }): JSX.Element { | |||
37 | }); | 33 | }); |
38 | const { | 34 | const { |
39 | settings: { selectedService }, | 35 | settings: { selectedService }, |
36 | shared: { services }, | ||
40 | } = useStore(); | 37 | } = useStore(); |
41 | const { | 38 | const { |
42 | settings: { name: serviceName }, | 39 | settings: { name: serviceName }, |
@@ -95,20 +92,13 @@ function App({ devMode }: { devMode: boolean }): JSX.Element { | |||
95 | <Box | 92 | <Box |
96 | sx={{ | 93 | sx={{ |
97 | flex: 1, | 94 | flex: 1, |
98 | display: 'flex', | ||
99 | overflow: 'hidden', | ||
100 | flexDirection: 'column', | ||
101 | alignItems: 'stretch', | ||
102 | height: '100%', | 95 | height: '100%', |
103 | zIndex: 100, | 96 | position: 'relative', |
104 | }} | 97 | }} |
105 | > | 98 | > |
106 | <LocationBar /> | 99 | {services.map((service) => ( |
107 | <InsecureConnectionBanner service={selectedService} /> | 100 | <ServicePanel key={service.id} service={service} /> |
108 | <NewWindowBanner service={selectedService} /> | 101 | ))} |
109 | <BrowserViewPlaceholder> | ||
110 | <ErrorPage service={selectedService} /> | ||
111 | </BrowserViewPlaceholder> | ||
112 | </Box> | 102 | </Box> |
113 | </Box> | 103 | </Box> |
114 | ); | 104 | ); |
diff --git a/packages/renderer/src/components/BrowserViewPlaceholder.tsx b/packages/renderer/src/components/BrowserViewPlaceholder.tsx index 9bd1176..2bfc9b0 100644 --- a/packages/renderer/src/components/BrowserViewPlaceholder.tsx +++ b/packages/renderer/src/components/BrowserViewPlaceholder.tsx | |||
@@ -22,29 +22,29 @@ import Box from '@mui/material/Box'; | |||
22 | import throttle from 'lodash-es/throttle'; | 22 | import throttle from 'lodash-es/throttle'; |
23 | import React, { ReactNode, useCallback, useRef } from 'react'; | 23 | import React, { ReactNode, useCallback, useRef } from 'react'; |
24 | 24 | ||
25 | import { useStore } from './StoreProvider'; | 25 | import Service from '../stores/Service'; |
26 | 26 | ||
27 | function BrowserViewPlaceholder({ | 27 | function BrowserViewPlaceholder({ |
28 | service, | ||
28 | children, | 29 | children, |
29 | }: { | 30 | }: { |
31 | service: Service; | ||
30 | children?: ReactNode; | 32 | children?: ReactNode; |
31 | }): JSX.Element { | 33 | }): JSX.Element { |
32 | const store = useStore(); | ||
33 | |||
34 | // eslint-disable-next-line react-hooks/exhaustive-deps -- react-hooks doesn't support `throttle`. | 34 | // eslint-disable-next-line react-hooks/exhaustive-deps -- react-hooks doesn't support `throttle`. |
35 | const onResize = useCallback( | 35 | const onResize = useCallback( |
36 | throttle(([entry]: ResizeObserverEntry[]) => { | 36 | throttle(([entry]: ResizeObserverEntry[]) => { |
37 | if (entry) { | 37 | if (entry) { |
38 | const { x, y, width, height } = entry.target.getBoundingClientRect(); | 38 | const { x, y, width, height } = entry.target.getBoundingClientRect(); |
39 | store.setBrowserViewBounds({ | 39 | service.setBrowserViewBounds({ |
40 | x: Math.round(x), | 40 | x: Math.round(x), |
41 | y: Math.round(y), | 41 | y: Math.round(y), |
42 | width: Math.round(width), | 42 | width: Math.round(width), |
43 | height: Math.round(height), | 43 | height: Math.round(height), |
44 | }); | 44 | }); |
45 | } | 45 | } |
46 | }, 40), | 46 | }, 100), |
47 | [store], | 47 | [service], |
48 | ); | 48 | ); |
49 | 49 | ||
50 | const resizeObserverRef = useRef<ResizeObserver | undefined>(); | 50 | const resizeObserverRef = useRef<ResizeObserver | undefined>(); |
@@ -61,7 +61,7 @@ function BrowserViewPlaceholder({ | |||
61 | resizeObserverRef.current = new ResizeObserver(onResize); | 61 | resizeObserverRef.current = new ResizeObserver(onResize); |
62 | resizeObserverRef.current.observe(element); | 62 | resizeObserverRef.current.observe(element); |
63 | }, | 63 | }, |
64 | [onResize, resizeObserverRef], | 64 | [onResize], |
65 | ); | 65 | ); |
66 | 66 | ||
67 | return ( | 67 | return ( |
diff --git a/packages/renderer/src/components/ServicePanel.tsx b/packages/renderer/src/components/ServicePanel.tsx new file mode 100644 index 0000000..de58d24 --- /dev/null +++ b/packages/renderer/src/components/ServicePanel.tsx | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2021-2022 Kristóf Marussy <kristof@marussy.com> | ||
3 | * | ||
4 | * This file is part of Sophie. | ||
5 | * | ||
6 | * Sophie is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU Affero General Public License as | ||
8 | * published by the Free Software Foundation, version 3. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Affero General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Affero General Public License | ||
16 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | * SPDX-License-Identifier: AGPL-3.0-only | ||
19 | */ | ||
20 | |||
21 | import Box from '@mui/material/Box'; | ||
22 | import { observer } from 'mobx-react-lite'; | ||
23 | import React from 'react'; | ||
24 | |||
25 | import Service from '../stores/Service'; | ||
26 | |||
27 | import BrowserViewPlaceholder from './BrowserViewPlaceholder'; | ||
28 | import { useStore } from './StoreProvider'; | ||
29 | import InsecureConnectionBanner from './banner/InsecureConnectionBanner'; | ||
30 | import NewWindowBanner from './banner/NewWindowBanner'; | ||
31 | import ErrorPage from './errorPage/ErrorPage'; | ||
32 | import LocationBar from './locationBar/LocationBar'; | ||
33 | |||
34 | export function getServicePanelID(service: Service): string { | ||
35 | return `Sophie-${service.id}-ServicePanel`; | ||
36 | } | ||
37 | |||
38 | function ServicePanel({ service }: { service: Service }): JSX.Element { | ||
39 | const { | ||
40 | settings: { selectedService }, | ||
41 | } = useStore(); | ||
42 | |||
43 | const { | ||
44 | settings: { name }, | ||
45 | } = service; | ||
46 | const visible = service === selectedService; | ||
47 | |||
48 | return ( | ||
49 | <Box | ||
50 | id={getServicePanelID(service)} | ||
51 | role="tabpanel" | ||
52 | aria-label={name} | ||
53 | sx={{ | ||
54 | position: 'absolute', | ||
55 | top: 0, | ||
56 | left: 0, | ||
57 | bottom: 0, | ||
58 | right: 0, | ||
59 | display: 'flex', | ||
60 | overflow: 'hidden', | ||
61 | visibility: visible ? 'visible' : 'hidden', | ||
62 | flexDirection: 'column', | ||
63 | alignItems: 'stretch', | ||
64 | }} | ||
65 | > | ||
66 | <LocationBar service={service} /> | ||
67 | <InsecureConnectionBanner service={service} /> | ||
68 | <NewWindowBanner service={service} /> | ||
69 | <BrowserViewPlaceholder service={service}> | ||
70 | <ErrorPage service={service} /> | ||
71 | </BrowserViewPlaceholder> | ||
72 | </Box> | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | export default observer(ServicePanel); | ||
diff --git a/packages/renderer/src/components/banner/InsecureConnectionBanner.tsx b/packages/renderer/src/components/banner/InsecureConnectionBanner.tsx index 7a03fce..0b70db6 100644 --- a/packages/renderer/src/components/banner/InsecureConnectionBanner.tsx +++ b/packages/renderer/src/components/banner/InsecureConnectionBanner.tsx | |||
@@ -34,17 +34,12 @@ import NotificationBanner from './NotificationBanner'; | |||
34 | function InsecureConnectionBanner({ | 34 | function InsecureConnectionBanner({ |
35 | service, | 35 | service, |
36 | }: { | 36 | }: { |
37 | service: Service | undefined; | 37 | service: Service; |
38 | }): JSX.Element | null { | 38 | }): JSX.Element | null { |
39 | const { t } = useTranslation(undefined, { | 39 | const { t } = useTranslation(undefined, { |
40 | keyPrefix: 'banner.insecureConnection', | 40 | keyPrefix: 'banner.insecureConnection', |
41 | }); | 41 | }); |
42 | 42 | ||
43 | if (service === undefined) { | ||
44 | // eslint-disable-next-line unicorn/no-null -- React requires `null` to skip rendering. | ||
45 | return null; | ||
46 | } | ||
47 | |||
48 | const { | 43 | const { |
49 | canReconnectSecurely, | 44 | canReconnectSecurely, |
50 | hasError, | 45 | hasError, |
diff --git a/packages/renderer/src/components/banner/NewWindowBanner.tsx b/packages/renderer/src/components/banner/NewWindowBanner.tsx index 478de8e..07fafda 100644 --- a/packages/renderer/src/components/banner/NewWindowBanner.tsx +++ b/packages/renderer/src/components/banner/NewWindowBanner.tsx | |||
@@ -32,17 +32,12 @@ import NotificationBanner from './NotificationBanner'; | |||
32 | function NewWindowBanner({ | 32 | function NewWindowBanner({ |
33 | service, | 33 | service, |
34 | }: { | 34 | }: { |
35 | service: Service | undefined; | 35 | service: Service; |
36 | }): JSX.Element | null { | 36 | }): JSX.Element | null { |
37 | const { t } = useTranslation(undefined, { | 37 | const { t } = useTranslation(undefined, { |
38 | keyPrefix: 'banner.newWindow', | 38 | keyPrefix: 'banner.newWindow', |
39 | }); | 39 | }); |
40 | 40 | ||
41 | if (service === undefined) { | ||
42 | // eslint-disable-next-line unicorn/no-null -- React requires `null` to skip rendering. | ||
43 | return null; | ||
44 | } | ||
45 | |||
46 | const { | 41 | const { |
47 | popups, | 42 | popups, |
48 | settings: { name }, | 43 | settings: { name }, |
diff --git a/packages/renderer/src/components/errorPage/ErrorPage.tsx b/packages/renderer/src/components/errorPage/ErrorPage.tsx index 99ca020..10f54cd 100644 --- a/packages/renderer/src/components/errorPage/ErrorPage.tsx +++ b/packages/renderer/src/components/errorPage/ErrorPage.tsx | |||
@@ -123,14 +123,10 @@ function formatError(service: Service, t: TFunction): ErrorDetails { | |||
123 | } | 123 | } |
124 | } | 124 | } |
125 | 125 | ||
126 | function ErrorPage({ | 126 | function ErrorPage({ service }: { service: Service }): JSX.Element | null { |
127 | service, | ||
128 | }: { | ||
129 | service: Service | undefined; | ||
130 | }): JSX.Element | null { | ||
131 | const { t } = useTranslation(undefined); | 127 | const { t } = useTranslation(undefined); |
132 | 128 | ||
133 | if (service === undefined || !service.hasError) { | 129 | if (!service.hasError) { |
134 | // eslint-disable-next-line unicorn/no-null -- React requires `null` to skip rendering. | 130 | // eslint-disable-next-line unicorn/no-null -- React requires `null` to skip rendering. |
135 | return null; | 131 | return null; |
136 | } | 132 | } |
diff --git a/packages/renderer/src/components/locationBar/ExtraButtons.tsx b/packages/renderer/src/components/locationBar/ExtraButtons.tsx index ef90199..4d4c3c4 100644 --- a/packages/renderer/src/components/locationBar/ExtraButtons.tsx +++ b/packages/renderer/src/components/locationBar/ExtraButtons.tsx | |||
@@ -27,11 +27,7 @@ import { useTranslation } from 'react-i18next'; | |||
27 | 27 | ||
28 | import type Service from '../../stores/Service'; | 28 | import type Service from '../../stores/Service'; |
29 | 29 | ||
30 | function ExtraButtons({ | 30 | function ExtraButtons({ service }: { service: Service }): JSX.Element { |
31 | service, | ||
32 | }: { | ||
33 | service: Service | undefined; | ||
34 | }): JSX.Element { | ||
35 | const { t } = useTranslation(undefined, { | 31 | const { t } = useTranslation(undefined, { |
36 | keyPrefix: 'toolbar', | 32 | keyPrefix: 'toolbar', |
37 | }); | 33 | }); |
@@ -40,8 +36,8 @@ function ExtraButtons({ | |||
40 | <Box display="flex"> | 36 | <Box display="flex"> |
41 | <IconButton | 37 | <IconButton |
42 | aria-label={t('openInBrowser')} | 38 | aria-label={t('openInBrowser')} |
43 | disabled={service?.currentUrl === undefined} | 39 | disabled={service.currentUrl === undefined} |
44 | onClick={() => service?.openCurrentURLInExternalBrowser()} | 40 | onClick={() => service.openCurrentURLInExternalBrowser()} |
45 | > | 41 | > |
46 | <IconOpenInBrowser /> | 42 | <IconOpenInBrowser /> |
47 | </IconButton> | 43 | </IconButton> |
diff --git a/packages/renderer/src/components/locationBar/LocationBar.tsx b/packages/renderer/src/components/locationBar/LocationBar.tsx index 54ead8e..c290722 100644 --- a/packages/renderer/src/components/locationBar/LocationBar.tsx +++ b/packages/renderer/src/components/locationBar/LocationBar.tsx | |||
@@ -22,13 +22,16 @@ import { styled } from '@mui/material/styles'; | |||
22 | import { observer } from 'mobx-react-lite'; | 22 | import { observer } from 'mobx-react-lite'; |
23 | import React from 'react'; | 23 | import React from 'react'; |
24 | 24 | ||
25 | import type Service from '../../stores/Service'; | ||
25 | import { useStore } from '../StoreProvider'; | 26 | import { useStore } from '../StoreProvider'; |
26 | 27 | ||
27 | import ExtraButtons from './ExtraButtons'; | 28 | import ExtraButtons from './ExtraButtons'; |
28 | import LocationTextField from './LocationTextField'; | 29 | import LocationTextField from './LocationTextField'; |
29 | import NavigationButtons from './NavigationButtons'; | 30 | import NavigationButtons from './NavigationButtons'; |
30 | 31 | ||
31 | export const LOCATION_BAR_ID = 'Sophie-LocationBar'; | 32 | export function getLocaltionBarID(service: Service): string { |
33 | return `Sophie-${service.id}-LocationBar`; | ||
34 | } | ||
32 | 35 | ||
33 | const LocationBarRoot = styled('header', { | 36 | const LocationBarRoot = styled('header', { |
34 | name: 'LocationBar', | 37 | name: 'LocationBar', |
@@ -41,17 +44,22 @@ const LocationBarRoot = styled('header', { | |||
41 | borderBottom: `1px solid ${theme.palette.divider}`, | 44 | borderBottom: `1px solid ${theme.palette.divider}`, |
42 | })); | 45 | })); |
43 | 46 | ||
44 | function LocationBar(): JSX.Element { | 47 | function LocationBar({ service }: { service: Service }): JSX.Element { |
45 | const { | 48 | const { |
46 | shared: { locationBarVisible }, | 49 | settings: { showLocationBar }, |
47 | settings: { selectedService }, | ||
48 | } = useStore(); | 50 | } = useStore(); |
49 | 51 | ||
52 | const { alwaysShowLocationBar } = service; | ||
53 | const locationBarVisible = showLocationBar || alwaysShowLocationBar; | ||
54 | |||
50 | return ( | 55 | return ( |
51 | <LocationBarRoot id={LOCATION_BAR_ID} hidden={!locationBarVisible}> | 56 | <LocationBarRoot |
52 | <NavigationButtons service={selectedService} /> | 57 | id={getLocaltionBarID(service)} |
53 | <LocationTextField service={selectedService} /> | 58 | hidden={!locationBarVisible} |
54 | <ExtraButtons service={selectedService} /> | 59 | > |
60 | <NavigationButtons service={service} /> | ||
61 | <LocationTextField service={service} /> | ||
62 | <ExtraButtons service={service} /> | ||
55 | </LocationBarRoot> | 63 | </LocationBarRoot> |
56 | ); | 64 | ); |
57 | } | 65 | } |
diff --git a/packages/renderer/src/components/locationBar/LocationTextField.tsx b/packages/renderer/src/components/locationBar/LocationTextField.tsx index 85cf794..1d6b561 100644 --- a/packages/renderer/src/components/locationBar/LocationTextField.tsx +++ b/packages/renderer/src/components/locationBar/LocationTextField.tsx | |||
@@ -20,7 +20,6 @@ | |||
20 | 20 | ||
21 | import FilledInput from '@mui/material/FilledInput'; | 21 | import FilledInput from '@mui/material/FilledInput'; |
22 | import { styled } from '@mui/material/styles'; | 22 | import { styled } from '@mui/material/styles'; |
23 | import { SecurityLabelKind } from '@sophie/shared'; | ||
24 | import { autorun } from 'mobx'; | 23 | import { autorun } from 'mobx'; |
25 | import { observer } from 'mobx-react-lite'; | 24 | import { observer } from 'mobx-react-lite'; |
26 | import React, { useCallback, useEffect, useState } from 'react'; | 25 | import React, { useCallback, useEffect, useState } from 'react'; |
@@ -44,19 +43,15 @@ const LocationTextFieldRoot = styled(FilledInput, { | |||
44 | }, | 43 | }, |
45 | })); | 44 | })); |
46 | 45 | ||
47 | function LocationTextField({ | 46 | function LocationTextField({ service }: { service: Service }): JSX.Element { |
48 | service, | ||
49 | }: { | ||
50 | service: Service | undefined; | ||
51 | }): JSX.Element { | ||
52 | const [inputFocused, setInputFocused] = useState(false); | 47 | const [inputFocused, setInputFocused] = useState(false); |
53 | const [changed, setChanged] = useState(false); | 48 | const [changed, setChanged] = useState(false); |
54 | const [value, setValue] = useState(''); | 49 | const [value, setValue] = useState(''); |
55 | 50 | ||
56 | const resetValue = useCallback(() => { | 51 | const resetValue = useCallback(() => { |
57 | setValue(service?.currentUrl ?? ''); | 52 | setValue(service.currentUrl ?? ''); |
58 | setChanged(false); | 53 | setChanged(false); |
59 | }, [service, setChanged, setValue]); | 54 | }, [service]); |
60 | 55 | ||
61 | useEffect( | 56 | useEffect( |
62 | () => | 57 | () => |
@@ -84,8 +79,8 @@ function LocationTextField({ | |||
84 | overlayVisible: !inputFocused && !changed, | 79 | overlayVisible: !inputFocused && !changed, |
85 | overlay: ( | 80 | overlay: ( |
86 | <UrlOverlay | 81 | <UrlOverlay |
87 | url={service?.currentUrl ?? ''} | 82 | url={service.currentUrl ?? ''} |
88 | alert={service?.hasSecurityLabelWarning ?? false} | 83 | alert={service.hasSecurityLabelWarning ?? false} |
89 | /> | 84 | /> |
90 | ), | 85 | ), |
91 | }} | 86 | }} |
@@ -102,7 +97,7 @@ function LocationTextField({ | |||
102 | resetValue(); | 97 | resetValue(); |
103 | break; | 98 | break; |
104 | case 'Enter': | 99 | case 'Enter': |
105 | service?.go(value); | 100 | service.go(value); |
106 | break; | 101 | break; |
107 | default: | 102 | default: |
108 | // Nothing to do, let the key event through. | 103 | // Nothing to do, let the key event through. |
@@ -117,14 +112,14 @@ function LocationTextField({ | |||
117 | disableUnderline | 112 | disableUnderline |
118 | startAdornment={ | 113 | startAdornment={ |
119 | <SecurityLabel | 114 | <SecurityLabel |
120 | kind={service?.securityLabel ?? SecurityLabelKind.Empty} | 115 | kind={service.securityLabel} |
121 | changed={changed} | 116 | changed={changed} |
122 | position="start" | 117 | position="start" |
123 | /> | 118 | /> |
124 | } | 119 | } |
125 | endAdornment={ | 120 | endAdornment={ |
126 | changed ? ( | 121 | changed ? ( |
127 | <GoButton onClick={() => service?.go(value)} position="end" /> | 122 | <GoButton onClick={() => service.go(value)} position="end" /> |
128 | ) : undefined | 123 | ) : undefined |
129 | } | 124 | } |
130 | value={value} | 125 | value={value} |
diff --git a/packages/renderer/src/components/locationBar/NavigationButtons.tsx b/packages/renderer/src/components/locationBar/NavigationButtons.tsx index 9c4ebdb..96e40e7 100644 --- a/packages/renderer/src/components/locationBar/NavigationButtons.tsx +++ b/packages/renderer/src/components/locationBar/NavigationButtons.tsx | |||
@@ -32,11 +32,7 @@ import { useTranslation } from 'react-i18next'; | |||
32 | 32 | ||
33 | import type Service from '../../stores/Service'; | 33 | import type Service from '../../stores/Service'; |
34 | 34 | ||
35 | function NavigationButtons({ | 35 | function NavigationButtons({ service }: { service: Service }): JSX.Element { |
36 | service, | ||
37 | }: { | ||
38 | service: Service | undefined; | ||
39 | }): JSX.Element { | ||
40 | const { t } = useTranslation(undefined, { | 36 | const { t } = useTranslation(undefined, { |
41 | keyPrefix: 'toolbar', | 37 | keyPrefix: 'toolbar', |
42 | }); | 38 | }); |
@@ -46,36 +42,31 @@ function NavigationButtons({ | |||
46 | <Box display="flex"> | 42 | <Box display="flex"> |
47 | <IconButton | 43 | <IconButton |
48 | aria-label={t('back')} | 44 | aria-label={t('back')} |
49 | disabled={service === undefined || !service.canGoBack} | 45 | disabled={!service.canGoBack} |
50 | onClick={() => service?.goBack()} | 46 | onClick={() => service.goBack()} |
51 | > | 47 | > |
52 | {direction === 'ltr' ? <IconArrowBack /> : <IconArrowForward />} | 48 | {direction === 'ltr' ? <IconArrowBack /> : <IconArrowForward />} |
53 | </IconButton> | 49 | </IconButton> |
54 | <IconButton | 50 | <IconButton |
55 | aria-label={t('forward')} | 51 | aria-label={t('forward')} |
56 | disabled={service === undefined || !service.canGoForward} | 52 | disabled={!service.canGoForward} |
57 | onClick={() => service?.goForward()} | 53 | onClick={() => service.goForward()} |
58 | > | 54 | > |
59 | {direction === 'ltr' ? <IconArrowForward /> : <IconArrowBack />} | 55 | {direction === 'ltr' ? <IconArrowForward /> : <IconArrowBack />} |
60 | </IconButton> | 56 | </IconButton> |
61 | {service?.loading ?? false ? ( | 57 | {service.loading ? ( |
62 | <IconButton aria-label={t('stop')} onClick={() => service?.stop()}> | 58 | <IconButton aria-label={t('stop')} onClick={() => service.stop()}> |
63 | <IconStop /> | 59 | <IconStop /> |
64 | </IconButton> | 60 | </IconButton> |
65 | ) : ( | 61 | ) : ( |
66 | <IconButton | 62 | <IconButton |
67 | aria-label={t('reload')} | 63 | aria-label={t('reload')} |
68 | disabled={service === undefined} | 64 | onClick={(event) => service.reload(event.shiftKey)} |
69 | onClick={(event) => service?.reload(event.shiftKey)} | ||
70 | > | 65 | > |
71 | <IconRefresh /> | 66 | <IconRefresh /> |
72 | </IconButton> | 67 | </IconButton> |
73 | )} | 68 | )} |
74 | <IconButton | 69 | <IconButton aria-label={t('home')} onClick={() => service.goHome()}> |
75 | aria-label={t('home')} | ||
76 | disabled={service === undefined} | ||
77 | onClick={() => service?.goHome()} | ||
78 | > | ||
79 | <IconHome /> | 70 | <IconHome /> |
80 | </IconButton> | 71 | </IconButton> |
81 | </Box> | 72 | </Box> |
diff --git a/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx b/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx index 0ebd359..24cfd0c 100644 --- a/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx +++ b/packages/renderer/src/components/sidebar/ServiceSwitcher.tsx | |||
@@ -27,6 +27,7 @@ import React from 'react'; | |||
27 | import { useTranslation } from 'react-i18next'; | 27 | import { useTranslation } from 'react-i18next'; |
28 | 28 | ||
29 | import type Service from '../../stores/Service'; | 29 | import type Service from '../../stores/Service'; |
30 | import { getServicePanelID } from '../ServicePanel'; | ||
30 | import { useStore } from '../StoreProvider'; | 31 | import { useStore } from '../StoreProvider'; |
31 | 32 | ||
32 | import ServiceIcon from './ServiceIcon'; | 33 | import ServiceIcon from './ServiceIcon'; |
@@ -112,6 +113,7 @@ function ServiceSwitcher(): JSX.Element { | |||
112 | value={service.id} | 113 | value={service.id} |
113 | icon={<ServiceIcon service={service} />} | 114 | icon={<ServiceIcon service={service} />} |
114 | aria-label={getServiceTitle(service, t)} | 115 | aria-label={getServiceTitle(service, t)} |
116 | aria-controls={getServicePanelID(service)} | ||
115 | /> | 117 | /> |
116 | ))} | 118 | ))} |
117 | </ServiceSwitcherRoot> | 119 | </ServiceSwitcherRoot> |
diff --git a/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx b/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx index 7fc559d..c697170 100644 --- a/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx +++ b/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx | |||
@@ -28,7 +28,7 @@ import React from 'react'; | |||
28 | import { useTranslation } from 'react-i18next'; | 28 | import { useTranslation } from 'react-i18next'; |
29 | 29 | ||
30 | import { useStore } from '../StoreProvider'; | 30 | import { useStore } from '../StoreProvider'; |
31 | import { LOCATION_BAR_ID } from '../locationBar/LocationBar'; | 31 | import { getLocaltionBarID } from '../locationBar/LocationBar'; |
32 | 32 | ||
33 | function ToggleLocationBarIcon({ | 33 | function ToggleLocationBarIcon({ |
34 | loading, | 34 | loading, |
@@ -54,13 +54,17 @@ function ToggleLocationBarButton(): JSX.Element { | |||
54 | const { selectedService } = settings; | 54 | const { selectedService } = settings; |
55 | 55 | ||
56 | return ( | 56 | return ( |
57 | /* eslint-disable react/jsx-props-no-spreading -- Conditionally set the aria-controls prop. */ | ||
57 | <IconButton | 58 | <IconButton |
58 | disabled={!canToggleLocationBar} | 59 | disabled={!canToggleLocationBar} |
59 | aria-pressed={locationBarVisible} | 60 | aria-pressed={locationBarVisible} |
60 | aria-controls={LOCATION_BAR_ID} | 61 | {...(selectedService === undefined |
62 | ? {} | ||
63 | : { 'aria-controls': getLocaltionBarID(selectedService) })} | ||
61 | aria-label={t('toolbar.toggleLocationBar')} | 64 | aria-label={t('toolbar.toggleLocationBar')} |
62 | onClick={() => settings.toggleLocationBar()} | 65 | onClick={() => settings.toggleLocationBar()} |
63 | > | 66 | > |
67 | {/* eslint-enable react/jsx-props-no-spreading */} | ||
64 | <ToggleLocationBarIcon | 68 | <ToggleLocationBarIcon |
65 | loading={selectedService?.loading ?? false} | 69 | loading={selectedService?.loading ?? false} |
66 | show={locationBarVisible} | 70 | show={locationBarVisible} |
diff --git a/packages/renderer/src/stores/RendererStore.ts b/packages/renderer/src/stores/RendererStore.ts index 7052162..a3983ca 100644 --- a/packages/renderer/src/stores/RendererStore.ts +++ b/packages/renderer/src/stores/RendererStore.ts | |||
@@ -18,14 +18,14 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { BrowserViewBounds, SophieRenderer } from '@sophie/shared'; | 21 | import type { SophieRenderer } from '@sophie/shared'; |
22 | import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree'; | 22 | import { applySnapshot, applyPatch, Instance, types } from 'mobx-state-tree'; |
23 | 23 | ||
24 | import { getLogger } from '../utils/log'; | 24 | import { getLogger } from '../utils/log'; |
25 | 25 | ||
26 | import GlobalSettings from './GlobalSettings'; | 26 | import type GlobalSettings from './GlobalSettings'; |
27 | import RendererEnv, { getEnv } from './RendererEnv'; | 27 | import type RendererEnv from './RendererEnv'; |
28 | import Service from './Service'; | 28 | import type Service from './Service'; |
29 | import SharedStore from './SharedStore'; | 29 | import SharedStore from './SharedStore'; |
30 | 30 | ||
31 | const log = getLogger('RendererStore'); | 31 | const log = getLogger('RendererStore'); |
@@ -41,14 +41,6 @@ const RendererStore = types | |||
41 | get services(): Service[] { | 41 | get services(): Service[] { |
42 | return self.shared.services; | 42 | return self.shared.services; |
43 | }, | 43 | }, |
44 | })) | ||
45 | .actions((self) => ({ | ||
46 | setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { | ||
47 | getEnv(self).dispatchMainAction({ | ||
48 | action: 'set-browser-view-bounds', | ||
49 | browserViewBounds, | ||
50 | }); | ||
51 | }, | ||
52 | })); | 44 | })); |
53 | 45 | ||
54 | /* | 46 | /* |
diff --git a/packages/renderer/src/stores/Service.ts b/packages/renderer/src/stores/Service.ts index dcaf96e..c8d513f 100644 --- a/packages/renderer/src/stores/Service.ts +++ b/packages/renderer/src/stores/Service.ts | |||
@@ -18,7 +18,11 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import { defineServiceModel, ServiceAction } from '@sophie/shared'; | 21 | import { |
22 | BrowserViewBounds, | ||
23 | defineServiceModel, | ||
24 | ServiceAction, | ||
25 | } from '@sophie/shared'; | ||
22 | import { Instance } from 'mobx-state-tree'; | 26 | import { Instance } from 'mobx-state-tree'; |
23 | 27 | ||
24 | import { getEnv } from './RendererEnv'; | 28 | import { getEnv } from './RendererEnv'; |
@@ -49,6 +53,12 @@ const Service = defineServiceModel(ServiceSettings) | |||
49 | } | 53 | } |
50 | 54 | ||
51 | return { | 55 | return { |
56 | setBrowserViewBounds(browserViewBounds: BrowserViewBounds): void { | ||
57 | dispatch({ | ||
58 | action: 'set-browser-view-bounds', | ||
59 | browserViewBounds, | ||
60 | }); | ||
61 | }, | ||
52 | goBack(): void { | 62 | goBack(): void { |
53 | dispatch({ | 63 | dispatch({ |
54 | action: 'back', | 64 | action: 'back', |
diff --git a/packages/shared/src/schemas/Action.ts b/packages/shared/src/schemas/Action.ts index ce983fa..7ece27a 100644 --- a/packages/shared/src/schemas/Action.ts +++ b/packages/shared/src/schemas/Action.ts | |||
@@ -20,7 +20,6 @@ | |||
20 | 20 | ||
21 | import { z } from 'zod'; | 21 | import { z } from 'zod'; |
22 | 22 | ||
23 | import { BrowserViewBounds } from './BrowserViewBounds'; | ||
24 | import { ServiceAction } from './ServiceAction'; | 23 | import { ServiceAction } from './ServiceAction'; |
25 | import { ThemeSource } from './ThemeSource'; | 24 | import { ThemeSource } from './ThemeSource'; |
26 | 25 | ||
@@ -31,10 +30,6 @@ export const Action = /* @__PURE__ */ (() => | |||
31 | serviceId: z.string(), | 30 | serviceId: z.string(), |
32 | }), | 31 | }), |
33 | z.object({ | 32 | z.object({ |
34 | action: z.literal('set-browser-view-bounds'), | ||
35 | browserViewBounds: BrowserViewBounds, | ||
36 | }), | ||
37 | z.object({ | ||
38 | action: z.literal('set-theme-source'), | 33 | action: z.literal('set-theme-source'), |
39 | themeSource: ThemeSource, | 34 | themeSource: ThemeSource, |
40 | }), | 35 | }), |
diff --git a/packages/shared/src/schemas/ServiceAction.ts b/packages/shared/src/schemas/ServiceAction.ts index c0f07d6..9486aaf 100644 --- a/packages/shared/src/schemas/ServiceAction.ts +++ b/packages/shared/src/schemas/ServiceAction.ts | |||
@@ -20,9 +20,15 @@ | |||
20 | 20 | ||
21 | import { z } from 'zod'; | 21 | import { z } from 'zod'; |
22 | 22 | ||
23 | import { BrowserViewBounds } from './BrowserViewBounds'; | ||
24 | |||
23 | export const ServiceAction = /* @__PURE__ */ (() => | 25 | export const ServiceAction = /* @__PURE__ */ (() => |
24 | z.union([ | 26 | z.union([ |
25 | z.object({ | 27 | z.object({ |
28 | action: z.literal('set-browser-view-bounds'), | ||
29 | browserViewBounds: BrowserViewBounds, | ||
30 | }), | ||
31 | z.object({ | ||
26 | action: z.literal('back'), | 32 | action: z.literal('back'), |
27 | }), | 33 | }), |
28 | z.object({ | 34 | z.object({ |
diff --git a/packages/shared/src/stores/SharedStoreBase.ts b/packages/shared/src/stores/SharedStoreBase.ts index e4b3a38..949ef6a 100644 --- a/packages/shared/src/stores/SharedStoreBase.ts +++ b/packages/shared/src/stores/SharedStoreBase.ts | |||
@@ -65,7 +65,10 @@ export function defineSharedStoreModel< | |||
65 | return settings.showLocationBar || self.alwaysShowLocationBar; | 65 | return settings.showLocationBar || self.alwaysShowLocationBar; |
66 | }, | 66 | }, |
67 | get canToggleLocationBar(): boolean { | 67 | get canToggleLocationBar(): boolean { |
68 | return !self.alwaysShowLocationBar; | 68 | return ( |
69 | !self.alwaysShowLocationBar && | ||
70 | self.settings.selectedService !== undefined | ||
71 | ); | ||
69 | }, | 72 | }, |
70 | })); | 73 | })); |
71 | } | 74 | } |