aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Markus Hatvan <markus_hatvan@aon.at>2021-08-10 19:04:54 +0200
committerLibravatar GitHub <noreply@github.com>2021-08-10 22:34:54 +0530
commit969eda02a66050cf4518ddfa657e86d1d6d8b6c3 (patch)
tree9f21b062f0c188f2c3ddfbb6594670982610aadf /src
parentrefactor: Move platform-specific logic for shortcut keys into common location. (diff)
downloadferdium-app-969eda02a66050cf4518ddfa657e86d1d6d8b6c3.tar.gz
ferdium-app-969eda02a66050cf4518ddfa657e86d1d6d8b6c3.tar.zst
ferdium-app-969eda02a66050cf4518ddfa657e86d1d6d8b6c3.zip
feat: follow OS reduced motion setting (#1757)
- add missing meta charset to index.html - dont restrict scaling for user in index.html - load animations.css conditionally based on motion preference - load transitions conditionally in js and css based on motion preference Co-authored-by: Vijay Raghavan Aravamudhan <vraravam@users.noreply.github.com>
Diffstat (limited to 'src')
-rw-r--r--src/components/auth/SetupAssistant.js110
-rw-r--r--src/components/layout/AppLayout.js86
-rw-r--r--src/components/services/content/ConnectionLostBanner.js36
-rw-r--r--src/components/services/tabs/TabItem.js193
-rw-r--r--src/components/ui/AppLoader/styles.js8
-rw-r--r--src/features/webControls/components/WebControls.js65
-rw-r--r--src/features/workspaces/components/WorkspaceDrawerItem.js47
-rw-r--r--src/features/workspaces/components/WorkspaceSwitchingIndicator.js23
-rw-r--r--src/i18n/locales/defaultMessages.json46
-rw-r--r--src/i18n/messages/src/components/auth/SetupAssistant.json10
-rw-r--r--src/i18n/messages/src/components/layout/AppLayout.json2
-rw-r--r--src/i18n/messages/src/components/services/content/ConnectionLostBanner.json12
-rw-r--r--src/i18n/messages/src/components/services/tabs/TabItem.json2
-rw-r--r--src/i18n/messages/src/features/webControls/components/WebControls.json20
-rw-r--r--src/index.html122
-rw-r--r--src/internal-server/public/css/main.css11
-rw-r--r--src/styles/button.scss91
-rw-r--r--src/styles/content-tabs.scss29
-rw-r--r--src/styles/fonts.scss6
-rw-r--r--src/styles/image-upload.scss32
-rw-r--r--src/styles/layout.scss70
-rw-r--r--src/styles/main.scss1
-rw-r--r--src/styles/radio.scss19
-rw-r--r--src/styles/recipes.scss36
-rw-r--r--src/styles/settings.scss213
-rw-r--r--src/styles/slider.scss29
-rw-r--r--src/styles/tabs.scss58
-rw-r--r--src/styles/toggle.scss4
-rw-r--r--src/styles/type.scss46
-rw-r--r--src/styles/welcome.scss36
-rw-r--r--src/webview/screenshare.js16
31 files changed, 924 insertions, 555 deletions
diff --git a/src/components/auth/SetupAssistant.js b/src/components/auth/SetupAssistant.js
index bd9069eb7..06ab09892 100644
--- a/src/components/auth/SetupAssistant.js
+++ b/src/components/auth/SetupAssistant.js
@@ -18,15 +18,16 @@ const SLACK_ID = 'slack';
18const messages = defineMessages({ 18const messages = defineMessages({
19 headline: { 19 headline: {
20 id: 'setupAssistant.headline', 20 id: 'setupAssistant.headline',
21 defaultMessage: '!!!Let\'s get started', 21 defaultMessage: "!!!Let's get started",
22 }, 22 },
23 subHeadline: { 23 subHeadline: {
24 id: 'setupAssistant.subheadline', 24 id: 'setupAssistant.subheadline',
25 defaultMessage: '!!!Choose from our most used services and get back on top of your messaging now.', 25 defaultMessage:
26 '!!!Choose from our most used services and get back on top of your messaging now.',
26 }, 27 },
27 submitButtonLabel: { 28 submitButtonLabel: {
28 id: 'setupAssistant.submit.label', 29 id: 'setupAssistant.submit.label',
29 defaultMessage: '!!!Let\'s go', 30 defaultMessage: "!!!Let's go",
30 }, 31 },
31 inviteSuccessInfo: { 32 inviteSuccessInfo: {
32 id: 'invite.successInfo', 33 id: 'invite.successInfo',
@@ -34,14 +35,19 @@ const messages = defineMessages({
34 }, 35 },
35}); 36});
36 37
37const styles = (theme) => ({ 38let transition = 'none';
39
40if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
41 transition = 'all 0.25s';
42}
43
44const styles = theme => ({
38 root: { 45 root: {
39 width: '500px !important', 46 width: '500px !important',
40 textAlign: 'center', 47 textAlign: 'center',
41 padding: 20, 48 padding: 20,
42 49
43 '& h1': { 50 '& h1': {},
44 },
45 }, 51 },
46 servicesGrid: { 52 servicesGrid: {
47 display: 'flex', 53 display: 'flex',
@@ -60,7 +66,7 @@ const styles = (theme) => ({
60 borderRadius: theme.borderRadius, 66 borderRadius: theme.borderRadius,
61 marginBottom: 10, 67 marginBottom: 10,
62 opacity: 0.5, 68 opacity: 0.5,
63 transition: 'all 0.25s', 69 transition,
64 border: [3, 'solid', 'transparent'], 70 border: [3, 'solid', 'transparent'],
65 71
66 '& h2': { 72 '& h2': {
@@ -70,10 +76,8 @@ const styles = (theme) => ({
70 76
71 '&:hover': { 77 '&:hover': {
72 border: [3, 'solid', theme.brandPrimary], 78 border: [3, 'solid', theme.brandPrimary],
73 '& $serviceIcon': { 79 '& $serviceIcon': {},
74 },
75 }, 80 },
76
77 }, 81 },
78 selected: { 82 selected: {
79 border: [3, 'solid', theme.brandPrimary], 83 border: [3, 'solid', theme.brandPrimary],
@@ -82,7 +86,7 @@ const styles = (theme) => ({
82 }, 86 },
83 serviceIcon: { 87 serviceIcon: {
84 width: 50, 88 width: 50,
85 transition: 'all 0.25s', 89 transition,
86 }, 90 },
87 91
88 slackModalContent: { 92 slackModalContent: {
@@ -125,7 +129,8 @@ const styles = (theme) => ({
125 }, 129 },
126}); 130});
127 131
128@injectSheet(styles) @observer 132@injectSheet(styles)
133@observer
129class SetupAssistant extends Component { 134class SetupAssistant extends Component {
130 static propTypes = { 135 static propTypes = {
131 classes: PropTypes.object.isRequired, 136 classes: PropTypes.object.isRequired,
@@ -144,13 +149,17 @@ class SetupAssistant extends Component {
144 }; 149 };
145 150
146 state = { 151 state = {
147 services: [{ 152 services: [
148 id: 'whatsapp', 153 {
149 }, { 154 id: 'whatsapp',
150 id: 'messenger', 155 },
151 }, { 156 {
152 id: 'gmail', 157 id: 'messenger',
153 }], 158 },
159 {
160 id: 'gmail',
161 },
162 ],
154 isSlackModalOpen: false, 163 isSlackModalOpen: false,
155 slackWorkspace: '', 164 slackWorkspace: '',
156 }; 165 };
@@ -158,10 +167,12 @@ class SetupAssistant extends Component {
158 slackWorkspaceHandler() { 167 slackWorkspaceHandler() {
159 const { slackWorkspace = '', services } = this.state; 168 const { slackWorkspace = '', services } = this.state;
160 169
161 const sanitizedWorkspace = slackWorkspace.trim().replace(/^https?:\/\//, ''); 170 const sanitizedWorkspace = slackWorkspace
171 .trim()
172 .replace(/^https?:\/\//, '');
162 173
163 if (sanitizedWorkspace) { 174 if (sanitizedWorkspace) {
164 const index = services.findIndex((s) => s.id === SLACK_ID); 175 const index = services.findIndex(s => s.id === SLACK_ID);
165 176
166 if (index === -1) { 177 if (index === -1) {
167 const newServices = services; 178 const newServices = services;
@@ -179,9 +190,17 @@ class SetupAssistant extends Component {
179 render() { 190 render() {
180 const { intl } = this.context; 191 const { intl } = this.context;
181 const { 192 const {
182 classes, isInviteSuccessful, onSubmit, services, isSettingUpServices, 193 classes,
194 isInviteSuccessful,
195 onSubmit,
196 services,
197 isSettingUpServices,
183 } = this.props; 198 } = this.props;
184 const { isSlackModalOpen, slackWorkspace, services: addedServices } = this.state; 199 const {
200 isSlackModalOpen,
201 slackWorkspace,
202 services: addedServices,
203 } = this.state;
185 204
186 return ( 205 return (
187 <div className={`auth__container ${classes.root}`}> 206 <div className={`auth__container ${classes.root}`}>
@@ -197,29 +216,22 @@ class SetupAssistant extends Component {
197 </Appear> 216 </Appear>
198 )} 217 )}
199 218
200 <img 219 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
201 src="./assets/images/logo.svg" 220 <h1>{intl.formatMessage(messages.headline)}</h1>
202 className="auth__logo" 221 <h2>{intl.formatMessage(messages.subHeadline)}</h2>
203 alt=""
204 />
205 <h1>
206 {intl.formatMessage(messages.headline)}
207 </h1>
208 <h2>
209 {intl.formatMessage(messages.subHeadline)}
210 </h2>
211 <div className={classnames('grid', classes.servicesGrid)}> 222 <div className={classnames('grid', classes.servicesGrid)}>
212 {Object.keys(services).map((id) => { 223 {Object.keys(services).map(id => {
213 const service = services[id]; 224 const service = services[id];
214 return ( 225 return (
215 <button 226 <button
216 className={classnames({ 227 className={classnames({
217 [classes.serviceContainer]: true, 228 [classes.serviceContainer]: true,
218 [classes.selected]: this.state.services.findIndex((s) => s.id === id) !== -1, 229 [classes.selected]:
230 this.state.services.findIndex(s => s.id === id) !== -1,
219 })} 231 })}
220 key={id} 232 key={id}
221 onClick={() => { 233 onClick={() => {
222 const index = this.state.services.findIndex((s) => s.id === id); 234 const index = this.state.services.findIndex(s => s.id === id);
223 if (index === -1) { 235 if (index === -1) {
224 if (id === SLACK_ID) { 236 if (id === SLACK_ID) {
225 this.setState({ isSlackModalOpen: true }); 237 this.setState({ isSlackModalOpen: true });
@@ -244,9 +256,7 @@ class SetupAssistant extends Component {
244 className={classes.serviceIcon} 256 className={classes.serviceIcon}
245 alt="" 257 alt=""
246 /> 258 />
247 <h2> 259 <h2>{service.name}</h2>
248 {service.name}
249 </h2>
250 {id === SLACK_ID && slackWorkspace && ( 260 {id === SLACK_ID && slackWorkspace && (
251 <Badge type="secondary" className={classes.slackBadge}> 261 <Badge type="secondary" className={classes.slackBadge}>
252 {slackWorkspace} 262 {slackWorkspace}
@@ -275,22 +285,22 @@ class SetupAssistant extends Component {
275 <div className={classes.slackModalContent}> 285 <div className={classes.slackModalContent}>
276 <img src={`${CDN_URL}/recipes/dist/slack/src/icon.svg`} alt="" /> 286 <img src={`${CDN_URL}/recipes/dist/slack/src/icon.svg`} alt="" />
277 <h1>Create your first Slack workspace</h1> 287 <h1>Create your first Slack workspace</h1>
278 <form onSubmit={(e) => { 288 <form
279 e.preventDefault(); 289 onSubmit={e => {
280 this.slackWorkspaceHandler(); 290 e.preventDefault();
281 }} 291 this.slackWorkspaceHandler();
292 }}
282 > 293 >
283 <Input 294 <Input
284 suffix=".slack.com" 295 suffix=".slack.com"
285 placeholder="workspace-url" 296 placeholder="workspace-url"
286 onChange={(e) => this.setState({ slackWorkspace: e.target.value })} 297 onChange={e =>
298 this.setState({ slackWorkspace: e.target.value })
299 }
287 value={slackWorkspace} 300 value={slackWorkspace}
288 /> 301 />
289 <div className={classes.modalActionContainer}> 302 <div className={classes.modalActionContainer}>
290 <Button 303 <Button type="submit" label="Save" />
291 type="submit"
292 label="Save"
293 />
294 <Button 304 <Button
295 type="link" 305 type="link"
296 buttonType="secondary" 306 buttonType="secondary"
@@ -305,7 +315,7 @@ class SetupAssistant extends Component {
305 <Button 315 <Button
306 type="button" 316 type="button"
307 className="auth__button" 317 className="auth__button"
308 // disabled={!atLeastOneEmailAddress} 318 // disabled={!atLeastOneEmailAddress}
309 label={intl.formatMessage(messages.submitButtonLabel)} 319 label={intl.formatMessage(messages.submitButtonLabel)}
310 onClick={() => onSubmit(this.state.services)} 320 onClick={() => onSubmit(this.state.services)}
311 busy={isSettingUpServices} 321 busy={isSettingUpServices}
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js
index 6e1d7c9a0..de962cc12 100644
--- a/src/components/layout/AppLayout.js
+++ b/src/components/layout/AppLayout.js
@@ -40,22 +40,32 @@ const messages = defineMessages({
40 }, 40 },
41 authRequestFailed: { 41 authRequestFailed: {
42 id: 'infobar.authRequestFailed', 42 id: 'infobar.authRequestFailed',
43 defaultMessage: '!!!There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.', 43 defaultMessage:
44 '!!!There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.',
44 }, 45 },
45}); 46});
46 47
47const styles = (theme) => ({ 48let transition = 'none';
49
50if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
51 transition = 'transform 0.5s ease';
52}
53
54const styles = theme => ({
48 appContent: { 55 appContent: {
49 // width: `calc(100% + ${theme.workspaces.drawer.width}px)`, 56 // width: `calc(100% + ${theme.workspaces.drawer.width}px)`,
50 width: '100%', 57 width: '100%',
51 transition: 'transform 0.5s ease', 58 transition,
52 transform() { 59 transform() {
53 return workspaceStore.isWorkspaceDrawerOpen ? 'translateX(0)' : `translateX(-${theme.workspaces.drawer.width}px)`; 60 return workspaceStore.isWorkspaceDrawerOpen
61 ? 'translateX(0)'
62 : `translateX(-${theme.workspaces.drawer.width}px)`;
54 }, 63 },
55 }, 64 },
56}); 65});
57 66
58@injectSheet(styles) @observer 67@injectSheet(styles)
68@observer
59class AppLayout extends Component { 69class AppLayout extends Component {
60 static propTypes = { 70 static propTypes = {
61 classes: PropTypes.object.isRequired, 71 classes: PropTypes.object.isRequired,
@@ -80,7 +90,7 @@ class AppLayout extends Component {
80 90
81 state = { 91 state = {
82 shouldShowAppUpdateInfoBar: true, 92 shouldShowAppUpdateInfoBar: true,
83 } 93 };
84 94
85 static defaultProps = { 95 static defaultProps = {
86 children: [], 96 children: [],
@@ -118,42 +128,48 @@ class AppLayout extends Component {
118 return ( 128 return (
119 <ErrorBoundary> 129 <ErrorBoundary>
120 <div className="app"> 130 <div className="app">
121 {isWindows && !isFullScreen && <TitleBar menu={window.ferdi.menu.template} icon="assets/images/logo.svg" />} 131 {isWindows && !isFullScreen && (
132 <TitleBar
133 menu={window.ferdi.menu.template}
134 icon="assets/images/logo.svg"
135 />
136 )}
122 <div className={`app__content ${classes.appContent}`}> 137 <div className={`app__content ${classes.appContent}`}>
123 {workspacesDrawer} 138 {workspacesDrawer}
124 {sidebar} 139 {sidebar}
125 <div className="app__service"> 140 <div className="app__service">
126 <WorkspaceSwitchingIndicator /> 141 <WorkspaceSwitchingIndicator />
127 {news.length > 0 && news.map((item) => ( 142 {news.length > 0 &&
143 news.map(item => (
144 <InfoBar
145 key={item.id}
146 position="top"
147 type={item.type}
148 sticky={item.sticky}
149 onHide={() => removeNewsItem({ newsId: item.id })}
150 >
151 <span
152 dangerouslySetInnerHTML={createMarkup(item.message)}
153 onClick={event => {
154 const { target } = event;
155 if (target && target.hasAttribute('data-is-news-cta')) {
156 removeNewsItem({ newsId: item.id });
157 }
158 }}
159 />
160 </InfoBar>
161 ))}
162 {!areRequiredRequestsSuccessful && showRequiredRequestsError && (
128 <InfoBar 163 <InfoBar
129 key={item.id} 164 type="danger"
130 position="top" 165 ctaLabel="Try again"
131 type={item.type} 166 ctaLoading={areRequiredRequestsLoading}
132 sticky={item.sticky} 167 sticky
133 onHide={() => removeNewsItem({ newsId: item.id })} 168 onClick={retryRequiredRequests}
134 > 169 >
135 <span 170 <span className="mdi mdi-flash" />
136 dangerouslySetInnerHTML={createMarkup(item.message)} 171 {intl.formatMessage(messages.requiredRequestsFailed)}
137 onClick={(event) => {
138 const { target } = event;
139 if (target && target.hasAttribute('data-is-news-cta')) {
140 removeNewsItem({ newsId: item.id });
141 }
142 }}
143 />
144 </InfoBar> 172 </InfoBar>
145 ))}
146 {!areRequiredRequestsSuccessful && showRequiredRequestsError && (
147 <InfoBar
148 type="danger"
149 ctaLabel="Try again"
150 ctaLoading={areRequiredRequestsLoading}
151 sticky
152 onClick={retryRequiredRequests}
153 >
154 <span className="mdi mdi-flash" />
155 {intl.formatMessage(messages.requiredRequestsFailed)}
156 </InfoBar>
157 )} 173 )}
158 {authRequestFailed && ( 174 {authRequestFailed && (
159 <InfoBar 175 <InfoBar
@@ -178,7 +194,7 @@ class AppLayout extends Component {
178 {intl.formatMessage(messages.servicesUpdated)} 194 {intl.formatMessage(messages.servicesUpdated)}
179 </InfoBar> 195 </InfoBar>
180 )} 196 )}
181 { appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( 197 {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && (
182 <AppUpdateInfoBar 198 <AppUpdateInfoBar
183 nextAppReleaseVersion={nextAppReleaseVersion} 199 nextAppReleaseVersion={nextAppReleaseVersion}
184 onInstallUpdate={installAppUpdate} 200 onInstallUpdate={installAppUpdate}
diff --git a/src/components/services/content/ConnectionLostBanner.js b/src/components/services/content/ConnectionLostBanner.js
index 0b9d3122c..343ddf8b4 100644
--- a/src/components/services/content/ConnectionLostBanner.js
+++ b/src/components/services/content/ConnectionLostBanner.js
@@ -5,9 +5,7 @@ import injectSheet from 'react-jss';
5import { Icon } from '@meetfranz/ui'; 5import { Icon } from '@meetfranz/ui';
6import { intlShape, defineMessages } from 'react-intl'; 6import { intlShape, defineMessages } from 'react-intl';
7 7
8import { 8import { mdiAlert } from '@mdi/js';
9 mdiAlert,
10} from '@mdi/js';
11import { LIVE_API_FERDI_WEBSITE } from '../../../config'; 9import { LIVE_API_FERDI_WEBSITE } from '../../../config';
12// import { Button } from '@meetfranz/forms'; 10// import { Button } from '@meetfranz/forms';
13 11
@@ -26,7 +24,13 @@ const messages = defineMessages({
26 }, 24 },
27}); 25});
28 26
29const styles = (theme) => ({ 27let buttonTransition = 'none';
28
29if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
30 buttonTransition = 'opacity 0.25s';
31}
32
33const styles = theme => ({
30 root: { 34 root: {
31 background: theme.colorBackground, 35 background: theme.colorBackground,
32 borderRadius: theme.borderRadius, 36 borderRadius: theme.borderRadius,
@@ -47,7 +51,7 @@ const styles = (theme) => ({
47 opacity: 0.7, 51 opacity: 0.7,
48 }, 52 },
49 button: { 53 button: {
50 transition: 'opacity 0.25s', 54 transition: buttonTransition,
51 color: theme.colorText, 55 color: theme.colorText,
52 border: [1, 'solid', theme.colorText], 56 border: [1, 'solid', theme.colorText],
53 borderRadius: theme.borderRadiusSmall, 57 borderRadius: theme.borderRadiusSmall,
@@ -65,13 +69,14 @@ const styles = (theme) => ({
65 }, 69 },
66}); 70});
67 71
68@injectSheet(styles) @observer 72@injectSheet(styles)
73@observer
69class ConnectionLostBanner extends Component { 74class ConnectionLostBanner extends Component {
70 static propTypes = { 75 static propTypes = {
71 classes: PropTypes.object.isRequired, 76 classes: PropTypes.object.isRequired,
72 name: PropTypes.string.isRequired, 77 name: PropTypes.string.isRequired,
73 reload: PropTypes.func.isRequired, 78 reload: PropTypes.func.isRequired,
74 } 79 };
75 80
76 static contextTypes = { 81 static contextTypes = {
77 intl: intlShape, 82 intl: intlShape,
@@ -80,20 +85,13 @@ class ConnectionLostBanner extends Component {
80 inputRef = React.createRef(); 85 inputRef = React.createRef();
81 86
82 render() { 87 render() {
83 const { 88 const { classes, name, reload } = this.props;
84 classes,
85 name,
86 reload,
87 } = this.props;
88 89
89 const { intl } = this.context; 90 const { intl } = this.context;
90 91
91 return ( 92 return (
92 <div className={classes.root}> 93 <div className={classes.root}>
93 <Icon 94 <Icon icon={mdiAlert} className={classes.icon} />
94 icon={mdiAlert}
95 className={classes.icon}
96 />
97 <p> 95 <p>
98 {intl.formatMessage(messages.text, { name })} 96 {intl.formatMessage(messages.text, { name })}
99 <br /> 97 <br />
@@ -104,11 +102,7 @@ class ConnectionLostBanner extends Component {
104 {intl.formatMessage(messages.moreInformation)} 102 {intl.formatMessage(messages.moreInformation)}
105 </a> 103 </a>
106 </p> 104 </p>
107 <button 105 <button type="button" className={classes.button} onClick={reload}>
108 type="button"
109 className={classes.button}
110 onClick={reload}
111 >
112 {intl.formatMessage(messages.cta)} 106 {intl.formatMessage(messages.cta)}
113 </button> 107 </button>
114 </div> 108 </div>
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js
index ccf3333f8..023e152c7 100644
--- a/src/components/services/tabs/TabItem.js
+++ b/src/components/services/tabs/TabItem.js
@@ -1,6 +1,4 @@
1import { 1import { Menu, dialog, app, getCurrentWindow } from '@electron/remote';
2 Menu, dialog, app, getCurrentWindow,
3} from '@electron/remote';
4import React, { Component } from 'react'; 2import React, { Component } from 'react';
5import { defineMessages, intlShape } from 'react-intl'; 3import { defineMessages, intlShape } from 'react-intl';
6import PropTypes from 'prop-types'; 4import PropTypes from 'prop-types';
@@ -14,7 +12,9 @@ import { observable, autorun } from 'mobx';
14import ServiceModel from '../../../models/Service'; 12import ServiceModel from '../../../models/Service';
15import { ctrlKey, cmdKey } from '../../../environment'; 13import { ctrlKey, cmdKey } from '../../../environment';
16 14
17const IS_SERVICE_DEBUGGING_ENABLED = (localStorage.getItem('debug') || '').includes('Ferdi:Service'); 15const IS_SERVICE_DEBUGGING_ENABLED = (
16 localStorage.getItem('debug') || ''
17).includes('Ferdi:Service');
18 18
19const messages = defineMessages({ 19const messages = defineMessages({
20 reload: { 20 reload: {
@@ -63,10 +63,21 @@ const messages = defineMessages({
63 }, 63 },
64 confirmDeleteService: { 64 confirmDeleteService: {
65 id: 'tabs.item.confirmDeleteService', 65 id: 'tabs.item.confirmDeleteService',
66 defaultMessage: '!!!Do you really want to delete the {serviceName} service?', 66 defaultMessage:
67 '!!!Do you really want to delete the {serviceName} service?',
67 }, 68 },
68}); 69});
69 70
71let pollIndicatorTransition = 'none';
72let polledTransition = 'none';
73let pollAnsweredTransition = 'none';
74
75if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
76 pollIndicatorTransition = 'background 0.5s';
77 polledTransition = 'background 0.1s';
78 pollAnsweredTransition = 'background 0.1s';
79}
80
70const styles = { 81const styles = {
71 pollIndicator: { 82 pollIndicator: {
72 position: 'absolute', 83 position: 'absolute',
@@ -75,7 +86,7 @@ const styles = {
75 height: 10, 86 height: 10,
76 borderRadius: 5, 87 borderRadius: 5,
77 background: 'gray', 88 background: 'gray',
78 transition: 'background 0.5s', 89 transition: pollIndicatorTransition,
79 }, 90 },
80 pollIndicatorPoll: { 91 pollIndicatorPoll: {
81 left: 2, 92 left: 2,
@@ -85,18 +96,20 @@ const styles = {
85 }, 96 },
86 polled: { 97 polled: {
87 background: 'yellow !important', 98 background: 'yellow !important',
88 transition: 'background 0.1s', 99 transition: polledTransition,
89 }, 100 },
90 pollAnswered: { 101 pollAnswered: {
91 background: 'green !important', 102 background: 'green !important',
92 transition: 'background 0.1s', 103 transition: pollAnsweredTransition,
93 }, 104 },
94 stale: { 105 stale: {
95 background: 'red !important', 106 background: 'red !important',
96 }, 107 },
97}; 108};
98 109
99@injectSheet(styles) @observer class TabItem extends Component { 110@injectSheet(styles)
111@observer
112class TabItem extends Component {
100 static propTypes = { 113 static propTypes = {
101 classes: PropTypes.object.isRequired, 114 classes: PropTypes.object.isRequired,
102 service: PropTypes.instanceOf(ServiceModel).isRequired, 115 service: PropTypes.instanceOf(ServiceModel).isRequired,
@@ -131,13 +144,17 @@ const styles = {
131 if (Date.now() - service.lastPoll < ms('0.2s')) { 144 if (Date.now() - service.lastPoll < ms('0.2s')) {
132 this.isPolled = true; 145 this.isPolled = true;
133 146
134 setTimeout(() => { this.isPolled = false; }, ms('1s')); 147 setTimeout(() => {
148 this.isPolled = false;
149 }, ms('1s'));
135 } 150 }
136 151
137 if (Date.now() - service.lastPollAnswer < ms('0.2s')) { 152 if (Date.now() - service.lastPollAnswer < ms('0.2s')) {
138 this.isPollAnswered = true; 153 this.isPollAnswered = true;
139 154
140 setTimeout(() => { this.isPollAnswered = false; }, ms('1s')); 155 setTimeout(() => {
156 this.isPollAnswered = false;
157 }, ms('1s'));
141 } 158 }
142 }); 159 });
143 } 160 }
@@ -163,62 +180,85 @@ const styles = {
163 } = this.props; 180 } = this.props;
164 const { intl } = this.context; 181 const { intl } = this.context;
165 182
166 const menuTemplate = [{ 183 const menuTemplate = [
167 label: service.name || service.recipe.name, 184 {
168 enabled: false, 185 label: service.name || service.recipe.name,
169 }, { 186 enabled: false,
170 type: 'separator', 187 },
171 }, { 188 {
172 label: intl.formatMessage(messages.reload), 189 type: 'separator',
173 click: reload, 190 },
174 accelerator: `${cmdKey}+R`, 191 {
175 }, { 192 label: intl.formatMessage(messages.reload),
176 label: intl.formatMessage(messages.edit), 193 click: reload,
177 click: () => openSettings({ 194 accelerator: `${cmdKey}+R`,
178 path: `services/edit/${service.id}`, 195 },
179 }), 196 {
180 }, { 197 label: intl.formatMessage(messages.edit),
181 type: 'separator', 198 click: () =>
182 }, { 199 openSettings({
183 label: service.isNotificationEnabled 200 path: `services/edit/${service.id}`,
184 ? intl.formatMessage(messages.disableNotifications) 201 }),
185 : intl.formatMessage(messages.enableNotifications), 202 },
186 click: () => toggleNotifications(), 203 {
187 }, { 204 type: 'separator',
188 label: service.isMuted
189 ? intl.formatMessage(messages.enableAudio)
190 : intl.formatMessage(messages.disableAudio),
191 click: () => toggleAudio(),
192 }, {
193 label: intl.formatMessage(service.isEnabled ? messages.disableService : messages.enableService),
194 click: () => (service.isEnabled ? disableService() : enableService()),
195 }, {
196 label: intl.formatMessage(service.isHibernating ? messages.wakeUpService : messages.hibernateService),
197 click: () => (service.isHibernating ? wakeUpService() : hibernateService()),
198 enabled: service.canHibernate,
199 }, {
200 type: 'separator',
201 }, {
202 label: intl.formatMessage(messages.deleteService),
203 click: () => {
204 const selection = dialog.showMessageBoxSync(app.mainWindow, {
205 type: 'question',
206 message: intl.formatMessage(messages.deleteService),
207 detail: intl.formatMessage(messages.confirmDeleteService, { serviceName: service.name || service.recipe.name }),
208 buttons: [
209 'Yes',
210 'No',
211 ],
212 });
213 if (selection === 0) {
214 deleteService();
215 }
216 }, 205 },
217 }]; 206 {
207 label: service.isNotificationEnabled
208 ? intl.formatMessage(messages.disableNotifications)
209 : intl.formatMessage(messages.enableNotifications),
210 click: () => toggleNotifications(),
211 },
212 {
213 label: service.isMuted
214 ? intl.formatMessage(messages.enableAudio)
215 : intl.formatMessage(messages.disableAudio),
216 click: () => toggleAudio(),
217 },
218 {
219 label: intl.formatMessage(
220 service.isEnabled ? messages.disableService : messages.enableService,
221 ),
222 click: () => (service.isEnabled ? disableService() : enableService()),
223 },
224 {
225 label: intl.formatMessage(
226 service.isHibernating
227 ? messages.wakeUpService
228 : messages.hibernateService,
229 ),
230 click: () =>
231 (service.isHibernating ? wakeUpService() : hibernateService()),
232 enabled: service.canHibernate,
233 },
234 {
235 type: 'separator',
236 },
237 {
238 label: intl.formatMessage(messages.deleteService),
239 click: () => {
240 const selection = dialog.showMessageBoxSync(app.mainWindow, {
241 type: 'question',
242 message: intl.formatMessage(messages.deleteService),
243 detail: intl.formatMessage(messages.confirmDeleteService, {
244 serviceName: service.name || service.recipe.name,
245 }),
246 buttons: ['Yes', 'No'],
247 });
248 if (selection === 0) {
249 deleteService();
250 }
251 },
252 },
253 ];
218 const menu = Menu.buildFromTemplate(menuTemplate); 254 const menu = Menu.buildFromTemplate(menuTemplate);
219 255
220 let notificationBadge = null; 256 let notificationBadge = null;
221 if ((showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) && showMessageBadgesEvenWhenMuted && service.isBadgeEnabled) { 257 if (
258 (showMessageBadgeWhenMutedSetting || service.isNotificationEnabled) &&
259 showMessageBadgesEvenWhenMuted &&
260 service.isBadgeEnabled
261 ) {
222 notificationBadge = ( 262 notificationBadge = (
223 <span> 263 <span>
224 {service.unreadDirectMessageCount > 0 && ( 264 {service.unreadDirectMessageCount > 0 && (
@@ -226,17 +266,13 @@ const styles = {
226 {service.unreadDirectMessageCount} 266 {service.unreadDirectMessageCount}
227 </span> 267 </span>
228 )} 268 )}
229 {service.unreadIndirectMessageCount > 0 269 {service.unreadIndirectMessageCount > 0 &&
230 && service.unreadDirectMessageCount === 0 270 service.unreadDirectMessageCount === 0 &&
231 && service.isIndirectMessageBadgeEnabled && ( 271 service.isIndirectMessageBadgeEnabled && (
232 <span className="tab-item__message-count is-indirect"> 272 <span className="tab-item__message-count is-indirect">•</span>
233 •
234 </span>
235 )} 273 )}
236 {service.isHibernating && ( 274 {service.isHibernating && (
237 <span className="tab-item__message-count hibernating"> 275 <span className="tab-item__message-count hibernating">•</span>
238 •
239 </span>
240 )} 276 )}
241 </span> 277 </span>
242 ); 278 );
@@ -245,7 +281,8 @@ const styles = {
245 return ( 281 return (
246 <li 282 <li
247 className={classnames({ 283 className={classnames({
248 [classes.stale]: IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection, 284 [classes.stale]:
285 IS_SERVICE_DEBUGGING_ENABLED && service.lostRecipeConnection,
249 'tab-item': true, 286 'tab-item': true,
250 'is-active': service.isActive, 287 'is-active': service.isActive,
251 'has-custom-icon': service.hasCustomIcon, 288 'has-custom-icon': service.hasCustomIcon,
@@ -253,13 +290,11 @@ const styles = {
253 })} 290 })}
254 onClick={clickHandler} 291 onClick={clickHandler}
255 onContextMenu={() => menu.popup(getCurrentWindow())} 292 onContextMenu={() => menu.popup(getCurrentWindow())}
256 data-tip={`${service.name} ${shortcutIndex <= 9 ? `(${ctrlKey}+${shortcutIndex})` : ''}`} 293 data-tip={`${service.name} ${
294 shortcutIndex <= 9 ? `(${ctrlKey}+${shortcutIndex})` : ''
295 }`}
257 > 296 >
258 <img 297 <img src={service.icon} className="tab-item__icon" alt="" />
259 src={service.icon}
260 className="tab-item__icon"
261 alt=""
262 />
263 {notificationBadge} 298 {notificationBadge}
264 {IS_SERVICE_DEBUGGING_ENABLED && ( 299 {IS_SERVICE_DEBUGGING_ENABLED && (
265 <> 300 <>
diff --git a/src/components/ui/AppLoader/styles.js b/src/components/ui/AppLoader/styles.js
index 755a56b40..011f6282d 100644
--- a/src/components/ui/AppLoader/styles.js
+++ b/src/components/ui/AppLoader/styles.js
@@ -1,3 +1,9 @@
1let sloganTransition = 'none';
2
3if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
4 sloganTransition = 'opacity 1s ease';
5}
6
1export default { 7export default {
2 component: { 8 component: {
3 color: '#FFF', 9 color: '#FFF',
@@ -5,7 +11,7 @@ export default {
5 slogan: { 11 slogan: {
6 display: 'block', 12 display: 'block',
7 opacity: 0, 13 opacity: 0,
8 transition: 'opacity 1s ease', 14 transition: sloganTransition,
9 position: 'absolute', 15 position: 'absolute',
10 textAlign: 'center', 16 textAlign: 'center',
11 width: '100%', 17 width: '100%',
diff --git a/src/features/webControls/components/WebControls.js b/src/features/webControls/components/WebControls.js
index 1cdd14e55..417ebb0b0 100644
--- a/src/features/webControls/components/WebControls.js
+++ b/src/features/webControls/components/WebControls.js
@@ -6,7 +6,11 @@ import { Icon } from '@meetfranz/ui';
6import { defineMessages, intlShape } from 'react-intl'; 6import { defineMessages, intlShape } from 'react-intl';
7 7
8import { 8import {
9 mdiReload, mdiArrowRight, mdiArrowLeft, mdiHomeOutline, mdiEarth, 9 mdiReload,
10 mdiArrowRight,
11 mdiArrowLeft,
12 mdiHomeOutline,
13 mdiEarth,
10} from '@mdi/js'; 14} from '@mdi/js';
11 15
12const messages = defineMessages({ 16const messages = defineMessages({
@@ -32,7 +36,13 @@ const messages = defineMessages({
32 }, 36 },
33}); 37});
34 38
35const styles = (theme) => ({ 39let buttonTransition = 'none';
40
41if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
42 buttonTransition = 'opacity 0.25s';
43}
44
45const styles = theme => ({
36 root: { 46 root: {
37 background: theme.colorBackground, 47 background: theme.colorBackground,
38 position: 'relative', 48 position: 'relative',
@@ -51,7 +61,7 @@ const styles = (theme) => ({
51 button: { 61 button: {
52 width: 30, 62 width: 30,
53 height: 50, 63 height: 50,
54 transition: 'opacity 0.25s', 64 transition: buttonTransition,
55 65
56 '&:hover': { 66 '&:hover': {
57 opacity: 0.8, 67 opacity: 0.8,
@@ -83,7 +93,8 @@ const styles = (theme) => ({
83 }, 93 },
84}); 94});
85 95
86@injectSheet(styles) @observer 96@injectSheet(styles)
97@observer
87class WebControls extends Component { 98class WebControls extends Component {
88 static propTypes = { 99 static propTypes = {
89 classes: PropTypes.object.isRequired, 100 classes: PropTypes.object.isRequired,
@@ -96,7 +107,7 @@ class WebControls extends Component {
96 openInBrowser: PropTypes.func.isRequired, 107 openInBrowser: PropTypes.func.isRequired,
97 url: PropTypes.string.isRequired, 108 url: PropTypes.string.isRequired,
98 navigate: PropTypes.func.isRequired, 109 navigate: PropTypes.func.isRequired,
99 } 110 };
100 111
101 static contextTypes = { 112 static contextTypes = {
102 intl: intlShape, 113 intl: intlShape,
@@ -119,7 +130,7 @@ class WebControls extends Component {
119 state = { 130 state = {
120 inputUrl: '', 131 inputUrl: '',
121 editUrl: false, 132 editUrl: false,
122 } 133 };
123 134
124 render() { 135 render() {
125 const { 136 const {
@@ -135,10 +146,7 @@ class WebControls extends Component {
135 navigate, 146 navigate,
136 } = this.props; 147 } = this.props;
137 148
138 const { 149 const { inputUrl, editUrl } = this.state;
139 inputUrl,
140 editUrl,
141 } = this.state;
142 150
143 const { intl } = this.context; 151 const { intl } = this.context;
144 152
@@ -151,10 +159,7 @@ class WebControls extends Component {
151 data-tip={intl.formatMessage(messages.goHome)} 159 data-tip={intl.formatMessage(messages.goHome)}
152 data-place="bottom" 160 data-place="bottom"
153 > 161 >
154 <Icon 162 <Icon icon={mdiHomeOutline} className={classes.icon} />
155 icon={mdiHomeOutline}
156 className={classes.icon}
157 />
158 </button> 163 </button>
159 <button 164 <button
160 onClick={goBack} 165 onClick={goBack}
@@ -164,10 +169,7 @@ class WebControls extends Component {
164 data-tip={intl.formatMessage(messages.back)} 169 data-tip={intl.formatMessage(messages.back)}
165 data-place="bottom" 170 data-place="bottom"
166 > 171 >
167 <Icon 172 <Icon icon={mdiArrowLeft} className={classes.icon} />
168 icon={mdiArrowLeft}
169 className={classes.icon}
170 />
171 </button> 173 </button>
172 <button 174 <button
173 onClick={goForward} 175 onClick={goForward}
@@ -177,10 +179,7 @@ class WebControls extends Component {
177 data-tip={intl.formatMessage(messages.forward)} 179 data-tip={intl.formatMessage(messages.forward)}
178 data-place="bottom" 180 data-place="bottom"
179 > 181 >
180 <Icon 182 <Icon icon={mdiArrowRight} className={classes.icon} />
181 icon={mdiArrowRight}
182 className={classes.icon}
183 />
184 </button> 183 </button>
185 <button 184 <button
186 onClick={reload} 185 onClick={reload}
@@ -189,25 +188,24 @@ class WebControls extends Component {
189 data-tip={intl.formatMessage(messages.reload)} 188 data-tip={intl.formatMessage(messages.reload)}
190 data-place="bottom" 189 data-place="bottom"
191 > 190 >
192 <Icon 191 <Icon icon={mdiReload} className={classes.icon} />
193 icon={mdiReload}
194 className={classes.icon}
195 />
196 </button> 192 </button>
197 <input 193 <input
198 value={editUrl ? inputUrl : url} 194 value={editUrl ? inputUrl : url}
199 className={classes.input} 195 className={classes.input}
200 onChange={(event) => this.setState({ 196 onChange={event =>
201 inputUrl: event.target.value, 197 this.setState({
202 })} 198 inputUrl: event.target.value,
203 onFocus={(event) => { 199 })
200 }
201 onFocus={event => {
204 console.log('on focus event'); 202 console.log('on focus event');
205 event.target.select(); 203 event.target.select();
206 this.setState({ 204 this.setState({
207 editUrl: true, 205 editUrl: true,
208 }); 206 });
209 }} 207 }}
210 onKeyDown={(event) => { 208 onKeyDown={event => {
211 if (event.key === 'Enter') { 209 if (event.key === 'Enter') {
212 this.setState({ 210 this.setState({
213 editUrl: false, 211 editUrl: false,
@@ -231,10 +229,7 @@ class WebControls extends Component {
231 data-tip={intl.formatMessage(messages.openInBrowser)} 229 data-tip={intl.formatMessage(messages.openInBrowser)}
232 data-place="bottom" 230 data-place="bottom"
233 > 231 >
234 <Icon 232 <Icon icon={mdiEarth} className={classes.icon} />
235 icon={mdiEarth}
236 className={classes.icon}
237 />
238 </button> 233 </button>
239 </div> 234 </div>
240 ); 235 );
diff --git a/src/features/workspaces/components/WorkspaceDrawerItem.js b/src/features/workspaces/components/WorkspaceDrawerItem.js
index 0294f69fc..c3f9fcb19 100644
--- a/src/features/workspaces/components/WorkspaceDrawerItem.js
+++ b/src/features/workspaces/components/WorkspaceDrawerItem.js
@@ -18,12 +18,18 @@ const messages = defineMessages({
18 }, 18 },
19}); 19});
20 20
21const styles = (theme) => ({ 21let itemTransition = 'none';
22
23if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
24 itemTransition = 'background-color 300ms ease-out';
25}
26
27const styles = theme => ({
22 item: { 28 item: {
23 height: '67px', 29 height: '67px',
24 padding: `15px ${theme.workspaces.drawer.padding}px`, 30 padding: `15px ${theme.workspaces.drawer.padding}px`,
25 borderBottom: `1px solid ${theme.workspaces.drawer.listItem.border}`, 31 borderBottom: `1px solid ${theme.workspaces.drawer.listItem.border}`,
26 transition: 'background-color 300ms ease-out', 32 transition: itemTransition,
27 '&:first-child': { 33 '&:first-child': {
28 borderTop: `1px solid ${theme.workspaces.drawer.listItem.border}`, 34 borderTop: `1px solid ${theme.workspaces.drawer.listItem.border}`,
29 }, 35 },
@@ -59,7 +65,8 @@ const styles = (theme) => ({
59 }, 65 },
60}); 66});
61 67
62@injectSheet(styles) @observer 68@injectSheet(styles)
69@observer
63class WorkspaceDrawerItem extends Component { 70class WorkspaceDrawerItem extends Component {
64 static propTypes = { 71 static propTypes = {
65 classes: PropTypes.object.isRequired, 72 classes: PropTypes.object.isRequired,
@@ -91,15 +98,19 @@ class WorkspaceDrawerItem extends Component {
91 } = this.props; 98 } = this.props;
92 const { intl } = this.context; 99 const { intl } = this.context;
93 100
94 const contextMenuTemplate = [{ 101 const contextMenuTemplate = [
95 label: name, 102 {
96 enabled: false, 103 label: name,
97 }, { 104 enabled: false,
98 type: 'separator', 105 },
99 }, { 106 {
100 label: intl.formatMessage(messages.contextMenuEdit), 107 type: 'separator',
101 click: onContextMenuEditClick, 108 },
102 }]; 109 {
110 label: intl.formatMessage(messages.contextMenuEdit),
111 click: onContextMenuEditClick,
112 },
113 ];
103 114
104 const contextMenu = Menu.buildFromTemplate(contextMenuTemplate); 115 const contextMenu = Menu.buildFromTemplate(contextMenuTemplate);
105 116
@@ -110,10 +121,12 @@ class WorkspaceDrawerItem extends Component {
110 isActive ? classes.isActiveItem : null, 121 isActive ? classes.isActiveItem : null,
111 ])} 122 ])}
112 onClick={onClick} 123 onClick={onClick}
113 onContextMenu={() => ( 124 onContextMenu={() =>
114 onContextMenuEditClick && contextMenu.popup(getCurrentWindow()) 125 onContextMenuEditClick && contextMenu.popup(getCurrentWindow())
115 )} 126 }
116 data-tip={`${shortcutIndex <= 9 ? `(${ctrlKey}+${altKey}+${shortcutIndex})` : ''}`} 127 data-tip={`${
128 shortcutIndex <= 9 ? `(${ctrlKey}+${altKey}+${shortcutIndex})` : ''
129 }`}
117 > 130 >
118 <span 131 <span
119 className={classnames([ 132 className={classnames([
@@ -129,7 +142,9 @@ class WorkspaceDrawerItem extends Component {
129 isActive ? classes.activeServices : null, 142 isActive ? classes.activeServices : null,
130 ])} 143 ])}
131 > 144 >
132 {services.length ? services.join(', ') : intl.formatMessage(messages.noServicesAddedYet)} 145 {services.length
146 ? services.join(', ')
147 : intl.formatMessage(messages.noServicesAddedYet)}
133 </span> 148 </span>
134 </div> 149 </div>
135 ); 150 );
diff --git a/src/features/workspaces/components/WorkspaceSwitchingIndicator.js b/src/features/workspaces/components/WorkspaceSwitchingIndicator.js
index b46959e91..613075c4a 100644
--- a/src/features/workspaces/components/WorkspaceSwitchingIndicator.js
+++ b/src/features/workspaces/components/WorkspaceSwitchingIndicator.js
@@ -15,12 +15,18 @@ const messages = defineMessages({
15 }, 15 },
16}); 16});
17 17
18const styles = (theme) => ({ 18let wrapperTransition = 'none';
19
20if (window.matchMedia('(prefers-reduced-motion: no-preference)')) {
21 wrapperTransition = 'width 0.5s ease';
22}
23
24const styles = theme => ({
19 wrapper: { 25 wrapper: {
20 display: 'flex', 26 display: 'flex',
21 alignItems: 'flex-start', 27 alignItems: 'flex-start',
22 position: 'absolute', 28 position: 'absolute',
23 transition: 'width 0.5s ease', 29 transition: wrapperTransition,
24 width: `calc(100% - ${theme.workspaces.drawer.width}px)`, 30 width: `calc(100% - ${theme.workspaces.drawer.width}px)`,
25 marginTop: '20px', 31 marginTop: '20px',
26 }, 32 },
@@ -47,7 +53,8 @@ const styles = (theme) => ({
47 }, 53 },
48}); 54});
49 55
50@injectSheet(styles) @observer 56@injectSheet(styles)
57@observer
51class WorkspaceSwitchingIndicator extends Component { 58class WorkspaceSwitchingIndicator extends Component {
52 static propTypes = { 59 static propTypes = {
53 classes: PropTypes.object.isRequired, 60 classes: PropTypes.object.isRequired,
@@ -63,13 +70,11 @@ class WorkspaceSwitchingIndicator extends Component {
63 const { intl } = this.context; 70 const { intl } = this.context;
64 const { isSwitchingWorkspace, nextWorkspace } = workspaceStore; 71 const { isSwitchingWorkspace, nextWorkspace } = workspaceStore;
65 if (!isSwitchingWorkspace) return null; 72 if (!isSwitchingWorkspace) return null;
66 const nextWorkspaceName = nextWorkspace ? nextWorkspace.name : 'All services'; 73 const nextWorkspaceName = nextWorkspace
74 ? nextWorkspace.name
75 : 'All services';
67 return ( 76 return (
68 <div 77 <div className={classnames([classes.wrapper])}>
69 className={classnames([
70 classes.wrapper,
71 ])}
72 >
73 <div className={classes.component}> 78 <div className={classes.component}>
74 <Loader 79 <Loader
75 className={classes.spinner} 80 className={classes.spinner}
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 99f450c23..4279ba079 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -773,7 +773,7 @@
773 "defaultMessage": "!!!Choose from our most used services and get back on top of your messaging now.", 773 "defaultMessage": "!!!Choose from our most used services and get back on top of your messaging now.",
774 "end": { 774 "end": {
775 "column": 3, 775 "column": 3,
776 "line": 26 776 "line": 27
777 }, 777 },
778 "file": "src/components/auth/SetupAssistant.js", 778 "file": "src/components/auth/SetupAssistant.js",
779 "id": "setupAssistant.subheadline", 779 "id": "setupAssistant.subheadline",
@@ -786,26 +786,26 @@
786 "defaultMessage": "!!!Let's go", 786 "defaultMessage": "!!!Let's go",
787 "end": { 787 "end": {
788 "column": 3, 788 "column": 3,
789 "line": 30 789 "line": 31
790 }, 790 },
791 "file": "src/components/auth/SetupAssistant.js", 791 "file": "src/components/auth/SetupAssistant.js",
792 "id": "setupAssistant.submit.label", 792 "id": "setupAssistant.submit.label",
793 "start": { 793 "start": {
794 "column": 21, 794 "column": 21,
795 "line": 27 795 "line": 28
796 } 796 }
797 }, 797 },
798 { 798 {
799 "defaultMessage": "!!!Invitations sent successfully", 799 "defaultMessage": "!!!Invitations sent successfully",
800 "end": { 800 "end": {
801 "column": 3, 801 "column": 3,
802 "line": 34 802 "line": 35
803 }, 803 },
804 "file": "src/components/auth/SetupAssistant.js", 804 "file": "src/components/auth/SetupAssistant.js",
805 "id": "invite.successInfo", 805 "id": "invite.successInfo",
806 "start": { 806 "start": {
807 "column": 21, 807 "column": 21,
808 "line": 31 808 "line": 32
809 } 809 }
810 } 810 }
811 ], 811 ],
@@ -1074,7 +1074,7 @@
1074 "defaultMessage": "!!!There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.", 1074 "defaultMessage": "!!!There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.",
1075 "end": { 1075 "end": {
1076 "column": 3, 1076 "column": 3,
1077 "line": 44 1077 "line": 45
1078 }, 1078 },
1079 "file": "src/components/layout/AppLayout.js", 1079 "file": "src/components/layout/AppLayout.js",
1080 "id": "infobar.authRequestFailed", 1080 "id": "infobar.authRequestFailed",
@@ -1354,39 +1354,39 @@
1354 "defaultMessage": "!!!Oh no! Ferdi lost the connection to {name}.", 1354 "defaultMessage": "!!!Oh no! Ferdi lost the connection to {name}.",
1355 "end": { 1355 "end": {
1356 "column": 3, 1356 "column": 3,
1357 "line": 18 1357 "line": 16
1358 }, 1358 },
1359 "file": "src/components/services/content/ConnectionLostBanner.js", 1359 "file": "src/components/services/content/ConnectionLostBanner.js",
1360 "id": "connectionLostBanner.message", 1360 "id": "connectionLostBanner.message",
1361 "start": { 1361 "start": {
1362 "column": 8, 1362 "column": 8,
1363 "line": 15 1363 "line": 13
1364 } 1364 }
1365 }, 1365 },
1366 { 1366 {
1367 "defaultMessage": "!!!What happened?", 1367 "defaultMessage": "!!!What happened?",
1368 "end": { 1368 "end": {
1369 "column": 3, 1369 "column": 3,
1370 "line": 22 1370 "line": 20
1371 }, 1371 },
1372 "file": "src/components/services/content/ConnectionLostBanner.js", 1372 "file": "src/components/services/content/ConnectionLostBanner.js",
1373 "id": "connectionLostBanner.informationLink", 1373 "id": "connectionLostBanner.informationLink",
1374 "start": { 1374 "start": {
1375 "column": 19, 1375 "column": 19,
1376 "line": 19 1376 "line": 17
1377 } 1377 }
1378 }, 1378 },
1379 { 1379 {
1380 "defaultMessage": "!!!Reload Service", 1380 "defaultMessage": "!!!Reload Service",
1381 "end": { 1381 "end": {
1382 "column": 3, 1382 "column": 3,
1383 "line": 26 1383 "line": 24
1384 }, 1384 },
1385 "file": "src/components/services/content/ConnectionLostBanner.js", 1385 "file": "src/components/services/content/ConnectionLostBanner.js",
1386 "id": "connectionLostBanner.cta", 1386 "id": "connectionLostBanner.cta",
1387 "start": { 1387 "start": {
1388 "column": 7, 1388 "column": 7,
1389 "line": 23 1389 "line": 21
1390 } 1390 }
1391 } 1391 }
1392 ], 1392 ],
@@ -1839,7 +1839,7 @@
1839 "defaultMessage": "!!!Do you really want to delete the {serviceName} service?", 1839 "defaultMessage": "!!!Do you really want to delete the {serviceName} service?",
1840 "end": { 1840 "end": {
1841 "column": 3, 1841 "column": 3,
1842 "line": 67 1842 "line": 68
1843 }, 1843 },
1844 "file": "src/components/services/tabs/TabItem.js", 1844 "file": "src/components/services/tabs/TabItem.js",
1845 "id": "tabs.item.confirmDeleteService", 1845 "id": "tabs.item.confirmDeleteService",
@@ -5299,65 +5299,65 @@
5299 "defaultMessage": "!!!Home", 5299 "defaultMessage": "!!!Home",
5300 "end": { 5300 "end": {
5301 "column": 3, 5301 "column": 3,
5302 "line": 16 5302 "line": 20
5303 }, 5303 },
5304 "file": "src/features/webControls/components/WebControls.js", 5304 "file": "src/features/webControls/components/WebControls.js",
5305 "id": "webControls.goHome", 5305 "id": "webControls.goHome",
5306 "start": { 5306 "start": {
5307 "column": 10, 5307 "column": 10,
5308 "line": 13 5308 "line": 17
5309 } 5309 }
5310 }, 5310 },
5311 { 5311 {
5312 "defaultMessage": "!!!Open in Browser", 5312 "defaultMessage": "!!!Open in Browser",
5313 "end": { 5313 "end": {
5314 "column": 3, 5314 "column": 3,
5315 "line": 20 5315 "line": 24
5316 }, 5316 },
5317 "file": "src/features/webControls/components/WebControls.js", 5317 "file": "src/features/webControls/components/WebControls.js",
5318 "id": "webControls.openInBrowser", 5318 "id": "webControls.openInBrowser",
5319 "start": { 5319 "start": {
5320 "column": 17, 5320 "column": 17,
5321 "line": 17 5321 "line": 21
5322 } 5322 }
5323 }, 5323 },
5324 { 5324 {
5325 "defaultMessage": "!!!Back", 5325 "defaultMessage": "!!!Back",
5326 "end": { 5326 "end": {
5327 "column": 3, 5327 "column": 3,
5328 "line": 24 5328 "line": 28
5329 }, 5329 },
5330 "file": "src/features/webControls/components/WebControls.js", 5330 "file": "src/features/webControls/components/WebControls.js",
5331 "id": "webControls.back", 5331 "id": "webControls.back",
5332 "start": { 5332 "start": {
5333 "column": 8, 5333 "column": 8,
5334 "line": 21 5334 "line": 25
5335 } 5335 }
5336 }, 5336 },
5337 { 5337 {
5338 "defaultMessage": "!!!Forward", 5338 "defaultMessage": "!!!Forward",
5339 "end": { 5339 "end": {
5340 "column": 3, 5340 "column": 3,
5341 "line": 28 5341 "line": 32
5342 }, 5342 },
5343 "file": "src/features/webControls/components/WebControls.js", 5343 "file": "src/features/webControls/components/WebControls.js",
5344 "id": "webControls.forward", 5344 "id": "webControls.forward",
5345 "start": { 5345 "start": {
5346 "column": 11, 5346 "column": 11,
5347 "line": 25 5347 "line": 29
5348 } 5348 }
5349 }, 5349 },
5350 { 5350 {
5351 "defaultMessage": "!!!Reload", 5351 "defaultMessage": "!!!Reload",
5352 "end": { 5352 "end": {
5353 "column": 3, 5353 "column": 3,
5354 "line": 32 5354 "line": 36
5355 }, 5355 },
5356 "file": "src/features/webControls/components/WebControls.js", 5356 "file": "src/features/webControls/components/WebControls.js",
5357 "id": "webControls.reload", 5357 "id": "webControls.reload",
5358 "start": { 5358 "start": {
5359 "column": 10, 5359 "column": 10,
5360 "line": 29 5360 "line": 33
5361 } 5361 }
5362 } 5362 }
5363 ], 5363 ],
diff --git a/src/i18n/messages/src/components/auth/SetupAssistant.json b/src/i18n/messages/src/components/auth/SetupAssistant.json
index 03593cfbc..f950228b7 100644
--- a/src/i18n/messages/src/components/auth/SetupAssistant.json
+++ b/src/i18n/messages/src/components/auth/SetupAssistant.json
@@ -21,7 +21,7 @@
21 "column": 15 21 "column": 15
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 26, 24 "line": 27,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Let's go", 30 "defaultMessage": "!!!Let's go",
31 "file": "src/components/auth/SetupAssistant.js", 31 "file": "src/components/auth/SetupAssistant.js",
32 "start": { 32 "start": {
33 "line": 27, 33 "line": 28,
34 "column": 21 34 "column": 21
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 30, 37 "line": 31,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Invitations sent successfully", 43 "defaultMessage": "!!!Invitations sent successfully",
44 "file": "src/components/auth/SetupAssistant.js", 44 "file": "src/components/auth/SetupAssistant.js",
45 "start": { 45 "start": {
46 "line": 31, 46 "line": 32,
47 "column": 21 47 "column": 21
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 34, 50 "line": 35,
51 "column": 3 51 "column": 3
52 } 52 }
53 } 53 }
diff --git a/src/i18n/messages/src/components/layout/AppLayout.json b/src/i18n/messages/src/components/layout/AppLayout.json
index d14c30077..e3f5658b2 100644
--- a/src/i18n/messages/src/components/layout/AppLayout.json
+++ b/src/i18n/messages/src/components/layout/AppLayout.json
@@ -47,7 +47,7 @@
47 "column": 21 47 "column": 21
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 44, 50 "line": 45,
51 "column": 3 51 "column": 3
52 } 52 }
53 } 53 }
diff --git a/src/i18n/messages/src/components/services/content/ConnectionLostBanner.json b/src/i18n/messages/src/components/services/content/ConnectionLostBanner.json
index bcfb6ab94..6805b4d67 100644
--- a/src/i18n/messages/src/components/services/content/ConnectionLostBanner.json
+++ b/src/i18n/messages/src/components/services/content/ConnectionLostBanner.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Oh no! Ferdi lost the connection to {name}.", 4 "defaultMessage": "!!!Oh no! Ferdi lost the connection to {name}.",
5 "file": "src/components/services/content/ConnectionLostBanner.js", 5 "file": "src/components/services/content/ConnectionLostBanner.js",
6 "start": { 6 "start": {
7 "line": 15, 7 "line": 13,
8 "column": 8 8 "column": 8
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 18, 11 "line": 16,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!What happened?", 17 "defaultMessage": "!!!What happened?",
18 "file": "src/components/services/content/ConnectionLostBanner.js", 18 "file": "src/components/services/content/ConnectionLostBanner.js",
19 "start": { 19 "start": {
20 "line": 19, 20 "line": 17,
21 "column": 19 21 "column": 19
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 22, 24 "line": 20,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Reload Service", 30 "defaultMessage": "!!!Reload Service",
31 "file": "src/components/services/content/ConnectionLostBanner.js", 31 "file": "src/components/services/content/ConnectionLostBanner.js",
32 "start": { 32 "start": {
33 "line": 23, 33 "line": 21,
34 "column": 7 34 "column": 7
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 26, 37 "line": 24,
38 "column": 3 38 "column": 3
39 } 39 }
40 } 40 }
diff --git a/src/i18n/messages/src/components/services/tabs/TabItem.json b/src/i18n/messages/src/components/services/tabs/TabItem.json
index 6d4d5f6fd..60cfde52d 100644
--- a/src/i18n/messages/src/components/services/tabs/TabItem.json
+++ b/src/i18n/messages/src/components/services/tabs/TabItem.json
@@ -151,7 +151,7 @@
151 "column": 24 151 "column": 24
152 }, 152 },
153 "end": { 153 "end": {
154 "line": 67, 154 "line": 68,
155 "column": 3 155 "column": 3
156 } 156 }
157 } 157 }
diff --git a/src/i18n/messages/src/features/webControls/components/WebControls.json b/src/i18n/messages/src/features/webControls/components/WebControls.json
index 969437e98..7676ec0b1 100644
--- a/src/i18n/messages/src/features/webControls/components/WebControls.json
+++ b/src/i18n/messages/src/features/webControls/components/WebControls.json
@@ -4,11 +4,11 @@
4 "defaultMessage": "!!!Home", 4 "defaultMessage": "!!!Home",
5 "file": "src/features/webControls/components/WebControls.js", 5 "file": "src/features/webControls/components/WebControls.js",
6 "start": { 6 "start": {
7 "line": 13, 7 "line": 17,
8 "column": 10 8 "column": 10
9 }, 9 },
10 "end": { 10 "end": {
11 "line": 16, 11 "line": 20,
12 "column": 3 12 "column": 3
13 } 13 }
14 }, 14 },
@@ -17,11 +17,11 @@
17 "defaultMessage": "!!!Open in Browser", 17 "defaultMessage": "!!!Open in Browser",
18 "file": "src/features/webControls/components/WebControls.js", 18 "file": "src/features/webControls/components/WebControls.js",
19 "start": { 19 "start": {
20 "line": 17, 20 "line": 21,
21 "column": 17 21 "column": 17
22 }, 22 },
23 "end": { 23 "end": {
24 "line": 20, 24 "line": 24,
25 "column": 3 25 "column": 3
26 } 26 }
27 }, 27 },
@@ -30,11 +30,11 @@
30 "defaultMessage": "!!!Back", 30 "defaultMessage": "!!!Back",
31 "file": "src/features/webControls/components/WebControls.js", 31 "file": "src/features/webControls/components/WebControls.js",
32 "start": { 32 "start": {
33 "line": 21, 33 "line": 25,
34 "column": 8 34 "column": 8
35 }, 35 },
36 "end": { 36 "end": {
37 "line": 24, 37 "line": 28,
38 "column": 3 38 "column": 3
39 } 39 }
40 }, 40 },
@@ -43,11 +43,11 @@
43 "defaultMessage": "!!!Forward", 43 "defaultMessage": "!!!Forward",
44 "file": "src/features/webControls/components/WebControls.js", 44 "file": "src/features/webControls/components/WebControls.js",
45 "start": { 45 "start": {
46 "line": 25, 46 "line": 29,
47 "column": 11 47 "column": 11
48 }, 48 },
49 "end": { 49 "end": {
50 "line": 28, 50 "line": 32,
51 "column": 3 51 "column": 3
52 } 52 }
53 }, 53 },
@@ -56,11 +56,11 @@
56 "defaultMessage": "!!!Reload", 56 "defaultMessage": "!!!Reload",
57 "file": "src/features/webControls/components/WebControls.js", 57 "file": "src/features/webControls/components/WebControls.js",
58 "start": { 58 "start": {
59 "line": 29, 59 "line": 33,
60 "column": 10 60 "column": 10
61 }, 61 },
62 "end": { 62 "end": {
63 "line": 32, 63 "line": 36,
64 "column": 3 64 "column": 3
65 } 65 }
66 } 66 }
diff --git a/src/index.html b/src/index.html
index 6b67cc248..929aec3ad 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,60 +1,84 @@
1<!DOCTYPE html> 1<!DOCTYPE html>
2<html lang="en"> 2<html lang="en">
3<head> 3 <head>
4 <title>Ferdi</title> 4 <title>Ferdi</title>
5 <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> 5 <meta charset="UTF-8" />
6 <link rel="stylesheet" type="text/css" href="./styles/main.css" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1" />
7 <script type="text/javascript" src="./app.js"></script> 7 <link rel="stylesheet" type="text/css" href="./styles/main.css" />
8</head> 8 <link
9<body> 9 rel="stylesheet"
10 <div class="window-draggable"></div> 10 type="text/css"
11 <div class="dev-warning">DEV MODE</div> 11 href="./styles/animations.css"
12 <div id="root"></div> 12 media="(prefers-reduced-motion: no-preference)"
13 <div id="portalContainer"></div> 13 />
14 <script> 14 <script type="text/javascript" src="./app.js"></script>
15 document.querySelector('body').classList.add(process.env.OS_PLATFORM ? process.env.OS_PLATFORM : process.platform); 15 </head>
16 <body>
17 <div class="window-draggable"></div>
18 <div class="dev-warning">DEV MODE</div>
19 <div id="root"></div>
20 <div id="portalContainer"></div>
21 <script>
22 document
23 .querySelector('body')
24 .classList.add(
25 process.env.OS_PLATFORM ? process.env.OS_PLATFORM : process.platform,
26 );
16 27
17 require('./sentry') 28 require('./sentry');
18 const { isDevMode } = require('./environment'); 29 const { isDevMode } = require('./environment');
19 if (isDevMode) { 30 if (isDevMode) {
20 const debugging = require('debug'); 31 const debugging = require('debug');
21 debugging.enable(process.env.DEBUG); 32 debugging.enable(process.env.DEBUG);
22 33
23 const debug = debugging('Index'); 34 const debug = debugging('Index');
24 35
25 document.querySelector('body').classList.add('isDevMode'); 36 document.querySelector('body').classList.add('isDevMode');
26 37
27 (function() { 38 (function () {
28 const lrHost = 'http://localhost:35729'; 39 const lrHost = 'http://localhost:35729';
29 const s = document.createElement('script'); 40 const s = document.createElement('script');
30 s.async = true; 41 s.async = true;
31 s.setAttribute('src', lrHost + '/livereload.js'); 42 s.setAttribute('src', lrHost + '/livereload.js');
32 document.body.appendChild(s); 43 document.body.appendChild(s);
33 44
34 s.onload = () => { 45 s.onload = () => {
35 debug('livereload loaded'); 46 debug('livereload loaded');
36 const originalReloadBehaviour = window._onLiveReloadFileChanged; 47 const originalReloadBehaviour = window._onLiveReloadFileChanged;
37 48
38 window._onLiveReloadFileChanged = (file) => { 49 window._onLiveReloadFileChanged = file => {
39 const isTodoPreloadPath = file.path.includes('/build/features/todos/preload.js'); 50 const isTodoPreloadPath = file.path.includes(
40 if (!file.path.includes('/build/webview/') && !file.path.includes('/build/index.js') && !file.path.includes('/build/electron/') && !isTodoPreloadPath) { 51 '/build/features/todos/preload.js',
41 originalReloadBehaviour(file); 52 );
42 } else { 53 if (
43 if (isTodoPreloadPath) { 54 !file.path.includes('/build/webview/') &&
44 debug('Livereload: Reloading all webviews'); 55 !file.path.includes('/build/index.js') &&
45 const webview = document.querySelector('webview[partition="persist:todos"]'); 56 !file.path.includes('/build/electron/') &&
46 if (webview) webview.reload(); 57 !isTodoPreloadPath
47 } else if (file.path.includes('/build/webview/')) { 58 ) {
48 debug('Livereload: Reloading all webviews'); 59 originalReloadBehaviour(file);
49 const webviews = document.querySelectorAll('webview').forEach(webview => webview.reload());
50 } else { 60 } else {
51 debug('Livereload: skip reload as only main process files have changed'); 61 if (isTodoPreloadPath) {
62 debug('Livereload: Reloading all webviews');
63 const webview = document.querySelector(
64 'webview[partition="persist:todos"]',
65 );
66 if (webview) webview.reload();
67 } else if (file.path.includes('/build/webview/')) {
68 debug('Livereload: Reloading all webviews');
69 const webviews = document
70 .querySelectorAll('webview')
71 .forEach(webview => webview.reload());
72 } else {
73 debug(
74 'Livereload: skip reload as only main process files have changed',
75 );
76 }
52 } 77 }
53 } 78 };
54 } 79 };
55 } 80 })();
56 })(); 81 }
57 } 82 </script>
58 </script> 83 </body>
59</body>
60</html> 84</html>
diff --git a/src/internal-server/public/css/main.css b/src/internal-server/public/css/main.css
index a1c5653d7..b20b67922 100644
--- a/src/internal-server/public/css/main.css
+++ b/src/internal-server/public/css/main.css
@@ -4,7 +4,8 @@ input {
4 padding: 0.5rem; 4 padding: 0.5rem;
5} 5}
6 6
7button, .button { 7button,
8.button {
8 display: flex; 9 display: flex;
9 overflow: hidden; 10 overflow: hidden;
10 padding: 12px 12px; 11 padding: 12px 12px;
@@ -14,7 +15,9 @@ button, .button {
14 -moz-user-select: none; 15 -moz-user-select: none;
15 -ms-user-select: none; 16 -ms-user-select: none;
16 user-select: none; 17 user-select: none;
17 transition: all 150ms linear; 18 @media (prefers-reduced-motion: no-preference) {
19 transition: all 150ms linear;
20 }
18 text-align: center; 21 text-align: center;
19 white-space: nowrap; 22 white-space: nowrap;
20 text-decoration: none !important; 23 text-decoration: none !important;
@@ -33,7 +36,7 @@ button, .button {
33 align-items: center; 36 align-items: center;
34 flex: 0 0 160px; 37 flex: 0 0 160px;
35 box-shadow: 2px 5px 10px #e4e4e4; 38 box-shadow: 2px 5px 10px #e4e4e4;
36 color: #FFFFFF; 39 color: #ffffff;
37 background: #161616; 40 background: #161616;
38} 41}
39 42
@@ -66,4 +69,4 @@ button, .button {
66 69
67td { 70td {
68 word-break: break-all; 71 word-break: break-all;
69} \ No newline at end of file 72}
diff --git a/src/styles/button.scss b/src/styles/button.scss
index d18b683d5..86b3501f0 100644
--- a/src/styles/button.scss
+++ b/src/styles/button.scss
@@ -4,29 +4,45 @@
4 background: $theme-brand-primary; 4 background: $theme-brand-primary;
5 color: $dark-theme-text-color; 5 color: $dark-theme-text-color;
6 6
7 &:hover { background: darken($theme-brand-primary, 5%); } 7 &:hover {
8 &:active { background: lighten($theme-brand-primary, 5%); } 8 background: darken($theme-brand-primary, 5%);
9 }
10 &:active {
11 background: lighten($theme-brand-primary, 5%);
12 }
9 13
10 &.franz-form__button--secondary { 14 &.franz-form__button--secondary {
11 background: $dark-theme-gray-dark; 15 background: $dark-theme-gray-dark;
12 color: $dark-theme-text-color; 16 color: $dark-theme-text-color;
13 17
14 &:hover { background: lighten($dark-theme-gray-dark, 10%); } 18 &:hover {
15 &:active { background: lighten($dark-theme-gray-dark, 20%); } 19 background: lighten($dark-theme-gray-dark, 10%);
20 }
21 &:active {
22 background: lighten($dark-theme-gray-dark, 20%);
23 }
16 } 24 }
17 25
18 &.franz-form__button--danger { 26 &.franz-form__button--danger {
19 background: $theme-brand-danger; 27 background: $theme-brand-danger;
20 28
21 &:hover { background: darken($theme-brand-danger, 5%); } 29 &:hover {
22 &:active { background: lighten($theme-brand-danger, 5%); } 30 background: darken($theme-brand-danger, 5%);
31 }
32 &:active {
33 background: lighten($theme-brand-danger, 5%);
34 }
23 } 35 }
24 36
25 &.franz-form__button--warning { 37 &.franz-form__button--warning {
26 background: $theme-brand-warning; 38 background: $theme-brand-warning;
27 39
28 &:hover { background: darken($theme-brand-warning, 5%); } 40 &:hover {
29 &:active { background: lighten($theme-brand-warning, 5%); } 41 background: darken($theme-brand-warning, 5%);
42 }
43 &:active {
44 background: lighten($theme-brand-warning, 5%);
45 }
30 } 46 }
31 47
32 &.franz-form__button--inverted { 48 &.franz-form__button--inverted {
@@ -39,27 +55,35 @@
39 } 55 }
40 } 56 }
41 57
42 &:disabled { opacity: .5; } 58 &:disabled {
59 opacity: 0.5;
60 }
43} 61}
44 62
45.franz-form__button { 63.franz-form__button {
46 background: $theme-brand-primary; 64 background: $theme-brand-primary;
47 border-radius: 3px; 65 border-radius: 3px;
48 display: block; 66 display: block;
49 color: #FFF; 67 color: #fff;
50 padding: 10px 20px; 68 padding: 10px 20px;
51 position: relative; 69 position: relative;
52 transition: background .5s; 70 @media (prefers-reduced-motion: no-preference) {
71 transition: background 0.5s;
72 }
53 text-align: center; 73 text-align: center;
54 74
55 &:hover { background: darken($theme-brand-primary, 5%) } 75 &:hover {
76 background: darken($theme-brand-primary, 5%);
77 }
56 78
57 &:active { 79 &:active {
58 background: lighten($theme-brand-primary, 5%); 80 background: lighten($theme-brand-primary, 5%);
59 transition: none; 81 transition: none;
60 } 82 }
61 83
62 &:disabled { opacity: .2; } 84 &:disabled {
85 opacity: 0.2;
86 }
63 87
64 &.franz-form__button--large { 88 &.franz-form__button--large {
65 width: 100%; 89 width: 100%;
@@ -70,22 +94,34 @@
70 background: $theme-gray-lighter; 94 background: $theme-gray-lighter;
71 color: $theme-gray; 95 color: $theme-gray;
72 96
73 &:hover { background: darken($theme-gray-lighter, 5%); } 97 &:hover {
74 &:active { background: lighten($theme-gray-lighter, 5%); } 98 background: darken($theme-gray-lighter, 5%);
99 }
100 &:active {
101 background: lighten($theme-gray-lighter, 5%);
102 }
75 } 103 }
76 104
77 &.franz-form__button--danger { 105 &.franz-form__button--danger {
78 background: $theme-brand-danger; 106 background: $theme-brand-danger;
79 107
80 &:hover { background: darken($theme-brand-danger, 5%); } 108 &:hover {
81 &:active { background: lighten($theme-brand-danger, 5%); } 109 background: darken($theme-brand-danger, 5%);
110 }
111 &:active {
112 background: lighten($theme-brand-danger, 5%);
113 }
82 } 114 }
83 115
84 &.franz-form__button--warning { 116 &.franz-form__button--warning {
85 background: $theme-brand-warning; 117 background: $theme-brand-warning;
86 118
87 &:hover { background: darken($theme-brand-warning, 5%); } 119 &:hover {
88 &:active { background: lighten($theme-brand-warning, 5%); } 120 background: darken($theme-brand-warning, 5%);
121 }
122 &:active {
123 background: lighten($theme-brand-warning, 5%);
124 }
89 } 125 }
90 126
91 &.franz-form__button--inverted { 127 &.franz-form__button--inverted {
@@ -93,11 +129,12 @@
93 border: 2px solid $theme-brand-primary; 129 border: 2px solid $theme-brand-primary;
94 color: $theme-brand-primary; 130 color: $theme-brand-primary;
95 padding: 10px 20px; 131 padding: 10px 20px;
96 transition: background .5s, color .5s; 132 @media (prefers-reduced-motion: no-preference) {
97 133 transition: background 0.5s, color 0.5s;
134 }
98 &:hover { 135 &:hover {
99 background: darken($theme-brand-primary, 5%); 136 background: darken($theme-brand-primary, 5%);
100 color: #FFF; 137 color: #fff;
101 } 138 }
102 } 139 }
103 140
@@ -122,20 +159,20 @@
122 z-index: 9998; 159 z-index: 9998;
123 list-style: none; 160 list-style: none;
124 background: $theme-brand-primary; 161 background: $theme-brand-primary;
125 162
126 position: absolute; 163 position: absolute;
127 bottom: 20px; 164 bottom: 20px;
128 right: 20px; 165 right: 20px;
129 166
130 cursor: pointer; 167 cursor: pointer;
131 168
132 display: flex; 169 display: flex;
133 justify-content: center; 170 justify-content: center;
134 align-items: center; 171 align-items: center;
135 172
136 a { 173 a {
137 font-size: 30px; 174 font-size: 30px;
138 color: #FFFFFF; 175 color: #ffffff;
139 cursor: pointer; 176 cursor: pointer;
140 } 177 }
141} 178}
diff --git a/src/styles/content-tabs.scss b/src/styles/content-tabs.scss
index 03befedcb..41bd2c251 100644
--- a/src/styles/content-tabs.scss
+++ b/src/styles/content-tabs.scss
@@ -9,7 +9,7 @@
9 .content-tabs__tabs { 9 .content-tabs__tabs {
10 .content-tabs__item { 10 .content-tabs__item {
11 background: $dark-theme-gray; 11 background: $dark-theme-gray;
12 color: #FFF; 12 color: #fff;
13 border: 0; 13 border: 0;
14 } 14 }
15 } 15 }
@@ -24,19 +24,26 @@
24 overflow: hidden; 24 overflow: hidden;
25 25
26 .content-tabs__item { 26 .content-tabs__item {
27 background: linear-gradient($theme-gray-lightest 80%, darken($theme-gray-lightest, 3%)); 27 background: linear-gradient(
28 $theme-gray-lightest 80%,
29 darken($theme-gray-lightest, 3%)
30 );
28 border-right: 1px solid $theme-gray-lighter; 31 border-right: 1px solid $theme-gray-lighter;
29 color: $theme-gray-dark; 32 color: $theme-gray-dark;
30 flex: 1; 33 flex: 1;
31 padding: 10px; 34 padding: 10px;
32 transition: background $theme-transition-time; 35 @media (prefers-reduced-motion: no-preference) {
36 transition: background $theme-transition-time;
37 }
33 38
34 &:last-of-type { border-right: 0; } 39 &:last-of-type {
40 border-right: 0;
41 }
35 42
36 &.is-active { 43 &.is-active {
37 background: $theme-brand-primary; 44 background: $theme-brand-primary;
38 box-shadow: none; 45 box-shadow: none;
39 color: #FFF; 46 color: #fff;
40 } 47 }
41 } 48 }
42 } 49 }
@@ -51,10 +58,16 @@
51 display: none; 58 display: none;
52 top: 0; 59 top: 0;
53 60
54 &.is-active { display: block; } 61 &.is-active {
62 display: block;
63 }
55 } 64 }
56 65
57 .franz-form__input-wrapper { background: #FFF; } 66 .franz-form__input-wrapper {
58 .franz-form__field:last-of-type { margin-bottom: 0; } 67 background: #fff;
68 }
69 .franz-form__field:last-of-type {
70 margin-bottom: 0;
71 }
59 } 72 }
60} 73}
diff --git a/src/styles/fonts.scss b/src/styles/fonts.scss
index 432332b49..1b2c99945 100644
--- a/src/styles/fonts.scss
+++ b/src/styles/fonts.scss
@@ -5,6 +5,7 @@
5 src: url('../assets/fonts/OpenSans-Light.ttf'); 5 src: url('../assets/fonts/OpenSans-Light.ttf');
6 font-weight: 300; 6 font-weight: 300;
7 font-style: normal; 7 font-style: normal;
8 display: swap;
8} 9}
9 10
10@font-face { 11@font-face {
@@ -12,6 +13,7 @@
12 src: url('../assets/fonts/OpenSans-Regular.ttf'); 13 src: url('../assets/fonts/OpenSans-Regular.ttf');
13 font-weight: normal; 14 font-weight: normal;
14 font-style: normal; 15 font-style: normal;
16 display: swap;
15} 17}
16 18
17@font-face { 19@font-face {
@@ -19,6 +21,7 @@
19 src: url('../assets/fonts/OpenSans-Bold.ttf'); 21 src: url('../assets/fonts/OpenSans-Bold.ttf');
20 font-weight: bold; 22 font-weight: bold;
21 font-style: normal; 23 font-style: normal;
24 display: swap;
22} 25}
23 26
24@font-face { 27@font-face {
@@ -26,6 +29,7 @@
26 src: url('../assets/fonts/OpenSans-BoldItalic.ttf'); 29 src: url('../assets/fonts/OpenSans-BoldItalic.ttf');
27 font-weight: bold; 30 font-weight: bold;
28 font-style: italic; 31 font-style: italic;
32 display: swap;
29} 33}
30 34
31@font-face { 35@font-face {
@@ -33,6 +37,7 @@
33 src: url('../assets/fonts/OpenSans-ExtraBold.ttf'); 37 src: url('../assets/fonts/OpenSans-ExtraBold.ttf');
34 font-weight: 800; 38 font-weight: 800;
35 font-style: normal; 39 font-style: normal;
40 display: swap;
36} 41}
37 42
38@font-face { 43@font-face {
@@ -40,4 +45,5 @@
40 src: url('../assets/fonts/OpenSans-ExtraBoldItalic.ttf'); 45 src: url('../assets/fonts/OpenSans-ExtraBoldItalic.ttf');
41 font-weight: 800; 46 font-weight: 800;
42 font-style: italic; 47 font-style: italic;
48 display: swap;
43} 49}
diff --git a/src/styles/image-upload.scss b/src/styles/image-upload.scss
index 31300c227..b5f6d5cd4 100644
--- a/src/styles/image-upload.scss
+++ b/src/styles/image-upload.scss
@@ -5,17 +5,23 @@
5 color: $dark-theme-gray-lighter; 5 color: $dark-theme-gray-lighter;
6 6
7 &__action { 7 &__action {
8 &-background { background: rgba($dark-theme-black, .7); } 8 &-background {
9 background: rgba($dark-theme-black, 0.7);
10 }
9 11
10 button { 12 button {
11 color: $dark-theme-gray-lightest; 13 color: $dark-theme-gray-lightest;
12 14
13 .mdi { color: $dark-theme-gray-lightest; } 15 .mdi {
16 color: $dark-theme-gray-lightest;
17 }
14 } 18 }
15 } 19 }
16 } 20 }
17 21
18 .image-upload-wrapper .mdi { color: $dark-theme-gray-light; } 22 .image-upload-wrapper .mdi {
23 color: $dark-theme-gray-light;
24 }
19} 25}
20 26
21.image-upload { 27.image-upload {
@@ -49,11 +55,13 @@
49 justify-content: center; 55 justify-content: center;
50 opacity: 0; 56 opacity: 0;
51 position: relative; 57 position: relative;
52 transition: opacity .5s; 58 @media (prefers-reduced-motion: no-preference) {
59 transition: opacity 0.5s;
60 }
53 z-index: 10; 61 z-index: 10;
54 62
55 &-background { 63 &-background {
56 background: rgba($theme-gray, .7); 64 background: rgba($theme-gray, 0.7);
57 bottom: 0; 65 bottom: 0;
58 left: 0; 66 left: 0;
59 position: absolute; 67 position: absolute;
@@ -63,11 +71,13 @@
63 } 71 }
64 72
65 button { 73 button {
66 color: #FFF; 74 color: #fff;
67 position: relative; 75 position: relative;
68 z-index: 100; 76 z-index: 100;
69 77
70 .mdi { color: #FFF; } 78 .mdi {
79 color: #fff;
80 }
71 } 81 }
72 } 82 }
73 83
@@ -83,7 +93,9 @@
83 93
84 &__dropzone, 94 &__dropzone,
85 button { 95 button {
86 .mdi { margin-bottom: 5px; } 96 .mdi {
97 margin-bottom: 5px;
98 }
87 99
88 p { 100 p {
89 font-size: 10px; 101 font-size: 10px;
@@ -91,7 +103,9 @@
91 } 103 }
92 } 104 }
93 105
94 &:hover .image-upload__action { opacity: 1; } 106 &:hover .image-upload__action {
107 opacity: 1;
108 }
95} 109}
96 110
97.image-upload-wrapper .mdi { 111.image-upload-wrapper .mdi {
diff --git a/src/styles/layout.scss b/src/styles/layout.scss
index acbd65ad1..49e041022 100644
--- a/src/styles/layout.scss
+++ b/src/styles/layout.scss
@@ -1,6 +1,8 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3html { overflow: hidden; } 3html {
4 overflow: hidden;
5}
4 6
5@keyframes pulse-danger { 7@keyframes pulse-danger {
6 0% { 8 0% {
@@ -21,7 +23,7 @@ html { overflow: hidden; }
21 23
22 &::after { 24 &::after {
23 box-shadow: inset 0 0 5px 0 $dark-theme-black, 25 box-shadow: inset 0 0 5px 0 $dark-theme-black,
24 inset 0 0 2px 0 rgba(0, 0, 0, 0.4); 26 inset 0 0 2px 0 rgba(0, 0, 0, 0.4);
25 } 27 }
26 28
27 .sidebar__add-service { 29 .sidebar__add-service {
@@ -48,12 +50,12 @@ html { overflow: hidden; }
48 filter: grayscale(1); 50 filter: grayscale(1);
49 } 51 }
50 52
51 .update-available { 53 .update-available {
52 align-items: center; 54 align-items: center;
53 background: $theme-brand-danger; 55 background: $theme-brand-danger;
54 border-radius: 20px; 56 border-radius: 20px;
55 bottom: 5px; 57 bottom: 5px;
56 color: #FFF; 58 color: #fff;
57 display: flex; 59 display: flex;
58 justify-content: center; 60 justify-content: center;
59 padding: 0px 5px; 61 padding: 0px 5px;
@@ -71,7 +73,9 @@ html { overflow: hidden; }
71 } 73 }
72 } 74 }
73 75
74 .app-loader .app-loader__title { color: $dark-theme-gray-lightest; } 76 .app-loader .app-loader__title {
77 color: $dark-theme-gray-lightest;
78 }
75} 79}
76 80
77body.win32:not(.isFullScreen) .app .app__content { 81body.win32:not(.isFullScreen) .app .app__content {
@@ -96,7 +100,9 @@ body.win32:not(.isFullScreen) .app .app__content {
96 } 100 }
97} 101}
98 102
99.electron-app-title-bar { z-index: 99999999; } 103.electron-app-title-bar {
104 z-index: 99999999;
105}
100 106
101.window-draggable { 107.window-draggable {
102 height: 22px; 108 height: 22px;
@@ -109,7 +115,9 @@ body.win32:not(.isFullScreen) .app .app__content {
109 -webkit-app-region: drag; 115 -webkit-app-region: drag;
110} 116}
111 117
112.darwin .sidebar { padding-top: 23px; } 118.darwin .sidebar {
119 padding-top: 23px;
120}
113 121
114.sidebar { 122.sidebar {
115 position: relative; 123 position: relative;
@@ -133,8 +141,8 @@ body.win32:not(.isFullScreen) .app .app__content {
133 z-index: 1000; 141 z-index: 1000;
134 pointer-events: none; 142 pointer-events: none;
135 clip-path: inset(10px 0 10px 10px); 143 clip-path: inset(10px 0 10px 10px);
136 box-shadow: inset 0 0 10px 0 rgba(0, 0, 0, .12), 144 box-shadow: inset 0 0 10px 0 rgba(0, 0, 0, 0.12),
137 inset 0 0 2px 0 rgba(0, 0, 0, 0.24); 145 inset 0 0 2px 0 rgba(0, 0, 0, 0.24);
138 } 146 }
139 147
140 .sidebar__add-service { 148 .sidebar__add-service {
@@ -154,16 +162,25 @@ body.win32:not(.isFullScreen) .app .app__content {
154 width: $theme-sidebar-width; 162 width: $theme-sidebar-width;
155 163
156 &:hover, 164 &:hover,
157 &:active { color: lighten($theme-gray-light, 10%); } 165 &:active {
158 &.is-muted, &.is-active { color: $theme-brand-primary; } 166 color: lighten($theme-gray-light, 10%);
159 &--new-service { padding-bottom: 6px; } 167 }
168 &.is-muted,
169 &.is-active {
170 color: $theme-brand-primary;
171 }
172 &--new-service {
173 padding-bottom: 6px;
174 }
160 } 175 }
161 176
162 & > div { 177 & > div {
163 display: flex; 178 display: flex;
164 overflow-y: scroll; 179 overflow-y: scroll;
165 180
166 &::-webkit-scrollbar { display: none; } 181 &::-webkit-scrollbar {
182 display: none;
183 }
167 } 184 }
168} 185}
169 186
@@ -171,8 +188,12 @@ body.win32:not(.isFullScreen) .app .app__content {
171 display: flex; 188 display: flex;
172 flex-direction: row; 189 flex-direction: row;
173 190
174 & > * { margin-right: 20px; } 191 & > * {
175 & :last-child { margin-right: 0; } 192 margin-right: 20px;
193 }
194 & :last-child {
195 margin-right: 0;
196 }
176} 197}
177 198
178.app-loader { 199.app-loader {
@@ -181,19 +202,23 @@ body.win32:not(.isFullScreen) .app .app__content {
181 justify-content: center; 202 justify-content: center;
182 203
183 .app-loader__title { 204 .app-loader__title {
184 color: #FFF; 205 color: #fff;
185 font-size: 40px; 206 font-size: 40px;
186 } 207 }
187 208
188 & > span { height: auto; } 209 & > span {
210 height: auto;
211 }
189} 212}
190 213
191.dev-warning { display: none; } 214.dev-warning {
215 display: none;
216}
192 217
193.isDevMode .dev-warning { 218.isDevMode .dev-warning {
194 border-radius: 3px; 219 border-radius: 3px;
195 background: $theme-brand-warning; 220 background: $theme-brand-warning;
196 color: #FFF; 221 color: #fff;
197 display: block; 222 display: block;
198 font-size: 8px; 223 font-size: 8px;
199 height: auto; 224 height: auto;
@@ -201,12 +226,15 @@ body.win32:not(.isFullScreen) .app .app__content {
201 position: fixed; 226 position: fixed;
202 left: 9px; 227 left: 9px;
203 bottom: 0px; 228 bottom: 0px;
204 transition: opacity .5s ease; 229 @media (prefers-reduced-motion: no-preference) {
230 transition: opacity 0.5s ease;
231 }
205 width: auto; 232 width: auto;
206 z-index: 999999999; 233 z-index: 999999999;
207 pointer-events: none; 234 pointer-events: none;
208} 235}
209 236
210a, button { 237a,
238button {
211 cursor: pointer; 239 cursor: pointer;
212} 240}
diff --git a/src/styles/main.scss b/src/styles/main.scss
index 4cc996785..b0815e086 100644
--- a/src/styles/main.scss
+++ b/src/styles/main.scss
@@ -22,7 +22,6 @@ $mdi-font-path: '../node_modules/@mdi/font/fonts';
22@import './tooltip.scss'; 22@import './tooltip.scss';
23@import './info-bar.scss'; 23@import './info-bar.scss';
24@import './status-bar-target-url.scss'; 24@import './status-bar-target-url.scss';
25@import './animations.scss';
26@import './infobox.scss'; 25@import './infobox.scss';
27@import './badge.scss'; 26@import './badge.scss';
28@import './content-tabs.scss'; 27@import './content-tabs.scss';
diff --git a/src/styles/radio.scss b/src/styles/radio.scss
index b1e148ca0..e8297408d 100644
--- a/src/styles/radio.scss
+++ b/src/styles/radio.scss
@@ -11,9 +11,10 @@
11 } 11 }
12} 12}
13 13
14
15.franz-form { 14.franz-form {
16 .franz-form__radio-wrapper { display: flex; } 15 .franz-form__radio-wrapper {
16 display: flex;
17 }
17 18
18 .franz-form__radio { 19 .franz-form__radio {
19 border: 2px solid $theme-gray-lighter; 20 border: 2px solid $theme-gray-lighter;
@@ -24,18 +25,24 @@
24 margin-right: 20px; 25 margin-right: 20px;
25 padding: 11px; 26 padding: 11px;
26 text-align: center; 27 text-align: center;
27 transition: background $theme-transition-time; 28 @media (prefers-reduced-motion: no-preference) {
29 transition: background $theme-transition-time;
30 }
28 31
29 &:last-of-type { margin-right: 0; } 32 &:last-of-type {
33 margin-right: 0;
34 }
30 35
31 &.is-selected { 36 &.is-selected {
32 background: #FFF; 37 background: #fff;
33 border-width: 2px; 38 border-width: 2px;
34 border-style: solid; 39 border-style: solid;
35 border-color: $theme-brand-primary; 40 border-color: $theme-brand-primary;
36 color: $theme-brand-primary; 41 color: $theme-brand-primary;
37 } 42 }
38 43
39 input { display: none; } 44 input {
45 display: none;
46 }
40 } 47 }
41} 48}
diff --git a/src/styles/recipes.scss b/src/styles/recipes.scss
index 5bdc60a57..37c2febf6 100644
--- a/src/styles/recipes.scss
+++ b/src/styles/recipes.scss
@@ -4,7 +4,9 @@
4 background-color: $dark-theme-gray-dark; 4 background-color: $dark-theme-gray-dark;
5 color: $dark-theme-text-color; 5 color: $dark-theme-text-color;
6 6
7 &:hover { background-color: $dark-theme-gray; } 7 &:hover {
8 background-color: $dark-theme-gray;
9 }
8} 10}
9 11
10.recipes { 12.recipes {
@@ -17,7 +19,7 @@
17 19
18 &.recipes__list--disabled { 20 &.recipes__list--disabled {
19 filter: grayscale(100%); 21 filter: grayscale(100%);
20 opacity: .3; 22 opacity: 0.3;
21 pointer-events: none; 23 pointer-events: none;
22 } 24 }
23 } 25 }
@@ -26,16 +28,20 @@
26 height: auto; 28 height: auto;
27 margin-bottom: 35px; 29 margin-bottom: 35px;
28 30
29 .badge { margin-right: 10px; } 31 .badge {
32 margin-right: 10px;
33 }
30 34
31 &.recipes__navigation--disabled { 35 &.recipes__navigation--disabled {
32 filter: grayscale(100%); 36 filter: grayscale(100%);
33 opacity: .3; 37 opacity: 0.3;
34 pointer-events: none; 38 pointer-events: none;
35 } 39 }
36 } 40 }
37 41
38 &__service-request { float: right; } 42 &__service-request {
43 float: right;
44 }
39} 45}
40 46
41.recipe-teaser { 47.recipe-teaser {
@@ -45,24 +51,32 @@
45 margin: 0 20px 20px 0; 51 margin: 0 20px 20px 0;
46 overflow: hidden; 52 overflow: hidden;
47 position: relative; 53 position: relative;
48 transition: background $theme-transition-time; 54 @media (prefers-reduced-motion: no-preference) {
55 transition: background $theme-transition-time;
56 }
49 width: calc(25% - 20px); 57 width: calc(25% - 20px);
50 58
51 &:hover { background-color: $theme-gray-lighter; } 59 &:hover {
60 background-color: $theme-gray-lighter;
61 }
52 62
53 .recipe-teaser__icon { 63 .recipe-teaser__icon {
54 margin-bottom: 10px; 64 margin-bottom: 10px;
55 width: 50px; 65 width: 50px;
56 } 66 }
57 67
58 .recipe-teaser__label { display: block; } 68 .recipe-teaser__label {
69 display: block;
70 }
59 71
60 h2 { z-index: 10; } 72 h2 {
73 z-index: 10;
74 }
61 75
62 &__dev-badge { 76 &__dev-badge {
63 background: $theme-brand-warning; 77 background: $theme-brand-warning;
64 box-shadow: 0 0 4px rgba(black, .2); 78 box-shadow: 0 0 4px rgba(black, 0.2);
65 color: #FFF; 79 color: #fff;
66 font-size: 10px; 80 font-size: 10px;
67 position: absolute; 81 position: absolute;
68 right: -13px; 82 right: -13px;
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index 5d4e81a4f..501f97b98 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -1,63 +1,85 @@
1@import './config.scss'; 1@import './config.scss';
2 2
3%headline { 3%headline {
4 color: #FFF; 4 color: #fff;
5 font-size: 20px; 5 font-size: 20px;
6 font-weight: 400; 6 font-weight: 400;
7 letter-spacing: -1px; 7 letter-spacing: -1px;
8 8
9 a { color: #FFF } 9 a {
10 color: #fff;
11 }
10} 12}
11 13
12%headline__dark { 14%headline__dark {
13 color: #FFF; 15 color: #fff;
14 font-size: 20px; 16 font-size: 20px;
15 font-weight: 400; 17 font-weight: 400;
16 letter-spacing: -1px; 18 letter-spacing: -1px;
17 19
18 a { color: #FFF } 20 a {
21 color: #fff;
22 }
19} 23}
20 24
21.theme__dark { 25.theme__dark {
22 .settings-wrapper { background: rgba($dark-theme-black, .8); } 26 .settings-wrapper {
27 background: rgba($dark-theme-black, 0.8);
28 }
23 29
24 .settings { 30 .settings {
25 .settings__header { 31 .settings__header {
26 .mdi { color: #FFF } 32 .mdi {
33 color: #fff;
34 }
27 } 35 }
28 36
29 .settings__main { 37 .settings__main {
30 background: $dark-theme-gray-darkest; 38 background: $dark-theme-gray-darkest;
31 } 39 }
32 40
33 .settings__body::-webkit-scrollbar-thumb { background: $dark-theme-gray; } 41 .settings__body::-webkit-scrollbar-thumb {
42 background: $dark-theme-gray;
43 }
34 44
35 .settings__close { 45 .settings__close {
36 color: #FFF; 46 color: #fff;
37 } 47 }
38 48
39 &__settings-group h3 { color: $dark-theme-gray-lightest; } 49 &__settings-group h3 {
50 color: $dark-theme-gray-lightest;
51 }
40 52
41 .settings__message { 53 .settings__message {
42 border-top: 1px solid $theme-gray-lighter; 54 border-top: 1px solid $theme-gray-lighter;
43 color: $dark-theme-gray-lightest; 55 color: $dark-theme-gray-lightest;
44 56
45 .mdi { color: $dark-theme-gray-lightest; } 57 .mdi {
58 color: $dark-theme-gray-lightest;
59 }
46 } 60 }
47 61
48 .settings__help { color: $dark-theme-gray-lightest; } 62 .settings__help {
63 color: $dark-theme-gray-lightest;
64 }
49 65
50 .settings__controls { 66 .settings__controls {
51 background: $dark-theme-gray-darker; 67 background: $dark-theme-gray-darker;
52 68
53 .franz-form__button.franz-form__button--secondary { background: $theme-gray-light; } 69 .franz-form__button.franz-form__button--secondary {
70 background: $theme-gray-light;
71 }
54 } 72 }
55 73
56 .account { 74 .account {
57 .account__box { background: $dark-theme-gray-darker; } 75 .account__box {
76 background: $dark-theme-gray-darker;
77 }
58 } 78 }
59 79
60 .legal { color: $theme-gray-light; } 80 .legal {
81 color: $theme-gray-light;
82 }
61 } 83 }
62 84
63 .settings-navigation { 85 .settings-navigation {
@@ -69,7 +91,7 @@
69 border-bottom: 1px solid darken($dark-theme-gray-darker, 3%); 91 border-bottom: 1px solid darken($dark-theme-gray-darker, 3%);
70 92
71 &:last-child { 93 &:last-child {
72 border: 0, 94 border: 0;
73 } 95 }
74 96
75 .badge { 97 .badge {
@@ -87,7 +109,7 @@
87 } 109 }
88 110
89 &.is-disabled { 111 &.is-disabled {
90 filter: grayscale(100%) opacity(.2); 112 filter: grayscale(100%) opacity(0.2);
91 } 113 }
92 114
93 &.is-active { 115 &.is-active {
@@ -101,13 +123,15 @@
101 } 123 }
102 } 124 }
103 125
104 .settings-navigation__action-badge { background: $theme-brand-danger; } 126 .settings-navigation__action-badge {
127 background: $theme-brand-danger;
128 }
105 } 129 }
106} 130}
107 131
108.settings-wrapper { 132.settings-wrapper {
109 align-items: center; 133 align-items: center;
110 background: rgba(black, .5); 134 background: rgba(black, 0.5);
111 display: flex; 135 display: flex;
112 height: 100%; 136 height: 100%;
113 left: 0; 137 left: 0;
@@ -129,7 +153,7 @@
129 153
130.settings { 154.settings {
131 border-radius: $theme-border-radius; 155 border-radius: $theme-border-radius;
132 box-shadow: 0 20px 50px rgba($dark-theme-black, .5); 156 box-shadow: 0 20px 50px rgba($dark-theme-black, 0.5);
133 display: flex; 157 display: flex;
134 height: 100%; 158 height: 100%;
135 max-height: 720px; 159 max-height: 720px;
@@ -147,7 +171,7 @@
147 height: auto; 171 height: auto;
148 border-radius: 0 $theme-border-radius $theme-border-radius 0; 172 border-radius: 0 $theme-border-radius $theme-border-radius 0;
149 overflow: hidden; 173 overflow: hidden;
150 background: #FFF; 174 background: #fff;
151 } 175 }
152 176
153 .settings__header { 177 .settings__header {
@@ -157,7 +181,7 @@
157 height: 50px; 181 height: 50px;
158 padding: 0 40px; 182 padding: 0 40px;
159 width: calc(100% - 60px); 183 width: calc(100% - 60px);
160 color: #FFF; 184 color: #fff;
161 185
162 h1 { 186 h1 {
163 @extend %headline; 187 @extend %headline;
@@ -177,11 +201,13 @@
177 transform: skew(15deg) rotate(2deg); 201 transform: skew(15deg) rotate(2deg);
178 } 202 }
179 203
180 .mdi { color: $theme-gray-light; } 204 .mdi {
205 color: $theme-gray-light;
206 }
181 } 207 }
182 208
183 .settings_titles { 209 .settings_titles {
184 display:inline-block; 210 display: inline-block;
185 } 211 }
186 212
187 .settings__body { 213 .settings__body {
@@ -190,9 +216,13 @@
190 overflow-y: scroll; 216 overflow-y: scroll;
191 padding: 25px 15px 15px 25px; 217 padding: 25px 15px 15px 25px;
192 218
193 .badge { margin-right: 10px; } 219 .badge {
220 margin-right: 10px;
221 }
194 222
195 &::-webkit-scrollbar { width: 8px; } 223 &::-webkit-scrollbar {
224 width: 8px;
225 }
196 226
197 /* Track */ 227 /* Track */
198 &::-webkit-scrollbar-track { 228 &::-webkit-scrollbar-track {
@@ -208,9 +238,16 @@
208 -webkit-border-radius: 10px; 238 -webkit-border-radius: 10px;
209 } 239 }
210 240
211 &::-webkit-scrollbar-thumb:window-inactive { background: none; } 241 &::-webkit-scrollbar-thumb:window-inactive {
212 .service-flex-grid { display: flex; } 242 background: none;
213 .service-name,.user-agent { flex: 1px; } 243 }
244 .service-flex-grid {
245 display: flex;
246 }
247 .service-name,
248 .user-agent {
249 flex: 1px;
250 }
214 251
215 .service-icon { 252 .service-icon {
216 float: right; 253 float: right;
@@ -232,20 +269,28 @@
232 .settings__close { 269 .settings__close {
233 background: $theme-brand-primary; 270 background: $theme-brand-primary;
234 // border-left: 1px solid darken($theme-brand-primary, 8%); 271 // border-left: 1px solid darken($theme-brand-primary, 8%);
235 color: #FFF; 272 color: #fff;
236 font-size: 20px; 273 font-size: 20px;
237 height: 50px; 274 height: 50px;
238 padding: 0 20px; 275 padding: 0 20px;
239 position: absolute; 276 position: absolute;
240 right: 0; 277 right: 0;
241 transition: background $theme-transition-time; 278 @media (prefers-reduced-motion: no-preference) {
279 transition: background $theme-transition-time;
280 }
242 cursor: pointer; 281 cursor: pointer;
243 282
244 &::before { cursor: pointer; } 283 &::before {
245 &:hover { background: darken($theme-brand-primary, 5%); } 284 cursor: pointer;
285 }
286 &:hover {
287 background: darken($theme-brand-primary, 5%);
288 }
246 } 289 }
247 290
248 .search-input { margin-bottom: 30px; } 291 .search-input {
292 margin-bottom: 30px;
293 }
249 294
250 &__options { 295 &__options {
251 flex: 1; 296 flex: 1;
@@ -258,10 +303,12 @@
258 h3 { 303 h3 {
259 color: $theme-gray-light; 304 color: $theme-gray-light;
260 font-weight: bold; 305 font-weight: bold;
261 letter-spacing: -.1px; 306 letter-spacing: -0.1px;
262 margin: 25px 0 15px; 307 margin: 25px 0 15px;
263 308
264 &:first-of-type { margin-top: 0; } 309 &:first-of-type {
310 margin-top: 0;
311 }
265 312
266 .badge { 313 .badge {
267 font-weight: normal; 314 font-weight: normal;
@@ -302,14 +349,20 @@
302 padding: 10px 20px; 349 padding: 10px 20px;
303 350
304 .franz-form__button { 351 .franz-form__button {
305 &[type='submit'] { margin-left: auto; } 352 &[type='submit'] {
306 &.franz-form__button--secondary { background: $theme-gray-light; } 353 margin-left: auto;
354 }
355 &.franz-form__button--secondary {
356 background: $theme-gray-light;
357 }
307 } 358 }
308 } 359 }
309 360
310 .settings__delete-button { right: 0; } 361 .settings__delete-button {
362 right: 0;
363 }
311 .settings__open-recipe-file-button { 364 .settings__open-recipe-file-button {
312 cursor:pointer; 365 cursor: pointer;
313 margin-right: 10px; 366 margin-right: 10px;
314 } 367 }
315 .settings__open-recipe-file-container { 368 .settings__open-recipe-file-container {
@@ -330,7 +383,9 @@
330 margin-bottom: 1em; 383 margin-bottom: 1em;
331 } 384 }
332 385
333 a.button { margin-top: 40px; } 386 a.button {
387 margin-top: 40px;
388 }
334 } 389 }
335 390
336 .account { 391 .account {
@@ -343,8 +398,12 @@
343 margin-bottom: 40px; 398 margin-bottom: 40px;
344 padding: 20px; 399 padding: 20px;
345 400
346 &.account__box--flex { display: flex; } 401 &.account__box--flex {
347 &.account__box--last { margin-bottom: 0; } 402 display: flex;
403 }
404 &.account__box--last {
405 margin-bottom: 0;
406 }
348 407
349 .auth__button { 408 .auth__button {
350 margin-top: 10px; 409 margin-top: 10px;
@@ -360,16 +419,24 @@
360 .account__info { 419 .account__info {
361 flex: 1; 420 flex: 1;
362 421
363 h2 { margin-bottom: 5px; } 422 h2 {
364 .badge { margin-top: 5px; } 423 margin-bottom: 5px;
365 .username { margin-right: 10 } 424 }
425 .badge {
426 margin-top: 5px;
427 }
428 .username {
429 margin-right: 10;
430 }
366 } 431 }
367 432
368 .account__subscription { 433 .account__subscription {
369 align-items: center; 434 align-items: center;
370 display: flex; 435 display: flex;
371 436
372 .badge { margin-left: 10px; } 437 .badge {
438 margin-left: 10px;
439 }
373 } 440 }
374 441
375 .manage-user-links { 442 .manage-user-links {
@@ -378,10 +445,18 @@
378 justify-content: space-between; 445 justify-content: space-between;
379 } 446 }
380 447
381 .account__subscription-button { margin-left: auto; } 448 .account__subscription-button {
382 .franz-form__button { white-space: nowrap; } 449 margin-left: auto;
383 div { height: auto; } 450 }
384 [data-type="franz-button"] div { height: 20px } 451 .franz-form__button {
452 white-space: nowrap;
453 }
454 div {
455 height: auto;
456 }
457 [data-type='franz-button'] div {
458 height: 20px;
459 }
385 460
386 .invoices { 461 .invoices {
387 width: 100%; 462 width: 100%;
@@ -399,7 +474,9 @@
399 .invoices__action { 474 .invoices__action {
400 text-align: right; 475 text-align: right;
401 476
402 button { color: $theme-brand-primary; } 477 button {
478 color: $theme-brand-primary;
479 }
403 } 480 }
404 } 481 }
405 } 482 }
@@ -409,7 +486,9 @@
409 font-size: 40px; 486 font-size: 40px;
410 margin-bottom: 20px; 487 margin-bottom: 20px;
411 488
412 img { width: 40px; } 489 img {
490 width: 40px;
491 }
413 } 492 }
414 493
415 .content-tabs { 494 .content-tabs {
@@ -432,8 +511,8 @@
432 width: 240px; 511 width: 240px;
433 height: 100%; 512 height: 100%;
434 align-self: center; 513 align-self: center;
435 border-top-left-radius: $theme-border-radius;; 514 border-top-left-radius: $theme-border-radius;
436 border-bottom-left-radius: $theme-border-radius;; 515 border-bottom-left-radius: $theme-border-radius;
437 overflow: hidden; 516 overflow: hidden;
438 517
439 .settings-navigation__link { 518 .settings-navigation__link {
@@ -445,12 +524,15 @@
445 height: 51px; 524 height: 51px;
446 padding: 0 20px; 525 padding: 0 20px;
447 text-decoration: none; 526 text-decoration: none;
448 transition: background $theme-transition-time, color $theme-transition-time; 527 @media (prefers-reduced-motion: no-preference) {
528 transition: background $theme-transition-time,
529 color $theme-transition-time;
530 }
449 border-bottom: 1px solid darken($theme-gray-lightest, 3%); 531 border-bottom: 1px solid darken($theme-gray-lightest, 3%);
450 532
451 .badge { 533 .badge {
452 background: $theme-gray-light; 534 background: $theme-gray-light;
453 color: #FFF; 535 color: #fff;
454 } 536 }
455 537
456 &:hover { 538 &:hover {
@@ -458,26 +540,31 @@
458 540
459 .badge { 541 .badge {
460 background: $theme-gray-light; 542 background: $theme-gray-light;
461 color: #FFF; 543 color: #fff;
462 } 544 }
463 } 545 }
464 546
465 &.is-active { 547 &.is-active {
466 background: $theme-brand-primary; 548 background: $theme-brand-primary;
467 color: #FFF; 549 color: #fff;
468 550
469 .badge { 551 .badge {
470 background: #FFF; 552 background: #fff;
471 color: $theme-brand-primary; 553 color: $theme-brand-primary;
472 } 554 }
473 } 555 }
474 } 556 }
475 557
476 .settings-navigation__expander { flex: 1; } 558 .settings-navigation__expander {
559 flex: 1;
560 }
477 561
478 .badge { 562 .badge {
479 display: initial; 563 display: initial;
480 transition: background $theme-transition-time, color $theme-transition-time; 564 @media (prefers-reduced-motion: no-preference) {
565 transition: background $theme-transition-time,
566 color $theme-transition-time;
567 }
481 } 568 }
482 569
483 .settings-navigation__action-badge { 570 .settings-navigation__action-badge {
@@ -491,5 +578,7 @@
491} 578}
492 579
493.settings__support-badges { 580.settings__support-badges {
494 a { margin-right: 10px } 581 a {
582 margin-right: 10px;
583 }
495} 584}
diff --git a/src/styles/slider.scss b/src/styles/slider.scss
index 8bb771586..85b31660f 100644
--- a/src/styles/slider.scss
+++ b/src/styles/slider.scss
@@ -3,43 +3,44 @@
3.theme__dark .franz-form .franz-form__slider-wrapper .slider { 3.theme__dark .franz-form .franz-form__slider-wrapper .slider {
4 border: 1px solid $dark-theme-gray; 4 border: 1px solid $dark-theme-gray;
5 background: $dark-theme-gray; 5 background: $dark-theme-gray;
6
7} 6}
8 7
9
10.franz-form { 8.franz-form {
11 .franz-form__slider-wrapper { 9 .franz-form__slider-wrapper {
12 display: flex; 10 display: flex;
13 flex-direction: row; 11 flex-direction: row;
14 12
15 .franz-form__label { margin-left: 20px; } 13 .franz-form__label {
14 margin-left: 20px;
15 }
16 16
17 .slider-container { 17 .slider-container {
18 width: 100%; /* Width of the outside container */ 18 width: 100%; /* Width of the outside container */
19 } 19 }
20 20
21 /* The slider itself */ 21 /* The slider itself */
22 .slider { 22 .slider {
23 -webkit-appearance: none; 23 -webkit-appearance: none;
24 width: 100%; 24 width: 100%;
25 height: 14px; 25 height: 14px;
26 border-radius: $theme-border-radius; 26 border-radius: $theme-border-radius;
27 background: $theme-gray-lighter; 27 background: $theme-gray-lighter;
28 outline: none; 28 outline: none;
29 opacity: 1.0; 29 opacity: 1;
30 -webkit-transition: .2s; 30 @media (prefers-reduced-motion: no-preference) {
31 transition: opacity .2s; 31 transition: opacity 0.2s;
32 }
32 } 33 }
33 34
34 .slider::-webkit-slider-thumb { 35 .slider::-webkit-slider-thumb {
35 -webkit-appearance: none; 36 -webkit-appearance: none;
36 appearance: none; 37 appearance: none;
37 width: 14px; 38 width: 14px;
38 height: 14px; 39 height: 14px;
39 border-radius: 50%; 40 border-radius: 50%;
40 background: $theme-brand-primary; 41 background: $theme-brand-primary;
41 box-shadow: 0 1px 4px rgba(0, 0, 0, .3); 42 box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
42 cursor: pointer; 43 cursor: pointer;
43 } 44 }
44 } 45 }
45} 46}
diff --git a/src/styles/tabs.scss b/src/styles/tabs.scss
index 31a239387..df10da77c 100644
--- a/src/styles/tabs.scss
+++ b/src/styles/tabs.scss
@@ -4,11 +4,17 @@
4 &.is-active { 4 &.is-active {
5 background: $dark-theme-gray; 5 background: $dark-theme-gray;
6 6
7 .tab-item__icon { margin-left: -4px; } 7 .tab-item__icon {
8 margin-left: -4px;
9 }
8 } 10 }
9 11
10 &.is-disabled .tab-item__icon { filter: grayscale(100%) opacity(.2); } 12 &.is-disabled .tab-item__icon {
11 .tab-item__icon { width: 34px; } 13 filter: grayscale(100%) opacity(0.2);
14 }
15 .tab-item__icon {
16 width: 34px;
17 }
12} 18}
13 19
14.tabs { 20.tabs {
@@ -29,22 +35,34 @@
29 justify-content: center; 35 justify-content: center;
30 min-height: 50px; 36 min-height: 50px;
31 position: relative; 37 position: relative;
32 transition: background $theme-transition-time; 38 @media (prefers-reduced-motion: no-preference) {
39 transition: background $theme-transition-time;
40 }
33 width: $theme-sidebar-width; 41 width: $theme-sidebar-width;
34 42
35 &.is-active { 43 &.is-active {
36 background: change-color($theme-brand-primary, 44 background: change-color(
37 $lightness: min(lightness($theme-brand-primary) * 1.35, 100)); 45 $theme-brand-primary,
46 $lightness: min(lightness($theme-brand-primary) * 1.35, 100)
47 );
38 border-left-width: 4px; 48 border-left-width: 4px;
39 border-left-style: solid; 49 border-left-style: solid;
40 color: $theme-brand-primary; 50 color: $theme-brand-primary;
41 51
42 .tab-item__icon { margin-left: -4px; } 52 .tab-item__icon {
53 margin-left: -4px;
54 }
43 } 55 }
44 56
45 &.is-disabled .tab-item__icon { filter: grayscale(100%) opacity(0.2); } 57 &.is-disabled .tab-item__icon {
46 &.has-custom-icon .tab-item__icon { border-radius: $theme-border-radius; } 58 filter: grayscale(100%) opacity(0.2);
47 &:active .tab-item__icon { opacity: .7; } 59 }
60 &.has-custom-icon .tab-item__icon {
61 border-radius: $theme-border-radius;
62 }
63 &:active .tab-item__icon {
64 opacity: 0.7;
65 }
48 66
49 .tab-item__icon { 67 .tab-item__icon {
50 height: auto; 68 height: auto;
@@ -56,7 +74,7 @@
56 background: $theme-brand-danger; 74 background: $theme-brand-danger;
57 border-radius: 20px; 75 border-radius: 20px;
58 bottom: 8px; 76 bottom: 8px;
59 color: #FFF; 77 color: #fff;
60 display: flex; 78 display: flex;
61 font-size: 11px; 79 font-size: 11px;
62 justify-content: center; 80 justify-content: center;
@@ -66,13 +84,13 @@
66 position: absolute; 84 position: absolute;
67 right: 8px; 85 right: 8px;
68 86
69 &.is-indirect { 87 &.is-indirect {
70 padding-top: 0; 88 padding-top: 0;
71 background: #0088cc; 89 background: #0088cc;
72 } 90 }
73 &.hibernating { 91 &.hibernating {
74 padding-top: 0; 92 padding-top: 0;
75 background: $theme-gray; 93 background: $theme-gray;
76 font-size: 0px; 94 font-size: 0px;
77 min-height: 10px; 95 min-height: 10px;
78 min-width: 10px; 96 min-width: 10px;
@@ -96,11 +114,13 @@
96 right: 8px; 114 right: 8px;
97 width: 17px; 115 width: 17px;
98 116
99 &.is-indirect { 117 &.is-indirect {
100 padding-top: 0; 118 padding-top: 0;
101 background: #0088cc; 119 background: #0088cc;
102 } 120 }
103 } 121 }
104 122
105 &.is-reordering { z-index: 99999; } 123 &.is-reordering {
124 z-index: 99999;
125 }
106} 126}
diff --git a/src/styles/toggle.scss b/src/styles/toggle.scss
index 5cd9e4526..ed4c0d11b 100644
--- a/src/styles/toggle.scss
+++ b/src/styles/toggle.scss
@@ -39,7 +39,9 @@ $toggle-button-size: 22px;
39 left: 1px; 39 left: 1px;
40 top: 1px; 40 top: 1px;
41 position: absolute; 41 position: absolute;
42 transition: all 0.5s; 42 @media (prefers-reduced-motion: no-preference) {
43 transition: all 0.5s;
44 }
43 width: $toggle-size - 2; 45 width: $toggle-size - 2;
44 } 46 }
45 47
diff --git a/src/styles/type.scss b/src/styles/type.scss
index 37ec0bcca..234c4d5c4 100644
--- a/src/styles/type.scss
+++ b/src/styles/type.scss
@@ -2,9 +2,15 @@
2@import './mixins.scss'; 2@import './mixins.scss';
3 3
4.theme__dark { 4.theme__dark {
5 a { color: $dark-theme-gray-smoke; } 5 a {
6 .label { color: $dark-theme-gray-lightest; } 6 color: $dark-theme-gray-smoke;
7 .footnote { color: $dark-theme-gray-lightest; } 7 }
8 .label {
9 color: $dark-theme-gray-lightest;
10 }
11 .footnote {
12 color: $dark-theme-gray-lightest;
13 }
8} 14}
9 15
10h1 { 16h1 {
@@ -21,36 +27,45 @@ h2 {
21 margin-bottom: 25px; 27 margin-bottom: 25px;
22 margin-top: 5px; 28 margin-top: 5px;
23 29
24 &:first-of-type { margin-top: 0; } 30 &:first-of-type {
31 margin-top: 0;
32 }
25} 33}
26 34
27p { 35p {
28 margin-bottom: 10px; 36 margin-bottom: 10px;
29 line-height: 1.7rem; 37 line-height: 1.7rem;
30 38
31 &:last-of-type { margin-bottom: 0; } 39 &:last-of-type {
40 margin-bottom: 0;
41 }
32} 42}
33 43
34strong { font-weight: bold; } 44strong {
45 font-weight: bold;
46}
35 47
36a, button { 48a,
49button {
37 color: $theme-text-color; 50 color: $theme-text-color;
38 text-decoration: none; 51 text-decoration: none;
39 52
40 &.button { 53 &.button {
41 background: $theme-brand-primary; 54 background: $theme-brand-primary;
42 color: #FFF; 55 color: #fff;
43 border-radius: 3px; 56 border-radius: 3px;
44 display: inline-block; 57 display: inline-block;
45 padding: 10px 20px; 58 padding: 10px 20px;
46 position: relative; 59 position: relative;
47 text-align: center; 60 text-align: center;
48 transition: background .5s, color .5s; 61 @media (prefers-reduced-motion: no-preference) {
62 transition: background 0.5s, color 0.5s;
63 }
49 cursor: pointer; 64 cursor: pointer;
50 65
51 &:hover { 66 &:hover {
52 background: darken($theme-brand-primary, 10%); 67 background: darken($theme-brand-primary, 10%);
53 color: #FFF; 68 color: #fff;
54 } 69 }
55 } 70 }
56 71
@@ -60,14 +75,19 @@ a, button {
60 } 75 }
61} 76}
62 77
63.error-message, .error-message:last-of-type { 78.error-message,
79.error-message:last-of-type {
64 color: $theme-brand-danger; 80 color: $theme-brand-danger;
65 margin: 10px 0; 81 margin: 10px 0;
66} 82}
67 83
68.center { text-align: center; } 84.center {
85 text-align: center;
86}
69 87
70.label { @include formLabel(); } 88.label {
89 @include formLabel();
90}
71 91
72.footnote { 92.footnote {
73 color: $theme-gray-light; 93 color: $theme-gray-light;
diff --git a/src/styles/welcome.scss b/src/styles/welcome.scss
index c1f85391e..7202fe148 100644
--- a/src/styles/welcome.scss
+++ b/src/styles/welcome.scss
@@ -1,24 +1,26 @@
1.auth .welcome { 1.auth .welcome {
2 height: auto; 2 height: auto;
3 3
4 &__content { 4 &__content {
5 align-items: center; 5 align-items: center;
6 color: #FFF; 6 color: #fff;
7 display: flex; 7 display: flex;
8 justify-content: center; 8 justify-content: center;
9 height: auto; 9 height: auto;
10 } 10 }
11 11
12 &__logo { width: 100px; } 12 &__logo {
13 width: 100px;
14 }
13 15
14 &__text { 16 &__text {
15 border-left: 1px solid #FFF; 17 border-left: 1px solid #fff;
16 margin-left: 40px; 18 margin-left: 40px;
17 padding-left: 40px; 19 padding-left: 40px;
18 20
19 h1 { 21 h1 {
20 font-size: 60px; 22 font-size: 60px;
21 letter-spacing: -.4rem; 23 letter-spacing: -0.4rem;
22 margin-bottom: 5px; 24 margin-bottom: 5px;
23 } 25 }
24 26
@@ -42,33 +44,35 @@
42 text-align: center; 44 text-align: center;
43 height: auto; 45 height: auto;
44 46
45 .button:first-of-type { margin-right: 25px; } 47 .button:first-of-type {
48 margin-right: 25px;
49 }
46 } 50 }
47 51
48 .button { 52 .button {
49 border-color: #FFF; 53 border-color: #fff;
50 color: #FFF; 54 color: #fff;
51 cursor: pointer; 55 cursor: pointer;
52 56
53 &:hover { 57 &:hover {
54 background: #FFF; 58 background: #fff;
55 color: $theme-brand-primary; 59 color: $theme-brand-primary;
56 } 60 }
57 61
58 &__inverted { 62 &__inverted {
59 background: #FFF; 63 background: #fff;
60 color: $theme-brand-primary; 64 color: $theme-brand-primary;
61 } 65 }
62 66
63 &__inverted:hover { 67 &__inverted:hover {
64 background: none; 68 background: none;
65 color: #FFF; 69 color: #fff;
66 } 70 }
67 } 71 }
68 72
69 &__featured-services { 73 &__featured-services {
70 align-items: center; 74 align-items: center;
71 background: #FFF; 75 background: #fff;
72 border-radius: 6px; 76 border-radius: 6px;
73 display: flex; 77 display: flex;
74 flex-wrap: wrap; 78 flex-wrap: wrap;
@@ -82,9 +86,13 @@
82 &__featured-service { 86 &__featured-service {
83 margin: 0 10px 15px; 87 margin: 0 10px 15px;
84 height: 35px; 88 height: 35px;
85 transition: .5s filter, .5s opacity; 89 @media (prefers-reduced-motion: no-preference) {
90 transition: 0.5s filter, 0.5s opacity;
91 }
86 width: 35px; 92 width: 35px;
87 93
88 img { width: 35px; } 94 img {
95 width: 35px;
96 }
89 } 97 }
90} 98}
diff --git a/src/webview/screenshare.js b/src/webview/screenshare.js
index ab548a625..e7e43c04e 100644
--- a/src/webview/screenshare.js
+++ b/src/webview/screenshare.js
@@ -3,17 +3,23 @@ import { desktopCapturer } from 'electron';
3const CANCEL_ID = 'desktop-capturer-selection__cancel'; 3const CANCEL_ID = 'desktop-capturer-selection__cancel';
4 4
5export async function getDisplayMediaSelector() { 5export async function getDisplayMediaSelector() {
6 const sources = await desktopCapturer.getSources({ types: ['screen', 'window'] }); 6 const sources = await desktopCapturer.getSources({
7 types: ['screen', 'window'],
8 });
7 return `<div class="desktop-capturer-selection__scroller"> 9 return `<div class="desktop-capturer-selection__scroller">
8 <ul class="desktop-capturer-selection__list"> 10 <ul class="desktop-capturer-selection__list">
9 ${sources.map(({ id, name, thumbnail }) => ` 11 ${sources
12 .map(
13 ({ id, name, thumbnail }) => `
10 <li class="desktop-capturer-selection__item"> 14 <li class="desktop-capturer-selection__item">
11 <button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}"> 15 <button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}">
12 <img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" /> 16 <img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" />
13 <span class="desktop-capturer-selection__name">${name}</span> 17 <span class="desktop-capturer-selection__name">${name}</span>
14 </button> 18 </button>
15 </li> 19 </li>
16 `).join('')} 20 `,
21 )
22 .join('')}
17 <li class="desktop-capturer-selection__item"> 23 <li class="desktop-capturer-selection__item">
18 <button class="desktop-capturer-selection__btn" data-id="${CANCEL_ID}" title="Cancel"> 24 <button class="desktop-capturer-selection__btn" data-id="${CANCEL_ID}" title="Cancel">
19 <span class="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">Cancel</span> 25 <span class="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">Cancel</span>
@@ -67,7 +73,9 @@ export const screenShareCss = `
67 padding: 4px; 73 padding: 4px;
68 background: #252626; 74 background: #252626;
69 text-align: left; 75 text-align: left;
70 transition: background-color .15s, box-shadow .15s, color .15s; 76 @media (prefers-reduced-motion: no-preference) {
77 transition: background-color .15s, box-shadow .15s, color .15s;
78 }
71 color: #dedede; 79 color: #dedede;
72} 80}
73.desktop-capturer-selection__btn:hover, 81.desktop-capturer-selection__btn:hover,