aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts36
-rw-r--r--packages/main/src/infrastructure/electron/types.ts2
-rw-r--r--packages/main/src/reactions/loadServices.ts51
-rw-r--r--packages/main/src/stores/MainStore.ts2
-rw-r--r--packages/main/src/stores/Service.ts31
-rw-r--r--packages/renderer/src/components/locationBar/NavigationButtons.tsx2
-rw-r--r--packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx2
-rw-r--r--packages/shared/src/stores/ServiceBase.ts62
8 files changed, 121 insertions, 67 deletions
diff --git a/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts b/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts
index e5fdf11..d90ff19 100644
--- a/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts
+++ b/packages/main/src/infrastructure/electron/impl/ElectronServiceView.ts
@@ -22,12 +22,15 @@ import type { BrowserViewBounds } from '@sophie/shared';
22import { BrowserView } from 'electron'; 22import { BrowserView } from 'electron';
23 23
24import type Service from '../../../stores/Service'; 24import type Service from '../../../stores/Service';
25import { getLogger } from '../../../utils/log';
25import type Resources from '../../resources/Resources'; 26import type Resources from '../../resources/Resources';
26import type { ServiceView } from '../types'; 27import type { ServiceView } from '../types';
27 28
28import ElectronPartition from './ElectronPartition'; 29import ElectronPartition from './ElectronPartition';
29import type ElectronViewFactory from './ElectronViewFactory'; 30import type ElectronViewFactory from './ElectronViewFactory';
30 31
32const log = getLogger('ElectronServiceView');
33
31export default class ElectronServiceView implements ServiceView { 34export default class ElectronServiceView implements ServiceView {
32 readonly id: string; 35 readonly id: string;
33 36
@@ -72,20 +75,39 @@ export default class ElectronServiceView implements ServiceView {
72 } 75 }
73 }); 76 });
74 77
78 webContents.on(
79 'did-fail-load',
80 (_event, errorCode, errorDesc, url, isMainFrame) => {
81 if (isMainFrame) {
82 setLocation(url);
83 service.setFailed(errorCode, errorDesc);
84 log.warn(
85 'Failed to load',
86 url,
87 'in service',
88 this.id,
89 errorCode,
90 errorDesc,
91 );
92 }
93 },
94 );
95
75 webContents.on('page-title-updated', (_event, title) => { 96 webContents.on('page-title-updated', (_event, title) => {
76 service.setTitle(title); 97 service.setTitle(title);
77 }); 98 });
78 99
79 webContents.on('did-start-loading', () => { 100 webContents.on('did-start-loading', () => {
80 service.startedLoading(); 101 service.startLoading();
81 }); 102 });
82 103
83 webContents.on('did-stop-loading', () => { 104 webContents.on('did-stop-loading', () => {
84 service.finishedLoading(); 105 service.finishLoading();
85 }); 106 });
86 107
87 webContents.on('render-process-gone', () => { 108 webContents.on('render-process-gone', (_event, details) => {
88 service.crashed(); 109 const { reason, exitCode } = details;
110 service.setCrashed(reason, exitCode);
89 }); 111 });
90 } 112 }
91 113
@@ -93,8 +115,10 @@ export default class ElectronServiceView implements ServiceView {
93 return this.browserView.webContents.id; 115 return this.browserView.webContents.id;
94 } 116 }
95 117
96 loadURL(url: string): Promise<void> { 118 loadURL(url: string): void {
97 return this.browserView.webContents.loadURL(url); 119 this.browserView.webContents.loadURL(url).catch((error) => {
120 log.warn('Error while loading', url, 'in service', this.id, error);
121 });
98 } 122 }
99 123
100 goBack(): void { 124 goBack(): void {
diff --git a/packages/main/src/infrastructure/electron/types.ts b/packages/main/src/infrastructure/electron/types.ts
index 63974ce..7b04a6b 100644
--- a/packages/main/src/infrastructure/electron/types.ts
+++ b/packages/main/src/infrastructure/electron/types.ts
@@ -55,7 +55,7 @@ export interface ServiceView {
55 55
56 readonly partitionId: string; 56 readonly partitionId: string;
57 57
58 loadURL(url: string): Promise<void>; 58 loadURL(url: string): void;
59 59
60 goBack(): void; 60 goBack(): void;
61 61
diff --git a/packages/main/src/reactions/loadServices.ts b/packages/main/src/reactions/loadServices.ts
index 533ef95..4ef6131 100644
--- a/packages/main/src/reactions/loadServices.ts
+++ b/packages/main/src/reactions/loadServices.ts
@@ -78,36 +78,29 @@ export default function loadServices(
78 78
79 const viewsToDispose = new Map(servicesToViews); 79 const viewsToDispose = new Map(servicesToViews);
80 servicesById.forEach((service, serviceId) => { 80 servicesById.forEach((service, serviceId) => {
81 if (service.shouldBeLoaded) { 81 if (!service.shouldBeLoaded) {
82 let view = servicesToViews.get(serviceId); 82 return;
83 const { 83 }
84 settings: { 84 let view = servicesToViews.get(serviceId);
85 profile: { id: profileId }, 85 const {
86 }, 86 settings: {
87 } = service; 87 profile: { id: profileId },
88 if (view === undefined || view.partitionId !== profileId) { 88 },
89 log.debug('Creating view for service', serviceId); 89 } = service;
90 const partition = profilesToPartitions.get(profileId); 90 if (view === undefined || view.partitionId !== profileId) {
91 if (partition === undefined) { 91 log.debug('Creating view for service', serviceId);
92 throw new Error(`Missing Partition ${profileId}`); 92 const partition = profilesToPartitions.get(profileId);
93 } 93 if (partition === undefined) {
94 view = viewFactory.createServiceView(service, partition); 94 throw new Error(`Missing Partition ${profileId}`);
95 view.setBounds(store.browserViewBounds);
96 servicesToViews.set(serviceId, view);
97 service.setServiceView(view);
98 const { urlToLoad } = service;
99 view.loadURL(urlToLoad).catch((error) => {
100 log.warn(
101 'Cannot URL',
102 urlToLoad,
103 'for service',
104 serviceId,
105 error,
106 );
107 });
108 } else {
109 viewsToDispose.delete(serviceId);
110 } 95 }
96 view = viewFactory.createServiceView(service, partition);
97 view.setBounds(store.browserViewBounds);
98 servicesToViews.set(serviceId, view);
99 service.setServiceView(view);
100 const { urlToLoad } = service;
101 view.loadURL(urlToLoad);
102 } else {
103 viewsToDispose.delete(serviceId);
111 } 104 }
112 }); 105 });
113 106
diff --git a/packages/main/src/stores/MainStore.ts b/packages/main/src/stores/MainStore.ts
index 21d7a63..dedc740 100644
--- a/packages/main/src/stores/MainStore.ts
+++ b/packages/main/src/stores/MainStore.ts
@@ -56,7 +56,7 @@ const MainStore = types
56 }, 56 },
57 get visibleService(): Service | undefined { 57 get visibleService(): Service | undefined {
58 const { selectedService } = this.settings; 58 const { selectedService } = this.settings;
59 return selectedService !== undefined && selectedService.shouldBeLoaded 59 return selectedService !== undefined && selectedService.shouldBeVisible
60 ? selectedService 60 ? selectedService
61 : undefined; 61 : undefined;
62 }, 62 },
diff --git a/packages/main/src/stores/Service.ts b/packages/main/src/stores/Service.ts
index cbd8662..d98e52e 100644
--- a/packages/main/src/stores/Service.ts
+++ b/packages/main/src/stores/Service.ts
@@ -40,7 +40,10 @@ const Service = defineServiceModel(ServiceSettings)
40 return self.currentUrl ?? self.settings.url; 40 return self.currentUrl ?? self.settings.url;
41 }, 41 },
42 get shouldBeLoaded(): boolean { 42 get shouldBeLoaded(): boolean {
43 return self.state !== 'crashed'; 43 return !self.crashed;
44 },
45 get shouldBeVisible(): boolean {
46 return this.shouldBeLoaded && !self.failed;
44 }, 47 },
45 })) 48 }))
46 .volatile( 49 .volatile(
@@ -67,17 +70,20 @@ const Service = defineServiceModel(ServiceSettings)
67 setTitle(title: string): void { 70 setTitle(title: string): void {
68 self.title = title; 71 self.title = title;
69 }, 72 },
70 startedLoading(): void { 73 startLoading(): void {
71 self.state = 'loading'; 74 self.state = { type: 'loading' };
72 }, 75 },
73 finishedLoading(): void { 76 finishLoading(): void {
74 if (self.state === 'loading') { 77 if (self.loading) {
75 // Do not overwrite crashed state if the service haven't been reloaded yet. 78 // Do not overwrite crashed state if the service haven't been reloaded yet.
76 self.state = 'loaded'; 79 self.state = { type: 'loaded' };
77 } 80 }
78 }, 81 },
79 crashed(): void { 82 setFailed(errorCode: number, errorDesc: string): void {
80 self.state = 'crashed'; 83 self.state = { type: 'failed', errorCode, errorDesc };
84 },
85 setCrashed(reason: string, exitCode: number): void {
86 self.state = { type: 'crashed', reason, exitCode };
81 }, 87 },
82 setUnreadCount({ direct, indirect }: UnreadCount): void { 88 setUnreadCount({ direct, indirect }: UnreadCount): void {
83 if (direct !== undefined) { 89 if (direct !== undefined) {
@@ -98,7 +104,7 @@ const Service = defineServiceModel(ServiceSettings)
98 }, 104 },
99 reload(ignoreCache = false): void { 105 reload(ignoreCache = false): void {
100 if (self.serviceView === undefined) { 106 if (self.serviceView === undefined) {
101 this.startedLoading(); 107 self.state = { type: 'initializing' };
102 } else { 108 } else {
103 self.serviceView?.reload(ignoreCache); 109 self.serviceView?.reload(ignoreCache);
104 } 110 }
@@ -109,12 +115,9 @@ const Service = defineServiceModel(ServiceSettings)
109 go(url: string): void { 115 go(url: string): void {
110 if (self.serviceView === undefined) { 116 if (self.serviceView === undefined) {
111 self.currentUrl = url; 117 self.currentUrl = url;
112 this.startedLoading(); 118 self.state = { type: 'initializing' };
113 } else { 119 } else {
114 self.serviceView?.loadURL(url).catch((error) => { 120 self.serviceView?.loadURL(url);
115 log.warn('Error while loading', url, error);
116 this.crashed();
117 });
118 } 121 }
119 }, 122 },
120 goHome(): void { 123 goHome(): void {
diff --git a/packages/renderer/src/components/locationBar/NavigationButtons.tsx b/packages/renderer/src/components/locationBar/NavigationButtons.tsx
index e71d3d8..5c5c959 100644
--- a/packages/renderer/src/components/locationBar/NavigationButtons.tsx
+++ b/packages/renderer/src/components/locationBar/NavigationButtons.tsx
@@ -59,7 +59,7 @@ function NavigationButtons({
59 > 59 >
60 {direction === 'ltr' ? <IconArrowForward /> : <IconArrowBack />} 60 {direction === 'ltr' ? <IconArrowForward /> : <IconArrowBack />}
61 </IconButton> 61 </IconButton>
62 {service?.state === 'loading' ? ( 62 {service?.loading ?? false ? (
63 <IconButton aria-label="Stop" onClick={() => service?.stop()}> 63 <IconButton aria-label="Stop" onClick={() => service?.stop()}>
64 <IconStop /> 64 <IconStop />
65 </IconButton> 65 </IconButton>
diff --git a/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx b/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx
index d2f0745..57b17e9 100644
--- a/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx
+++ b/packages/renderer/src/components/sidebar/ToggleLocationBarButton.tsx
@@ -56,7 +56,7 @@ function ToggleLocationBarButton(): JSX.Element {
56 onClick={() => settings.toggleLocationBar()} 56 onClick={() => settings.toggleLocationBar()}
57 > 57 >
58 <ToggleLocationBarIcon 58 <ToggleLocationBarIcon
59 loading={selectedService?.state === 'loading'} 59 loading={selectedService?.loading ?? false}
60 show={showLocationBar} 60 show={showLocationBar}
61 /> 61 />
62 </IconButton> 62 </IconButton>
diff --git a/packages/shared/src/stores/ServiceBase.ts b/packages/shared/src/stores/ServiceBase.ts
index cde403b..4a17bc5 100644
--- a/packages/shared/src/stores/ServiceBase.ts
+++ b/packages/shared/src/stores/ServiceBase.ts
@@ -23,20 +23,54 @@ import { IAnyModelType, Instance, types } from 'mobx-state-tree';
23import ServiceSettingsBase from './ServiceSettingsBase'; 23import ServiceSettingsBase from './ServiceSettingsBase';
24 24
25export function defineServiceModel<TS extends IAnyModelType>(settings: TS) { 25export function defineServiceModel<TS extends IAnyModelType>(settings: TS) {
26 return types.model('Service', { 26 return types
27 id: types.identifier, 27 .model('Service', {
28 settings, 28 id: types.identifier,
29 currentUrl: types.maybe(types.string), 29 settings,
30 canGoBack: false, 30 currentUrl: types.maybe(types.string),
31 canGoForward: false, 31 canGoBack: false,
32 title: types.maybe(types.string), 32 canGoForward: false,
33 state: types.optional( 33 title: types.maybe(types.string),
34 types.enumeration('ServiceState', ['loading', 'loaded', 'crashed']), 34 state: types.optional(
35 'loading', 35 types.union(
36 ), 36 types.model({
37 directMessageCount: 0, 37 type: types.literal('initializing'),
38 indirectMessageCount: 0, 38 }),
39 }); 39 types.model({
40 type: types.literal('loading'),
41 }),
42 types.model({
43 type: types.literal('loaded'),
44 }),
45 types.model({
46 type: types.literal('failed'),
47 errorCode: types.integer,
48 errorDesc: types.string,
49 }),
50 types.model({
51 type: types.literal('crashed'),
52 reason: types.string,
53 exitCode: types.integer,
54 }),
55 ),
56 { type: 'initializing' },
57 ),
58 directMessageCount: 0,
59 indirectMessageCount: 0,
60 })
61 .views((self) => ({
62 get loading(): boolean {
63 return (
64 self.state.type === 'initializing' || self.state.type === 'loading'
65 );
66 },
67 get failed(): boolean {
68 return self.state.type === 'failed';
69 },
70 get crashed(): boolean {
71 return self.state.type === 'crashed';
72 },
73 }));
40} 74}
41 75
42const ServiceBase = /* @__PURE__ */ (() => 76const ServiceBase = /* @__PURE__ */ (() =>