diff options
author | Kristóf Marussy <kristof@marussy.com> | 2022-03-14 20:06:52 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2022-05-16 00:54:54 +0200 |
commit | 1743621c3c2b0474be81276390fa45399105a4af (patch) | |
tree | 8b2eadd937fa13816a8702cd2873daced241f66c /packages | |
parent | feat(renderer): Update window title (diff) | |
download | sophie-1743621c3c2b0474be81276390fa45399105a4af.tar.gz sophie-1743621c3c2b0474be81276390fa45399105a4af.tar.zst sophie-1743621c3c2b0474be81276390fa45399105a4af.zip |
feat(renderer): Show service error on service icon
Service icons fade out on errors and an icon is displayed.
Signed-off-by: Kristóf Marussy <kristof@marussy.com>
Diffstat (limited to 'packages')
-rw-r--r-- | packages/renderer/src/components/sidebar/ServiceIcon.tsx | 71 |
1 files changed, 52 insertions, 19 deletions
diff --git a/packages/renderer/src/components/sidebar/ServiceIcon.tsx b/packages/renderer/src/components/sidebar/ServiceIcon.tsx index fe047cf..b8f9b96 100644 --- a/packages/renderer/src/components/sidebar/ServiceIcon.tsx +++ b/packages/renderer/src/components/sidebar/ServiceIcon.tsx | |||
@@ -18,6 +18,7 @@ | |||
18 | * SPDX-License-Identifier: AGPL-3.0-only | 18 | * SPDX-License-Identifier: AGPL-3.0-only |
19 | */ | 19 | */ |
20 | 20 | ||
21 | import IconWarning from '@mui/icons-material/Warning'; | ||
21 | import Badge from '@mui/material/Badge'; | 22 | import Badge from '@mui/material/Badge'; |
22 | import { styled, useTheme } from '@mui/material/styles'; | 23 | import { styled, useTheme } from '@mui/material/styles'; |
23 | import { observer } from 'mobx-react-lite'; | 24 | import { observer } from 'mobx-react-lite'; |
@@ -28,7 +29,8 @@ import type Service from '../../stores/Service'; | |||
28 | const ServiceIconRoot = styled('div', { | 29 | const ServiceIconRoot = styled('div', { |
29 | name: 'ServiceIcon', | 30 | name: 'ServiceIcon', |
30 | slot: 'Root', | 31 | slot: 'Root', |
31 | })(({ theme }) => ({ | 32 | shouldForwardProp: (prop) => prop !== 'hasError', |
33 | })<{ hasError: boolean }>(({ theme, hasError }) => ({ | ||
32 | width: 36, | 34 | width: 36, |
33 | height: 36, | 35 | height: 36, |
34 | borderRadius: theme.shape.borderRadius, | 36 | borderRadius: theme.shape.borderRadius, |
@@ -36,6 +38,13 @@ const ServiceIconRoot = styled('div', { | |||
36 | display: 'flex', | 38 | display: 'flex', |
37 | justifyContent: 'center', | 39 | justifyContent: 'center', |
38 | alignItems: 'center', | 40 | alignItems: 'center', |
41 | filter: hasError ? 'grayscale(100%)' : 'none', | ||
42 | opacity: hasError ? theme.palette.action.disabledOpacity : 1, | ||
43 | transition: theme.transitions.create(['filter', 'opacity'], { | ||
44 | duration: hasError | ||
45 | ? theme.transitions.duration.enteringScreen | ||
46 | : theme.transitions.duration.leavingScreen, | ||
47 | }), | ||
39 | })); | 48 | })); |
40 | 49 | ||
41 | const ServiceIconText = styled('div', { | 50 | const ServiceIconText = styled('div', { |
@@ -48,15 +57,18 @@ const ServiceIconText = styled('div', { | |||
48 | color: theme.palette.primary.contrastText, | 57 | color: theme.palette.primary.contrastText, |
49 | })); | 58 | })); |
50 | 59 | ||
51 | const ServiceIconBadge = styled(Badge, { | 60 | const ServiceIconBadgeBase = styled(Badge)({ |
52 | name: 'ServiceIcon', | 61 | '& > .MuiBadge-badge': { |
53 | slot: 'Badge', | ||
54 | })(({ theme }) => ({ | ||
55 | '.MuiBadge-badge': { | ||
56 | // Place badge above the sidebar inner shadow. | 62 | // Place badge above the sidebar inner shadow. |
57 | zIndex: 200, | 63 | zIndex: 200, |
58 | }, | 64 | }, |
59 | '.MuiBadge-dot': { | 65 | }); |
66 | |||
67 | const ServiceIconBadge = styled(ServiceIconBadgeBase, { | ||
68 | name: 'ServiceIcon', | ||
69 | slot: 'Badge', | ||
70 | })(({ theme }) => ({ | ||
71 | '& > .MuiBadge-dot': { | ||
60 | background: | 72 | background: |
61 | theme.palette.mode === 'dark' | 73 | theme.palette.mode === 'dark' |
62 | ? theme.palette.text.primary | 74 | ? theme.palette.text.primary |
@@ -64,15 +76,28 @@ const ServiceIconBadge = styled(Badge, { | |||
64 | }, | 76 | }, |
65 | })); | 77 | })); |
66 | 78 | ||
79 | const ServiceIconErrorBadge = styled(ServiceIconBadgeBase, { | ||
80 | name: 'ServiceIcon', | ||
81 | slot: 'ErrorBadge', | ||
82 | })(({ theme }) => ({ | ||
83 | '& > .MuiBadge-standard': { | ||
84 | color: | ||
85 | theme.palette.mode === 'dark' | ||
86 | ? theme.palette.error.light | ||
87 | : theme.palette.error.main, | ||
88 | }, | ||
89 | })); | ||
90 | |||
67 | function ServiceIcon({ service }: { service: Service }): JSX.Element { | 91 | function ServiceIcon({ service }: { service: Service }): JSX.Element { |
68 | const { direction } = useTheme(); | 92 | const { direction } = useTheme(); |
69 | const { | 93 | const { |
70 | settings: { name }, | 94 | settings: { name }, |
71 | directMessageCount, | 95 | directMessageCount, |
72 | indirectMessageCount, | 96 | indirectMessageCount, |
97 | hasError, | ||
73 | } = service; | 98 | } = service; |
74 | 99 | ||
75 | // Hadge color histeresis for smooth appear / disappear animation. | 100 | // Badge color histeresis for smooth appear / disappear animation. |
76 | // If we compute hasDirectMessage = directMessageCount >= 1 directly (without any histeresis), | 101 | // If we compute hasDirectMessage = directMessageCount >= 1 directly (without any histeresis), |
77 | // the badge momentarily turns light during the disappear animation. | 102 | // the badge momentarily turns light during the disappear animation. |
78 | const [hasDirectMessage, setHasDirectMessage] = useState(false); | 103 | const [hasDirectMessage, setHasDirectMessage] = useState(false); |
@@ -85,21 +110,29 @@ function ServiceIcon({ service }: { service: Service }): JSX.Element { | |||
85 | }, [directMessageCount, indirectMessageCount, setHasDirectMessage]); | 110 | }, [directMessageCount, indirectMessageCount, setHasDirectMessage]); |
86 | 111 | ||
87 | return ( | 112 | return ( |
88 | <ServiceIconBadge | 113 | <ServiceIconErrorBadge |
89 | badgeContent={ | 114 | badgeContent={hasError ? <IconWarning fontSize="small" /> : 0} |
90 | hasDirectMessage ? directMessageCount : indirectMessageCount | ||
91 | } | ||
92 | variant={hasDirectMessage ? 'standard' : 'dot'} | ||
93 | color="error" | ||
94 | anchorOrigin={{ | 115 | anchorOrigin={{ |
95 | vertical: 'top', | 116 | vertical: 'bottom', |
96 | horizontal: direction === 'ltr' ? 'right' : 'left', | 117 | horizontal: direction === 'ltr' ? 'right' : 'left', |
97 | }} | 118 | }} |
98 | > | 119 | > |
99 | <ServiceIconRoot> | 120 | <ServiceIconBadge |
100 | <ServiceIconText>{name.length > 0 ? name[0] : '?'}</ServiceIconText> | 121 | badgeContent={ |
101 | </ServiceIconRoot> | 122 | hasDirectMessage ? directMessageCount : indirectMessageCount |
102 | </ServiceIconBadge> | 123 | } |
124 | variant={hasDirectMessage ? 'standard' : 'dot'} | ||
125 | color="error" | ||
126 | anchorOrigin={{ | ||
127 | vertical: 'top', | ||
128 | horizontal: direction === 'ltr' ? 'right' : 'left', | ||
129 | }} | ||
130 | > | ||
131 | <ServiceIconRoot hasError={hasError}> | ||
132 | <ServiceIconText>{name.length > 0 ? name[0] : '?'}</ServiceIconText> | ||
133 | </ServiceIconRoot> | ||
134 | </ServiceIconBadge> | ||
135 | </ServiceIconErrorBadge> | ||
103 | ); | 136 | ); |
104 | } | 137 | } |
105 | 138 | ||