diff options
Diffstat (limited to 'packages/renderer/src')
-rw-r--r-- | packages/renderer/src/components/App.tsx | 66 | ||||
-rw-r--r-- | packages/renderer/src/components/WindowTitle.tsx | 63 |
2 files changed, 86 insertions, 43 deletions
diff --git a/packages/renderer/src/components/App.tsx b/packages/renderer/src/components/App.tsx index b0686a9..86e2a55 100644 --- a/packages/renderer/src/components/App.tsx +++ b/packages/renderer/src/components/App.tsx | |||
@@ -19,15 +19,31 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | import Box from '@mui/material/Box'; | 21 | import Box from '@mui/material/Box'; |
22 | import { styled } from '@mui/material/styles'; | ||
22 | import { observer } from 'mobx-react-lite'; | 23 | import { observer } from 'mobx-react-lite'; |
23 | import React, { useCallback, useEffect } from 'react'; | 24 | import React, { useCallback } from 'react'; |
24 | import { useTranslation } from 'react-i18next'; | ||
25 | 25 | ||
26 | import type RendererStore from '../stores/RendererStore'; | 26 | import type RendererStore from '../stores/RendererStore'; |
27 | 27 | ||
28 | import ServicePanel from './ServicePanel'; | 28 | import ServicePanel from './ServicePanel'; |
29 | import WindowTitle from './WindowTitle'; | ||
29 | import Sidebar from './sidebar/Sidebar'; | 30 | import Sidebar from './sidebar/Sidebar'; |
30 | 31 | ||
32 | const AppRoot = styled(Box)({ | ||
33 | display: 'flex', | ||
34 | overflow: 'hidden', | ||
35 | flexDirection: 'row', | ||
36 | alignItems: 'stretch', | ||
37 | height: '100vh', | ||
38 | width: '100vw', | ||
39 | }); | ||
40 | |||
41 | const AppServiceArea = styled(Box)({ | ||
42 | flex: 1, | ||
43 | height: '100%', | ||
44 | position: 'relative', | ||
45 | }); | ||
46 | |||
31 | function App({ | 47 | function App({ |
32 | store, | 48 | store, |
33 | devMode, | 49 | devMode, |
@@ -35,33 +51,10 @@ function App({ | |||
35 | store: RendererStore; | 51 | store: RendererStore; |
36 | devMode: boolean; | 52 | devMode: boolean; |
37 | }): JSX.Element { | 53 | }): JSX.Element { |
38 | const { ready, t } = useTranslation(undefined, { | ||
39 | useSuspense: false, | ||
40 | }); | ||
41 | const { | 54 | const { |
42 | settings: { selectedService }, | 55 | settings: { selectedService }, |
43 | shared: { services }, | 56 | shared: { services }, |
44 | } = store; | 57 | } = store; |
45 | const { | ||
46 | settings: { name: serviceName }, | ||
47 | title: serviceTitle, | ||
48 | } = selectedService ?? { settings: { name: undefined }, title: undefined }; | ||
49 | |||
50 | useEffect(() => { | ||
51 | if (!ready) { | ||
52 | // Only set title once the translations have been loaded. | ||
53 | return; | ||
54 | } | ||
55 | let title: string; | ||
56 | if (serviceName === undefined) { | ||
57 | title = t('title.noServiceName'); | ||
58 | } else if (serviceTitle === undefined) { | ||
59 | title = t('title.withServiceName', { serviceName }); | ||
60 | } else { | ||
61 | title = t('title.withServiceNameAndTitle', { serviceName, serviceTitle }); | ||
62 | } | ||
63 | document.title = devMode ? t('title.devMode', { title }) : title; | ||
64 | }, [devMode, ready, serviceName, serviceTitle, t]); | ||
65 | 58 | ||
66 | const handleBackForwardMouseButtons = useCallback( | 59 | const handleBackForwardMouseButtons = useCallback( |
67 | (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => { | 60 | (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => { |
@@ -83,31 +76,18 @@ function App({ | |||
83 | ); | 76 | ); |
84 | 77 | ||
85 | return ( | 78 | return ( |
86 | <Box | 79 | <AppRoot |
87 | onClick={handleBackForwardMouseButtons} | 80 | onClick={handleBackForwardMouseButtons} |
88 | onAuxClick={handleBackForwardMouseButtons} | 81 | onAuxClick={handleBackForwardMouseButtons} |
89 | sx={{ | ||
90 | display: 'flex', | ||
91 | overflow: 'hidden', | ||
92 | flexDirection: 'row', | ||
93 | alignItems: 'stretch', | ||
94 | height: '100vh', | ||
95 | width: '100vw', | ||
96 | }} | ||
97 | > | 82 | > |
83 | <WindowTitle selectedService={selectedService} devMode={devMode} /> | ||
98 | <Sidebar store={store} /> | 84 | <Sidebar store={store} /> |
99 | <Box | 85 | <AppServiceArea> |
100 | sx={{ | ||
101 | flex: 1, | ||
102 | height: '100%', | ||
103 | position: 'relative', | ||
104 | }} | ||
105 | > | ||
106 | {services.map((service) => ( | 86 | {services.map((service) => ( |
107 | <ServicePanel key={service.id} store={store} service={service} /> | 87 | <ServicePanel key={service.id} store={store} service={service} /> |
108 | ))} | 88 | ))} |
109 | </Box> | 89 | </AppServiceArea> |
110 | </Box> | 90 | </AppRoot> |
111 | ); | 91 | ); |
112 | } | 92 | } |
113 | 93 | ||
diff --git a/packages/renderer/src/components/WindowTitle.tsx b/packages/renderer/src/components/WindowTitle.tsx new file mode 100644 index 0000000..95e0fe6 --- /dev/null +++ b/packages/renderer/src/components/WindowTitle.tsx | |||
@@ -0,0 +1,63 @@ | |||
1 | /* | ||
2 | * Copyright (C) 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 { observer } from 'mobx-react-lite'; | ||
22 | import { useEffect } from 'react'; | ||
23 | import { useTranslation } from 'react-i18next'; | ||
24 | |||
25 | import type Service from '../stores/Service'; | ||
26 | |||
27 | function WindowTitle({ | ||
28 | selectedService, | ||
29 | devMode, | ||
30 | }: { | ||
31 | selectedService: Service | undefined; | ||
32 | devMode: boolean; | ||
33 | }): null { | ||
34 | const { ready, t } = useTranslation(undefined, { | ||
35 | useSuspense: false, | ||
36 | }); | ||
37 | |||
38 | const { | ||
39 | settings: { name: serviceName }, | ||
40 | title: serviceTitle, | ||
41 | } = selectedService ?? { settings: { name: undefined }, title: undefined }; | ||
42 | |||
43 | useEffect(() => { | ||
44 | if (!ready) { | ||
45 | // Only set title once the translations have been loaded. | ||
46 | return; | ||
47 | } | ||
48 | let title: string; | ||
49 | if (serviceName === undefined) { | ||
50 | title = t('title.noServiceName'); | ||
51 | } else if (serviceTitle === undefined) { | ||
52 | title = t('title.withServiceName', { serviceName }); | ||
53 | } else { | ||
54 | title = t('title.withServiceNameAndTitle', { serviceName, serviceTitle }); | ||
55 | } | ||
56 | document.title = devMode ? t('title.devMode', { title }) : title; | ||
57 | }, [devMode, ready, serviceName, serviceTitle, t]); | ||
58 | |||
59 | // eslint-disable-next-line unicorn/no-null -- React requires `null` to skip rendering. | ||
60 | return null; | ||
61 | } | ||
62 | |||
63 | export default observer(WindowTitle); | ||