aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/actions/app.js1
-rw-r--r--src/actions/service.js3
-rw-r--r--src/api/LocalApi.js8
-rw-r--r--src/api/ServicesApi.js7
-rw-r--r--src/api/server/LocalApi.js34
-rw-r--r--src/api/server/ServerApi.js6
-rw-r--r--src/components/settings/settings/EditSettingsForm.js35
-rw-r--r--src/containers/settings/EditSettingsScreen.js17
-rw-r--r--src/electron/ipc-api/autoUpdate.js3
-rw-r--r--src/helpers/service-helpers.js20
-rw-r--r--src/i18n/locales/en-US.json2
-rw-r--r--src/index.js1
-rw-r--r--src/stores/AppStore.js32
-rw-r--r--src/stores/ServicesStore.js9
-rw-r--r--src/styles/button.scss12
15 files changed, 185 insertions, 5 deletions
diff --git a/src/actions/app.js b/src/actions/app.js
index e4f648fc9..e6f7f22ba 100644
--- a/src/actions/app.js
+++ b/src/actions/app.js
@@ -25,4 +25,5 @@ export default {
25 overrideSystemMute: PropTypes.bool, 25 overrideSystemMute: PropTypes.bool,
26 }, 26 },
27 toggleMuteApp: {}, 27 toggleMuteApp: {},
28 clearAllCache: {},
28}; 29};
diff --git a/src/actions/service.js b/src/actions/service.js
index e3100e986..5d483b12a 100644
--- a/src/actions/service.js
+++ b/src/actions/service.js
@@ -25,6 +25,9 @@ export default {
25 serviceId: PropTypes.string.isRequired, 25 serviceId: PropTypes.string.isRequired,
26 redirect: PropTypes.string, 26 redirect: PropTypes.string,
27 }, 27 },
28 clearCache: {
29 serviceId: PropTypes.string.isRequired,
30 },
28 setUnreadMessageCount: { 31 setUnreadMessageCount: {
29 serviceId: PropTypes.string.isRequired, 32 serviceId: PropTypes.string.isRequired,
30 count: PropTypes.object.isRequired, 33 count: PropTypes.object.isRequired,
diff --git a/src/api/LocalApi.js b/src/api/LocalApi.js
index 6f2b049d6..3f84f8a0b 100644
--- a/src/api/LocalApi.js
+++ b/src/api/LocalApi.js
@@ -15,4 +15,12 @@ export default class LocalApi {
15 removeKey(key) { 15 removeKey(key) {
16 return this.local.removeKey(key); 16 return this.local.removeKey(key);
17 } 17 }
18
19 getAppCacheSize() {
20 return this.local.getAppCacheSize();
21 }
22
23 clearAppCache() {
24 return this.local.clearAppCache();
25 }
18} 26}
diff --git a/src/api/ServicesApi.js b/src/api/ServicesApi.js
index 3cb40ba0d..36ed9482f 100644
--- a/src/api/ServicesApi.js
+++ b/src/api/ServicesApi.js
@@ -1,5 +1,6 @@
1export default class ServicesApi { 1export default class ServicesApi {
2 constructor(server) { 2 constructor(server, local) {
3 this.local = local;
3 this.server = server; 4 this.server = server;
4 } 5 }
5 6
@@ -30,4 +31,8 @@ export default class ServicesApi {
30 reorder(data) { 31 reorder(data) {
31 return this.server.reorderService(data); 32 return this.server.reorderService(data);
32 } 33 }
34
35 clearCache(serviceId) {
36 return this.local.clearCache(serviceId);
37 }
33} 38}
diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js
index 79ac6e12f..e95d750ac 100644
--- a/src/api/server/LocalApi.js
+++ b/src/api/server/LocalApi.js
@@ -1,3 +1,10 @@
1import { remote } from 'electron';
2import du from 'du';
3
4import { getServicePartitionsDirectory } from '../../helpers/service-helpers.js';
5
6const { session } = remote;
7
1export default class LocalApi { 8export default class LocalApi {
2 // App 9 // App
3 async updateAppSettings(data) { 10 async updateAppSettings(data) {
@@ -30,4 +37,31 @@ export default class LocalApi {
30 localStorage.setItem('app', JSON.stringify(settings)); 37 localStorage.setItem('app', JSON.stringify(settings));
31 } 38 }
32 } 39 }
40
41 // Services
42 async getAppCacheSize() {
43 const partitionsDir = getServicePartitionsDirectory();
44 return new Promise((resolve, reject) => {
45 du(partitionsDir, (err, size) => {
46 if (err) reject(err);
47
48 console.debug('LocalApi::getAppCacheSize resolves', size);
49 resolve(size);
50 });
51 });
52 }
53
54 async clearCache(serviceId) {
55 const s = session.fromPartition(`persist:service-${serviceId}`);
56
57 console.debug('LocalApi::clearCache resolves', serviceId);
58 return new Promise(resolve => s.clearCache(resolve));
59 }
60
61 async clearAppCache() {
62 const s = session.defaultSession;
63
64 console.debug('LocalApi::clearCache clearAppCache');
65 return new Promise(resolve => s.clearCache(resolve));
66 }
33} 67}
diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js
index 8b3136d27..d75d2e559 100644
--- a/src/api/server/ServerApi.js
+++ b/src/api/server/ServerApi.js
@@ -22,6 +22,10 @@ import {
22 loadRecipeConfig, 22 loadRecipeConfig,
23} from '../../helpers/recipe-helpers'; 23} from '../../helpers/recipe-helpers';
24 24
25import {
26 removeServicePartitionDirectory,
27} from '../../helpers/service-helpers.js';
28
25module.paths.unshift( 29module.paths.unshift(
26 getDevRecipeDirectory(), 30 getDevRecipeDirectory(),
27 getRecipeDirectory(), 31 getRecipeDirectory(),
@@ -210,6 +214,8 @@ export default class ServerApi {
210 } 214 }
211 const data = await request.json(); 215 const data = await request.json();
212 216
217 removeServicePartitionDirectory(id, true);
218
213 console.debug('ServerApi::deleteService resolves', data); 219 console.debug('ServerApi::deleteService resolves', data);
214 return data; 220 return data;
215 } 221 }
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index ff398aa33..4f027638c 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -40,6 +40,14 @@ const messages = defineMessages({
40 id: 'settings.app.translationHelp', 40 id: 'settings.app.translationHelp',
41 defaultMessage: '!!!Help us to translate Franz into your language.', 41 defaultMessage: '!!!Help us to translate Franz into your language.',
42 }, 42 },
43 cacheInfo: {
44 id: 'settings.app.cacheInfo',
45 defaultMessage: '!!!Franz cache is currently using {size} of disk space.',
46 },
47 buttonClearAllCache: {
48 id: 'settings.app.buttonClearAllCache',
49 defaultMessage: '!!!Clear cache',
50 },
43 buttonSearchForUpdate: { 51 buttonSearchForUpdate: {
44 id: 'settings.app.buttonSearchForUpdate', 52 id: 'settings.app.buttonSearchForUpdate',
45 defaultMessage: '!!!Check for updates', 53 defaultMessage: '!!!Check for updates',
@@ -77,6 +85,9 @@ export default class EditSettingsForm extends Component {
77 isUpdateAvailable: PropTypes.bool.isRequired, 85 isUpdateAvailable: PropTypes.bool.isRequired,
78 noUpdateAvailable: PropTypes.bool.isRequired, 86 noUpdateAvailable: PropTypes.bool.isRequired,
79 updateIsReadyToInstall: PropTypes.bool.isRequired, 87 updateIsReadyToInstall: PropTypes.bool.isRequired,
88 isClearingAllCache: PropTypes.bool.isRequired,
89 onClearAllCache: PropTypes.func.isRequired,
90 cacheSize: PropTypes.string.isRequired,
80 }; 91 };
81 92
82 static contextTypes = { 93 static contextTypes = {
@@ -103,6 +114,9 @@ export default class EditSettingsForm extends Component {
103 isUpdateAvailable, 114 isUpdateAvailable,
104 noUpdateAvailable, 115 noUpdateAvailable,
105 updateIsReadyToInstall, 116 updateIsReadyToInstall,
117 isClearingAllCache,
118 onClearAllCache,
119 cacheSize,
106 } = this.props; 120 } = this.props;
107 const { intl } = this.context; 121 const { intl } = this.context;
108 122
@@ -155,6 +169,26 @@ export default class EditSettingsForm extends Component {
155 <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2> 169 <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2>
156 <Toggle field={form.$('enableSpellchecking')} /> 170 <Toggle field={form.$('enableSpellchecking')} />
157 {/* <Select field={form.$('spellcheckingLanguage')} /> */} 171 {/* <Select field={form.$('spellcheckingLanguage')} /> */}
172 <div className="settings__settings-group">
173 <h3>
174 {/* {intl.formatMessage(messages.headlineGeneral)} */}
175 Service cache
176 </h3>
177 <p>
178 {intl.formatMessage(messages.cacheInfo, {
179 size: cacheSize,
180 })}
181 </p>
182 <p>
183 <Button
184 buttonType="secondary"
185 label={intl.formatMessage(messages.buttonClearAllCache)}
186 onClick={onClearAllCache}
187 disabled={isClearingAllCache}
188 loaded={!isClearingAllCache}
189 />
190 </p>
191 </div>
158 192
159 {/* Updates */} 193 {/* Updates */}
160 <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> 194 <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2>
@@ -165,6 +199,7 @@ export default class EditSettingsForm extends Component {
165 /> 199 />
166 ) : ( 200 ) : (
167 <Button 201 <Button
202 buttonType="secondary"
168 label={intl.formatMessage(updateButtonLabelMessage)} 203 label={intl.formatMessage(updateButtonLabelMessage)}
169 onClick={checkForUpdates} 204 onClick={checkForUpdates}
170 disabled={isCheckingForUpdates || isUpdateAvailable} 205 disabled={isCheckingForUpdates || isUpdateAvailable}
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index 45ded9e5c..9fa815a0a 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -193,8 +193,17 @@ export default class EditSettingsScreen extends Component {
193 } 193 }
194 194
195 render() { 195 render() {
196 const { updateStatus, updateStatusTypes } = this.props.stores.app; 196 const {
197 const { checkForUpdates, installUpdate } = this.props.actions.app; 197 updateStatus,
198 cacheSize,
199 updateStatusTypes,
200 isClearingAllCache,
201 } = this.props.stores.app;
202 const {
203 checkForUpdates,
204 installUpdate,
205 clearAllCache,
206 } = this.props.actions.app;
198 const form = this.prepareForm(); 207 const form = this.prepareForm();
199 208
200 return ( 209 return (
@@ -207,6 +216,9 @@ export default class EditSettingsScreen extends Component {
207 noUpdateAvailable={updateStatus === updateStatusTypes.NOT_AVAILABLE} 216 noUpdateAvailable={updateStatus === updateStatusTypes.NOT_AVAILABLE}
208 updateIsReadyToInstall={updateStatus === updateStatusTypes.DOWNLOADED} 217 updateIsReadyToInstall={updateStatus === updateStatusTypes.DOWNLOADED}
209 onSubmit={d => this.onSubmit(d)} 218 onSubmit={d => this.onSubmit(d)}
219 cacheSize={cacheSize}
220 isClearingAllCache={isClearingAllCache}
221 onClearAllCache={clearAllCache}
210 /> 222 />
211 ); 223 );
212 } 224 }
@@ -223,6 +235,7 @@ EditSettingsScreen.wrappedComponent.propTypes = {
223 launchOnStartup: PropTypes.func.isRequired, 235 launchOnStartup: PropTypes.func.isRequired,
224 checkForUpdates: PropTypes.func.isRequired, 236 checkForUpdates: PropTypes.func.isRequired,
225 installUpdate: PropTypes.func.isRequired, 237 installUpdate: PropTypes.func.isRequired,
238 clearAllCache: PropTypes.func.isRequired,
226 }).isRequired, 239 }).isRequired,
227 settings: PropTypes.shape({ 240 settings: PropTypes.shape({
228 update: PropTypes.func.isRequired, 241 update: PropTypes.func.isRequired,
diff --git a/src/electron/ipc-api/autoUpdate.js b/src/electron/ipc-api/autoUpdate.js
index 7bc193e2d..ba49a2f97 100644
--- a/src/electron/ipc-api/autoUpdate.js
+++ b/src/electron/ipc-api/autoUpdate.js
@@ -1,8 +1,9 @@
1import { app, ipcMain } from 'electron'; 1import { app, ipcMain } from 'electron';
2import { autoUpdater } from 'electron-updater'; 2import { autoUpdater } from 'electron-updater';
3import { isDevMode } from '../../environment.js';
3 4
4export default (params) => { 5export default (params) => {
5 if (process.platform === 'darwin' || process.platform === 'win32') { 6 if (!isDevMode && (process.platform === 'darwin' || process.platform === 'win32')) {
6 // autoUpdater.setFeedURL(updateUrl); 7 // autoUpdater.setFeedURL(updateUrl);
7 ipcMain.on('autoUpdate', (event, args) => { 8 ipcMain.on('autoUpdate', (event, args) => {
8 try { 9 try {
diff --git a/src/helpers/service-helpers.js b/src/helpers/service-helpers.js
new file mode 100644
index 000000000..5f63f6b7c
--- /dev/null
+++ b/src/helpers/service-helpers.js
@@ -0,0 +1,20 @@
1import path from 'path';
2import { remote } from 'electron';
3import fs from 'fs-extra';
4
5const app = remote.app;
6
7export function getServicePartitionsDirectory() {
8 return path.join(app.getPath('userData'), 'Partitions');
9}
10
11export function removeServicePartitionDirectory(id = '', addServicePrefix = false) {
12 const servicePartition = path.join(getServicePartitionsDirectory(), `${addServicePrefix ? 'service-' : ''}${id}`);
13
14 return fs.remove(servicePartition);
15}
16
17export async function getServiceIdsFromPartitions() {
18 const files = await fs.readdir(getServicePartitionsDirectory());
19 return files.filter(n => n !== '__chrome_extension');
20}
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index da5d4a143..4a88fc397 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -150,6 +150,8 @@
150 "settings.app.updateStatusSearching": "Is searching for update", 150 "settings.app.updateStatusSearching": "Is searching for update",
151 "settings.app.updateStatusAvailable": "Update available, downloading...", 151 "settings.app.updateStatusAvailable": "Update available, downloading...",
152 "settings.app.updateStatusUpToDate": "You are using the latest version of Franz", 152 "settings.app.updateStatusUpToDate": "You are using the latest version of Franz",
153 "settings.app.cacheInfo": "Franz cache is currently using {size} of disk space.",
154 "settings.app.buttonClearAllCache": "Clear cache",
153 "settings.app.form.autoLaunchOnStart": "Launch Franz on start", 155 "settings.app.form.autoLaunchOnStart": "Launch Franz on start",
154 "settings.app.form.autoLaunchInBackground": "Open in background", 156 "settings.app.form.autoLaunchInBackground": "Open in background",
155 "settings.app.form.enableSystemTray": "Show Franz in system tray", 157 "settings.app.form.enableSystemTray": "Show Franz in system tray",
diff --git a/src/index.js b/src/index.js
index a047e2bc1..f82bb3590 100644
--- a/src/index.js
+++ b/src/index.js
@@ -44,6 +44,7 @@ const isSecondInstance = app.makeSingleInstance((argv) => {
44}); 44});
45 45
46if (isSecondInstance) { 46if (isSecondInstance) {
47 console.log('An instance of Franz is already running. Exiting...');
47 app.exit(); 48 app.exit();
48} 49}
49 50
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index aa6e801ff..e33f50f05 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -1,10 +1,11 @@
1import { remote, ipcRenderer, shell } from 'electron'; 1import { remote, ipcRenderer, shell } from 'electron';
2import { action, observable } from 'mobx'; 2import { action, computed, observable } from 'mobx';
3import moment from 'moment'; 3import moment from 'moment';
4import key from 'keymaster'; 4import key from 'keymaster';
5import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; 5import { getDoNotDisturb } from '@meetfranz/electron-notification-state';
6import idleTimer from '@paulcbetts/system-idle-time'; 6import idleTimer from '@paulcbetts/system-idle-time';
7import AutoLaunch from 'auto-launch'; 7import AutoLaunch from 'auto-launch';
8import prettyBytes from 'pretty-bytes';
8 9
9import Store from './lib/Store'; 10import Store from './lib/Store';
10import Request from './lib/Request'; 11import Request from './lib/Request';
@@ -14,7 +15,10 @@ import locales from '../i18n/translations';
14import { gaEvent } from '../lib/analytics'; 15import { gaEvent } from '../lib/analytics';
15import Miner from '../lib/Miner'; 16import Miner from '../lib/Miner';
16 17
18import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js';
19
17const { app } = remote; 20const { app } = remote;
21
18const defaultLocale = DEFAULT_APP_SETTINGS.locale; 22const defaultLocale = DEFAULT_APP_SETTINGS.locale;
19const autoLauncher = new AutoLaunch({ 23const autoLauncher = new AutoLaunch({
20 name: 'Franz', 24 name: 'Franz',
@@ -30,6 +34,8 @@ export default class AppStore extends Store {
30 }; 34 };
31 35
32 @observable healthCheckRequest = new Request(this.api.app, 'health'); 36 @observable healthCheckRequest = new Request(this.api.app, 'health');
37 @observable getAppCacheSizeRequest = new Request(this.api.local, 'getAppCacheSize');
38 @observable clearAppCacheRequest = new Request(this.api.local, 'clearAppCache');
33 39
34 @observable autoLaunchOnStart = true; 40 @observable autoLaunchOnStart = true;
35 41
@@ -47,6 +53,8 @@ export default class AppStore extends Store {
47 53
48 @observable isSystemMuteOverridden = false; 54 @observable isSystemMuteOverridden = false;
49 55
56 @observable isClearingAllCache = false;
57
50 constructor(...args) { 58 constructor(...args) {
51 super(...args); 59 super(...args);
52 60
@@ -61,6 +69,7 @@ export default class AppStore extends Store {
61 this.actions.app.healthCheck.listen(this._healthCheck.bind(this)); 69 this.actions.app.healthCheck.listen(this._healthCheck.bind(this));
62 this.actions.app.muteApp.listen(this._muteApp.bind(this)); 70 this.actions.app.muteApp.listen(this._muteApp.bind(this));
63 this.actions.app.toggleMuteApp.listen(this._toggleMuteApp.bind(this)); 71 this.actions.app.toggleMuteApp.listen(this._toggleMuteApp.bind(this));
72 this.actions.app.clearAllCache.listen(this._clearAllCache.bind(this));
64 73
65 this.registerReactions([ 74 this.registerReactions([
66 this._offlineCheck.bind(this), 75 this._offlineCheck.bind(this),
@@ -165,6 +174,10 @@ export default class AppStore extends Store {
165 this._healthCheck(); 174 this._healthCheck();
166 } 175 }
167 176
177 @computed get cacheSize() {
178 return prettyBytes(this.getAppCacheSizeRequest.execute().result || 0);
179 }
180
168 // Actions 181 // Actions
169 @action _notify({ title, options, notificationId, serviceId = null }) { 182 @action _notify({ title, options, notificationId, serviceId = null }) {
170 if (this.stores.settings.all.isAppMuted) return; 183 if (this.stores.settings.all.isAppMuted) return;
@@ -255,6 +268,23 @@ export default class AppStore extends Store {
255 this._muteApp({ isMuted: !this.stores.settings.all.isAppMuted }); 268 this._muteApp({ isMuted: !this.stores.settings.all.isAppMuted });
256 } 269 }
257 270
271 @action async _clearAllCache() {
272 this.isClearingAllCache = true;
273 const clearAppCache = this.clearAppCacheRequest.execute();
274 const allServiceIds = await getServiceIdsFromPartitions();
275 const allOrphanedServiceIds = allServiceIds.filter(id => !this.stores.services.all.find(s => id.replace('service-', '') === s.id));
276
277 await Promise.all(allOrphanedServiceIds.map(id => removeServicePartitionDirectory(id)));
278
279 await Promise.all(this.stores.services.all.map(s => this.actions.service.clearCache({ serviceId: s.id })));
280
281 await clearAppCache._promise;
282
283 this.getAppCacheSizeRequest.execute();
284
285 this.isClearingAllCache = false;
286 }
287
258 // Reactions 288 // Reactions
259 _offlineCheck() { 289 _offlineCheck() {
260 if (!this.isOnline) { 290 if (!this.isOnline) {
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index 3abb57d1d..87ee57a0d 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -16,6 +16,7 @@ export default class ServicesStore extends Store {
16 @observable updateServiceRequest = new Request(this.api.services, 'update'); 16 @observable updateServiceRequest = new Request(this.api.services, 'update');
17 @observable reorderServicesRequest = new Request(this.api.services, 'reorder'); 17 @observable reorderServicesRequest = new Request(this.api.services, 'reorder');
18 @observable deleteServiceRequest = new Request(this.api.services, 'delete'); 18 @observable deleteServiceRequest = new Request(this.api.services, 'delete');
19 @observable clearCacheRequest = new Request(this.api.services, 'clearCache');
19 20
20 @observable filterNeedle = null; 21 @observable filterNeedle = null;
21 22
@@ -31,6 +32,7 @@ export default class ServicesStore extends Store {
31 this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this)); 32 this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this));
32 this.actions.service.updateService.listen(this._updateService.bind(this)); 33 this.actions.service.updateService.listen(this._updateService.bind(this));
33 this.actions.service.deleteService.listen(this._deleteService.bind(this)); 34 this.actions.service.deleteService.listen(this._deleteService.bind(this));
35 this.actions.service.clearCache.listen(this._clearCache.bind(this));
34 this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this)); 36 this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this));
35 this.actions.service.focusService.listen(this._focusService.bind(this)); 37 this.actions.service.focusService.listen(this._focusService.bind(this));
36 this.actions.service.focusActiveService.listen(this._focusActiveService.bind(this)); 38 this.actions.service.focusActiveService.listen(this._focusActiveService.bind(this));
@@ -205,6 +207,13 @@ export default class ServicesStore extends Store {
205 gaEvent('Service', 'delete', service.recipe.id); 207 gaEvent('Service', 'delete', service.recipe.id);
206 } 208 }
207 209
210 @action async _clearCache({ serviceId }) {
211 this.clearCacheRequest.reset();
212 const request = this.clearCacheRequest.execute(serviceId);
213 await request._promise;
214 gaEvent('Service', 'clear cache');
215 }
216
208 @action _setActive({ serviceId }) { 217 @action _setActive({ serviceId }) {
209 const service = this.one(serviceId); 218 const service = this.one(serviceId);
210 219
diff --git a/src/styles/button.scss b/src/styles/button.scss
index 75d2cb1d4..8d2adbbcc 100644
--- a/src/styles/button.scss
+++ b/src/styles/button.scss
@@ -48,6 +48,18 @@
48 } 48 }
49 } 49 }
50 50
51 &.franz-form__button--warning {
52 background: $theme-brand-warning;
53
54 &:hover {
55 background: darken($theme-brand-warning, 5%);
56 }
57
58 &:active {
59 background: lighten($theme-brand-warning, 5%);
60 }
61 }
62
51 &.franz-form__button--inverted { 63 &.franz-form__button--inverted {
52 background: none; 64 background: none;
53 padding: 10px 20px; 65 padding: 10px 20px;