aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts12
-rw-r--r--packages/main/src/infrastructure/electron/types.ts4
-rw-r--r--packages/main/src/stores/MainStore.ts21
-rw-r--r--packages/main/src/stores/Service.ts60
-rw-r--r--packages/renderer/src/components/locationBar/GoAdornment.tsx9
-rw-r--r--packages/renderer/src/components/locationBar/LocationTextField.tsx19
-rw-r--r--packages/renderer/src/components/locationBar/NavigationButtons.tsx16
-rw-r--r--packages/renderer/src/stores/Service.ts46
-rw-r--r--packages/shared/src/contextBridge/SophieRenderer.ts2
-rw-r--r--packages/shared/src/index.ts8
-rw-r--r--packages/shared/src/schemas/Action.ts (renamed from packages/shared/src/schemas.ts)29
-rw-r--r--packages/shared/src/schemas/BrowserViewBounds.ts35
-rw-r--r--packages/shared/src/schemas/ServiceAction.ts51
-rw-r--r--packages/shared/src/schemas/ThemeSource.ts29
-rw-r--r--packages/shared/src/stores/GlobalSettingsBase.ts2
15 files changed, 304 insertions, 39 deletions
diff --git a/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts b/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts
index 6ff8e21..e5fdf11 100644
--- a/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts
+++ b/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts
@@ -105,6 +105,18 @@ export default class ElectronServiceView implements ServiceView {
105 this.browserView.webContents.goForward(); 105 this.browserView.webContents.goForward();
106 } 106 }
107 107
108 reload(ignoreCache: boolean): void {
109 if (ignoreCache) {
110 this.browserView.webContents.reloadIgnoringCache();
111 } else {
112 this.browserView.webContents.reload();
113 }
114 }
115
116 stop(): void {
117 this.browserView.webContents.stop();
118 }
119
108 setBounds(bounds: BrowserViewBounds): void { 120 setBounds(bounds: BrowserViewBounds): void {
109 this.browserView.setBounds(bounds); 121 this.browserView.setBounds(bounds);
110 } 122 }
diff --git a/packages/main/src/infrastructure/electron/types.ts b/packages/main/src/infrastructure/electron/types.ts
index 9f03214..63974ce 100644
--- a/packages/main/src/infrastructure/electron/types.ts
+++ b/packages/main/src/infrastructure/electron/types.ts
@@ -61,6 +61,10 @@ export interface ServiceView {
61 61
62 goForward(): void; 62 goForward(): void;
63 63
64 reload(ignoreCache: boolean): void;
65
66 stop(): void;
67
64 setBounds(bounds: BrowserViewBounds): void; 68 setBounds(bounds: BrowserViewBounds): void;
65 69
66 dispose(): void; 70 dispose(): void;
diff --git a/packages/main/src/stores/MainStore.ts b/packages/main/src/stores/MainStore.ts
index cb4f4c9..bf351d7 100644
--- a/packages/main/src/stores/MainStore.ts
+++ b/packages/main/src/stores/MainStore.ts
@@ -74,9 +74,6 @@ const MainStore = types
74 }, 74 },
75 dispatch(action: Action): void { 75 dispatch(action: Action): void {
76 switch (action.action) { 76 switch (action.action) {
77 case 'reload-all-services':
78 // TODO
79 break;
80 case 'set-browser-view-bounds': 77 case 'set-browser-view-bounds':
81 this.setBrowserViewBounds(action.browserViewBounds); 78 this.setBrowserViewBounds(action.browserViewBounds);
82 break; 79 break;
@@ -89,6 +86,24 @@ const MainStore = types
89 case 'set-show-location-bar': 86 case 'set-show-location-bar':
90 self.settings.setShowLocationBar(action.showLocationBar); 87 self.settings.setShowLocationBar(action.showLocationBar);
91 break; 88 break;
89 case 'reload-all-services':
90 // TODO
91 break;
92 case 'dispatch-service-action': {
93 const { serviceId, serviceAction } = action;
94 const service = self.shared.servicesById.get(serviceId);
95 if (service === undefined) {
96 log.error(
97 'No such service',
98 serviceId,
99 'to dispatch action',
100 serviceAction,
101 );
102 } else {
103 service.dispatch(serviceAction);
104 }
105 break;
106 }
92 default: 107 default:
93 log.error('Unknown action to dispatch', action); 108 log.error('Unknown action to dispatch', action);
94 break; 109 break;
diff --git a/packages/main/src/stores/Service.ts b/packages/main/src/stores/Service.ts
index abef7c2..cbd8662 100644
--- a/packages/main/src/stores/Service.ts
+++ b/packages/main/src/stores/Service.ts
@@ -19,14 +19,17 @@
19 */ 19 */
20 20
21import type { UnreadCount } from '@sophie/service-shared'; 21import type { UnreadCount } from '@sophie/service-shared';
22import { defineServiceModel } from '@sophie/shared'; 22import { defineServiceModel, ServiceAction } from '@sophie/shared';
23import { Instance, getSnapshot } from 'mobx-state-tree'; 23import { Instance, getSnapshot } from 'mobx-state-tree';
24 24
25import type { ServiceView } from '../infrastructure/electron/types'; 25import type { ServiceView } from '../infrastructure/electron/types';
26import { getLogger } from '../utils/log';
26 27
27import ServiceSettings from './ServiceSettings'; 28import ServiceSettings from './ServiceSettings';
28import type ServiceConfig from './config/ServiceConfig'; 29import type ServiceConfig from './config/ServiceConfig';
29 30
31const log = getLogger('Service');
32
30const Service = defineServiceModel(ServiceSettings) 33const Service = defineServiceModel(ServiceSettings)
31 .views((self) => ({ 34 .views((self) => ({
32 get config(): ServiceConfig { 35 get config(): ServiceConfig {
@@ -87,6 +90,61 @@ const Service = defineServiceModel(ServiceSettings)
87 setServiceView(serviceView: ServiceView | undefined): void { 90 setServiceView(serviceView: ServiceView | undefined): void {
88 self.serviceView = serviceView; 91 self.serviceView = serviceView;
89 }, 92 },
93 goBack(): void {
94 self.serviceView?.goBack();
95 },
96 goForward(): void {
97 self.serviceView?.goForward();
98 },
99 reload(ignoreCache = false): void {
100 if (self.serviceView === undefined) {
101 this.startedLoading();
102 } else {
103 self.serviceView?.reload(ignoreCache);
104 }
105 },
106 stop(): void {
107 self.serviceView?.stop();
108 },
109 go(url: string): void {
110 if (self.serviceView === undefined) {
111 self.currentUrl = url;
112 this.startedLoading();
113 } else {
114 self.serviceView?.loadURL(url).catch((error) => {
115 log.warn('Error while loading', url, error);
116 this.crashed();
117 });
118 }
119 },
120 goHome(): void {
121 this.go(self.settings.url);
122 },
123 dispatch(action: ServiceAction): void {
124 switch (action.action) {
125 case 'back':
126 this.goBack();
127 break;
128 case 'forward':
129 this.goForward();
130 break;
131 case 'reload':
132 this.reload(action.ignoreCache);
133 break;
134 case 'stop':
135 this.stop();
136 break;
137 case 'go-home':
138 this.goHome();
139 break;
140 case 'go':
141 this.go(action.url);
142 break;
143 default:
144 log.error('Unknown action to dispatch', action);
145 break;
146 }
147 },
90 })); 148 }));
91 149
92/* 150/*
diff --git a/packages/renderer/src/components/locationBar/GoAdornment.tsx b/packages/renderer/src/components/locationBar/GoAdornment.tsx
index 43c8b7b..f049b8e 100644
--- a/packages/renderer/src/components/locationBar/GoAdornment.tsx
+++ b/packages/renderer/src/components/locationBar/GoAdornment.tsx
@@ -20,11 +20,15 @@
20 20
21import IconGo from '@mui/icons-material/Send'; 21import IconGo from '@mui/icons-material/Send';
22import Button from '@mui/material/Button'; 22import Button from '@mui/material/Button';
23import React from 'react'; 23import React, { MouseEventHandler } from 'react';
24 24
25import ButtonAdornment, { NO_LABEL_BUTTON_CLASS_NAME } from './ButtonAdornment'; 25import ButtonAdornment, { NO_LABEL_BUTTON_CLASS_NAME } from './ButtonAdornment';
26 26
27export default function GoAdornment(): JSX.Element { 27export default function GoAdornment({
28 onClick,
29}: {
30 onClick: MouseEventHandler<HTMLButtonElement>;
31}): JSX.Element {
28 return ( 32 return (
29 <ButtonAdornment position="end"> 33 <ButtonAdornment position="end">
30 <Button 34 <Button
@@ -32,6 +36,7 @@ export default function GoAdornment(): JSX.Element {
32 color="inherit" 36 color="inherit"
33 startIcon={<IconGo />} 37 startIcon={<IconGo />}
34 className={NO_LABEL_BUTTON_CLASS_NAME} 38 className={NO_LABEL_BUTTON_CLASS_NAME}
39 onClick={onClick}
35 /> 40 />
36 </ButtonAdornment> 41 </ButtonAdornment>
37 ); 42 );
diff --git a/packages/renderer/src/components/locationBar/LocationTextField.tsx b/packages/renderer/src/components/locationBar/LocationTextField.tsx
index e6da59f..e711abc 100644
--- a/packages/renderer/src/components/locationBar/LocationTextField.tsx
+++ b/packages/renderer/src/components/locationBar/LocationTextField.tsx
@@ -94,9 +94,18 @@ function LocationTextField({
94 setChanged(true); 94 setChanged(true);
95 }} 95 }}
96 onKeyUp={(event) => { 96 onKeyUp={(event) => {
97 if (event.key === 'Escape') { 97 switch (event.key) {
98 resetValue(); 98 case 'Escape':
99 event.preventDefault(); 99 resetValue();
100 event.preventDefault();
101 break;
102 case 'Enter':
103 service?.go(value);
104 event.preventDefault();
105 break;
106 default:
107 // Nothing to do, let the key event through.
108 break;
100 } 109 }
101 }} 110 }}
102 size="small" 111 size="small"
@@ -106,7 +115,9 @@ function LocationTextField({
106 startAdornment={ 115 startAdornment={
107 <UrlAdornment changed={changed} splitResult={splitResult} /> 116 <UrlAdornment changed={changed} splitResult={splitResult} />
108 } 117 }
109 endAdornment={changed ? <GoAdornment /> : undefined} 118 endAdornment={
119 changed ? <GoAdornment onClick={() => service?.go(value)} /> : undefined
120 }
110 value={value} 121 value={value}
111 /> 122 />
112 ); 123 );
diff --git a/packages/renderer/src/components/locationBar/NavigationButtons.tsx b/packages/renderer/src/components/locationBar/NavigationButtons.tsx
index ce59692..e71d3d8 100644
--- a/packages/renderer/src/components/locationBar/NavigationButtons.tsx
+++ b/packages/renderer/src/components/locationBar/NavigationButtons.tsx
@@ -48,25 +48,35 @@ function NavigationButtons({
48 <IconButton 48 <IconButton
49 aria-label="Back" 49 aria-label="Back"
50 disabled={service === undefined || !service.canGoBack} 50 disabled={service === undefined || !service.canGoBack}
51 onClick={() => service?.goBack()}
51 > 52 >
52 {direction === 'ltr' ? <IconArrowBack /> : <IconArrowForward />} 53 {direction === 'ltr' ? <IconArrowBack /> : <IconArrowForward />}
53 </IconButton> 54 </IconButton>
54 <IconButton 55 <IconButton
55 aria-label="Forward" 56 aria-label="Forward"
56 disabled={service === undefined || !service.canGoForward} 57 disabled={service === undefined || !service.canGoForward}
58 onClick={() => service?.goForward()}
57 > 59 >
58 {direction === 'ltr' ? <IconArrowForward /> : <IconArrowBack />} 60 {direction === 'ltr' ? <IconArrowForward /> : <IconArrowBack />}
59 </IconButton> 61 </IconButton>
60 {service?.state === 'loading' ? ( 62 {service?.state === 'loading' ? (
61 <IconButton aria-label="Stop"> 63 <IconButton aria-label="Stop" onClick={() => service?.stop()}>
62 <IconStop /> 64 <IconStop />
63 </IconButton> 65 </IconButton>
64 ) : ( 66 ) : (
65 <IconButton aria-label="Refresh" disabled={service === undefined}> 67 <IconButton
68 aria-label="Refresh"
69 disabled={service === undefined}
70 onClick={(event) => service?.reload(event.shiftKey)}
71 >
66 <IconRefresh /> 72 <IconRefresh />
67 </IconButton> 73 </IconButton>
68 )} 74 )}
69 <IconButton aria-label="Home" disabled={service === undefined}> 75 <IconButton
76 aria-label="Home"
77 disabled={service === undefined}
78 onClick={() => service?.goHome()}
79 >
70 <IconHome /> 80 <IconHome />
71 </IconButton> 81 </IconButton>
72 </Box> 82 </Box>
diff --git a/packages/renderer/src/stores/Service.ts b/packages/renderer/src/stores/Service.ts
index c2c938a..7878ea0 100644
--- a/packages/renderer/src/stores/Service.ts
+++ b/packages/renderer/src/stores/Service.ts
@@ -18,12 +18,54 @@
18 * SPDX-License-Identifier: AGPL-3.0-only 18 * SPDX-License-Identifier: AGPL-3.0-only
19 */ 19 */
20 20
21import { defineServiceModel } from '@sophie/shared'; 21import { defineServiceModel, ServiceAction } from '@sophie/shared';
22import { Instance } from 'mobx-state-tree'; 22import { Instance } from 'mobx-state-tree';
23 23
24import getEnv from '../env/getEnv';
25
24import ServiceSettings from './ServiceSettings'; 26import ServiceSettings from './ServiceSettings';
25 27
26const Service = defineServiceModel(ServiceSettings); 28const Service = defineServiceModel(ServiceSettings).actions((self) => ({
29 dispatch(serviceAction: ServiceAction): void {
30 getEnv(self).dispatchMainAction({
31 action: 'dispatch-service-action',
32 serviceId: self.id,
33 serviceAction,
34 });
35 },
36 goBack(): void {
37 this.dispatch({
38 action: 'back',
39 });
40 },
41 goForward(): void {
42 this.dispatch({
43 action: 'forward',
44 });
45 },
46 reload(ignoreCache = false): void {
47 this.dispatch({
48 action: 'reload',
49 ignoreCache,
50 });
51 },
52 stop(): void {
53 this.dispatch({
54 action: 'stop',
55 });
56 },
57 go(url: string): void {
58 this.dispatch({
59 action: 'go',
60 url,
61 });
62 },
63 goHome(): void {
64 this.dispatch({
65 action: 'go-home',
66 });
67 },
68}));
27 69
28/* 70/*
29 eslint-disable-next-line @typescript-eslint/no-redeclare -- 71 eslint-disable-next-line @typescript-eslint/no-redeclare --
diff --git a/packages/shared/src/contextBridge/SophieRenderer.ts b/packages/shared/src/contextBridge/SophieRenderer.ts
index 9e087da..dc77c97 100644
--- a/packages/shared/src/contextBridge/SophieRenderer.ts
+++ b/packages/shared/src/contextBridge/SophieRenderer.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
21import { Action } from '../schemas'; 21import { Action } from '../schemas/Action';
22import { SharedStoreListener } from '../stores/SharedStoreBase'; 22import { SharedStoreListener } from '../stores/SharedStoreBase';
23 23
24export default interface SophieRenderer { 24export default interface SophieRenderer {
diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts
index 66debf7..fa3fbfd 100644
--- a/packages/shared/src/index.ts
+++ b/packages/shared/src/index.ts
@@ -22,7 +22,13 @@ export type { default as SophieRenderer } from './contextBridge/SophieRenderer';
22 22
23export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc'; 23export { MainToRendererIpcMessage, RendererToMainIpcMessage } from './ipc';
24 24
25export { Action, BrowserViewBounds, ThemeSource } from './schemas'; 25export { Action } from './schemas/Action';
26
27export { BrowserViewBounds } from './schemas/BrowserViewBounds';
28
29export { ServiceAction } from './schemas/ServiceAction';
30
31export { ThemeSource } from './schemas/ThemeSource';
26 32
27export type { 33export type {
28 GlobalSettingsSnapshotIn, 34 GlobalSettingsSnapshotIn,
diff --git a/packages/shared/src/schemas.ts b/packages/shared/src/schemas/Action.ts
index 9d87d10..d5d0a8d 100644
--- a/packages/shared/src/schemas.ts
+++ b/packages/shared/src/schemas/Action.ts
@@ -20,27 +20,9 @@
20 20
21import { z } from 'zod'; 21import { z } from 'zod';
22 22
23export const BrowserViewBounds = /* @__PURE__ */ (() => 23import { BrowserViewBounds } from './BrowserViewBounds';
24 z.object({ 24import { ServiceAction } from './ServiceAction';
25 x: z.number().int().nonnegative(), 25import { ThemeSource } from './ThemeSource';
26 y: z.number().int().nonnegative(),
27 width: z.number().int().nonnegative(),
28 height: z.number().int().nonnegative(),
29 }))();
30
31/*
32 eslint-disable-next-line @typescript-eslint/no-redeclare --
33 Intentionally naming the type the same as the schema definition.
34*/
35export type BrowserViewBounds = z.infer<typeof BrowserViewBounds>;
36
37export const ThemeSource = /* @__PURE__ */ z.enum(['system', 'light', 'dark']);
38
39/*
40 eslint-disable-next-line @typescript-eslint/no-redeclare --
41 Intentionally naming the type the same as the schema definition.
42*/
43export type ThemeSource = z.infer<typeof ThemeSource>;
44 26
45export const Action = /* @__PURE__ */ (() => 27export const Action = /* @__PURE__ */ (() =>
46 z.union([ 28 z.union([
@@ -63,6 +45,11 @@ export const Action = /* @__PURE__ */ (() =>
63 z.object({ 45 z.object({
64 action: z.literal('reload-all-services'), 46 action: z.literal('reload-all-services'),
65 }), 47 }),
48 z.object({
49 action: z.literal('dispatch-service-action'),
50 serviceId: z.string(),
51 serviceAction: ServiceAction,
52 }),
66 ]))(); 53 ]))();
67 54
68/* 55/*
diff --git a/packages/shared/src/schemas/BrowserViewBounds.ts b/packages/shared/src/schemas/BrowserViewBounds.ts
new file mode 100644
index 0000000..e668a4a
--- /dev/null
+++ b/packages/shared/src/schemas/BrowserViewBounds.ts
@@ -0,0 +1,35 @@
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
21import { z } from 'zod';
22
23export const BrowserViewBounds = /* @__PURE__ */ (() =>
24 z.object({
25 x: z.number().int().nonnegative(),
26 y: z.number().int().nonnegative(),
27 width: z.number().int().nonnegative(),
28 height: z.number().int().nonnegative(),
29 }))();
30
31/*
32 eslint-disable-next-line @typescript-eslint/no-redeclare --
33 Intentionally naming the type the same as the schema definition.
34*/
35export type BrowserViewBounds = z.infer<typeof BrowserViewBounds>;
diff --git a/packages/shared/src/schemas/ServiceAction.ts b/packages/shared/src/schemas/ServiceAction.ts
new file mode 100644
index 0000000..a4a7049
--- /dev/null
+++ b/packages/shared/src/schemas/ServiceAction.ts
@@ -0,0 +1,51 @@
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
21import { z } from 'zod';
22
23export const ServiceAction = /* @__PURE__ */ (() =>
24 z.union([
25 z.object({
26 action: z.literal('back'),
27 }),
28 z.object({
29 action: z.literal('forward'),
30 }),
31 z.object({
32 action: z.literal('reload'),
33 ignoreCache: z.boolean(),
34 }),
35 z.object({
36 action: z.literal('stop'),
37 }),
38 z.object({
39 action: z.literal('go-home'),
40 }),
41 z.object({
42 action: z.literal('go'),
43 url: z.string(),
44 }),
45 ]))();
46
47/*
48 eslint-disable-next-line @typescript-eslint/no-redeclare --
49 Intentionally naming the type the same as the schema definition.
50*/
51export type ServiceAction = z.infer<typeof ServiceAction>;
diff --git a/packages/shared/src/schemas/ThemeSource.ts b/packages/shared/src/schemas/ThemeSource.ts
new file mode 100644
index 0000000..6db5788
--- /dev/null
+++ b/packages/shared/src/schemas/ThemeSource.ts
@@ -0,0 +1,29 @@
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
21import { z } from 'zod';
22
23export const ThemeSource = /* @__PURE__ */ z.enum(['system', 'light', 'dark']);
24
25/*
26 eslint-disable-next-line @typescript-eslint/no-redeclare --
27 Intentionally naming the type the same as the schema definition.
28*/
29export type ThemeSource = z.infer<typeof ThemeSource>;
diff --git a/packages/shared/src/stores/GlobalSettingsBase.ts b/packages/shared/src/stores/GlobalSettingsBase.ts
index 48092fd..1bd0628 100644
--- a/packages/shared/src/stores/GlobalSettingsBase.ts
+++ b/packages/shared/src/stores/GlobalSettingsBase.ts
@@ -26,7 +26,7 @@ import {
26 IAnyModelType, 26 IAnyModelType,
27} from 'mobx-state-tree'; 27} from 'mobx-state-tree';
28 28
29import { ThemeSource } from '../schemas'; 29import { ThemeSource } from '../schemas/ThemeSource';
30 30
31import ServiceBase from './ServiceBase'; 31import ServiceBase from './ServiceBase';
32 32