From d422d2e160e818dd0bff90707d0c0832c8c3367b Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Fri, 22 Dec 2017 20:53:45 +0100 Subject: setBadge debugging --- src/stores/ServicesStore.js | 1 + 1 file changed, 1 insertion(+) (limited to 'src/stores') diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 66f37af26..2417181e6 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -503,6 +503,7 @@ export default class ServicesStore extends Store { // We can't just block this earlier, otherwise the mobx reaction won't be aware of the vars to watch in some cases if (showMessageBadgesEvenWhenMuted) { + console.log('set badge', unreadDirectMessageCount, unreadIndirectMessageCount); this.actions.app.setBadge({ unreadDirectMessageCount, unreadIndirectMessageCount, -- cgit v1.2.3-70-g09d2 From 344c25061f6a780da2db55e1ec5a66024a580dba Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Fri, 22 Dec 2017 23:20:48 +0100 Subject: Fix indirect message badge being displayed when disabled --- src/stores/ServicesStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/stores') diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 2417181e6..c48f48a88 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -497,7 +497,7 @@ export default class ServicesStore extends Store { .reduce((a, b) => a + b, 0); const unreadIndirectMessageCount = this.allDisplayed - .filter(s => (showMessageBadgeWhenMuted || s.isIndirectMessageBadgeEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled) + .filter(s => (showMessageBadgeWhenMuted && showMessageBadgesEvenWhenMuted) && (s.isBadgeEnabled && s.isIndirectMessageBadgeEnabled)) .map(s => s.unreadIndirectMessageCount) .reduce((a, b) => a + b, 0); -- cgit v1.2.3-70-g09d2 From 7293492403d8d4c8441eb58cdb391f5476608cfa Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Sat, 23 Dec 2017 00:02:30 +0100 Subject: fix(Service): Ctrl/Cmd+R now navigates back to the service root --- src/stores/ServicesStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/stores') diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 66f37af26..6c1e757dc 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -368,7 +368,7 @@ export default class ServicesStore extends Store { const service = this.one(serviceId); service.resetMessageCount(); - service.webview.reload(); + service.webview.loadURL(service.url); } @action _reloadActive() { -- cgit v1.2.3-70-g09d2 From 531531e182b59dd4dab23d3f68717d609cdb7dd4 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Sun, 24 Dec 2017 21:40:49 +0100 Subject: fix(App): Fix service reload after waking machine up --- src/stores/AppStore.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'src/stores') diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 5a6c12ee1..7e2be5e9f 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js @@ -124,15 +124,23 @@ export default class AppStore extends Store { this.stores.router.push(data.url); }); + const TIMEOUT = 5000; // Check system idle time every minute setInterval(() => { this.idleTime = idleTimer.getIdleTime(); - }, 60000); + }, TIMEOUT); // Reload all services after a healthy nap - powerMonitor.on('resume', () => { - setTimeout(window.location.reload, 5000); - }); + // Alternative solution for powerMonitor as the resume event is not fired + // More information: https://github.com/electron/electron/issues/1615 + let lastTime = (new Date()).getTime(); + setInterval(() => { + const currentTime = (new Date()).getTime(); + if (currentTime > (lastTime + TIMEOUT + 2000)) { + this._reactivateServices(); + } + lastTime = currentTime; + }, TIMEOUT); // Set active the next service key( @@ -357,6 +365,16 @@ export default class AppStore extends Store { return autoLauncher.isEnabled() || false; } + _reactivateServices(retryCount = 0) { + if (!this.isOnline) { + console.debug('reactivateServices: computer is offline, trying again in 5s, retries:', retryCount) + return setTimeout(() => this._reactivateServices(retryCount + 1), 5000); + } + + console.debug('reactivateServices: reload all services'); + this.actions.service.reloadAll(); + } + _systemDND() { const dnd = getDoNotDisturb(); if (dnd === this.stores.settings.all.isAppMuted || !this.isSystemMuteOverriden) { -- cgit v1.2.3-70-g09d2 From b79c997b2316a60ebe7920f263c1edbc582c852e Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Sun, 24 Dec 2017 21:44:55 +0100 Subject: Fix linting issue --- src/stores/AppStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/stores') diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 7e2be5e9f..ea51b537a 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js @@ -367,7 +367,7 @@ export default class AppStore extends Store { _reactivateServices(retryCount = 0) { if (!this.isOnline) { - console.debug('reactivateServices: computer is offline, trying again in 5s, retries:', retryCount) + console.debug('reactivateServices: computer is offline, trying again in 5s, retries:', retryCount); return setTimeout(() => this._reactivateServices(retryCount + 1), 5000); } -- cgit v1.2.3-70-g09d2 From fa73612e38ccfab17bc7c39f8b92153903a7ca83 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 26 Dec 2017 13:52:50 +0100 Subject: Fix linting issues pt 2 --- src/stores/AppStore.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/stores') diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index ea51b537a..aa6e801ff 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js @@ -14,7 +14,7 @@ import locales from '../i18n/translations'; import { gaEvent } from '../lib/analytics'; import Miner from '../lib/Miner'; -const { app, powerMonitor } = remote; +const { app } = remote; const defaultLocale = DEFAULT_APP_SETTINGS.locale; const autoLauncher = new AutoLaunch({ name: 'Franz', @@ -368,11 +368,11 @@ export default class AppStore extends Store { _reactivateServices(retryCount = 0) { if (!this.isOnline) { console.debug('reactivateServices: computer is offline, trying again in 5s, retries:', retryCount); - return setTimeout(() => this._reactivateServices(retryCount + 1), 5000); + setTimeout(() => this._reactivateServices(retryCount + 1), 5000); + } else { + console.debug('reactivateServices: reload all services'); + this.actions.service.reloadAll(); } - - console.debug('reactivateServices: reload all services'); - this.actions.service.reloadAll(); } _systemDND() { -- cgit v1.2.3-70-g09d2 From 150cfe764aeb9e93341ba2f231fd121fe85472af Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 27 Dec 2017 13:09:17 +0100 Subject: First working draft of icon upload --- package.json | 2 +- src/api/server/ServerApi.js | 42 ++++++++++++++++++++-- .../settings/services/EditServiceForm.js | 7 +++- src/components/ui/ImageUpload.js | 42 +++++++--------------- src/containers/settings/EditServiceScreen.js | 7 ++-- src/i18n/locales/en-US.json | 1 + src/models/Service.js | 11 +++--- src/stores/ServicesStore.js | 14 +++++++- yarn.lock | 6 ++-- 9 files changed, 86 insertions(+), 46 deletions(-) (limited to 'src/stores') diff --git a/package.json b/package.json index 6c4c65cb8..5dae5bc7c 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "mkdirp": "^0.5.1", "mobx": "^3.1.0", "mobx-react": "^4.1.0", - "mobx-react-form": "1.24.0", + "mobx-react-form": "^1.32.2", "mobx-react-router": "^3.1.2", "moment": "^2.17.1", "normalize-url": "^1.9.1", diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index 8b3136d27..6b96f709e 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js @@ -167,27 +167,65 @@ export default class ServerApi { throw request; } const serviceData = await request.json(); + + if (data.iconFile) { + const iconUrl = await this.uploadServiceIcon(serviceData.data.id, data.iconFile); + + serviceData.data.iconUrl = iconUrl; + } + const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) }); console.debug('ServerApi::createService resolves', service); return service; } - async updateService(recipeId, data) { - const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service/${recipeId}`, this._prepareAuthRequest({ + async updateService(serviceId, rawData) { + const data = rawData; + + if (data.iconFile) { + await this.uploadServiceIcon(serviceId, data.iconFile); + } + + const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service/${serviceId}`, this._prepareAuthRequest({ method: 'PUT', body: JSON.stringify(data), })); + if (!request.ok) { throw request; } + const serviceData = await request.json(); + const service = Object.assign(serviceData, { data: await this._prepareServiceModel(serviceData.data) }); console.debug('ServerApi::updateService resolves', service); return service; } + async uploadServiceIcon(serviceId, icon) { + const formData = new FormData(); + formData.append('icon', icon); + + const requestData = this._prepareAuthRequest({ + method: 'PUT', + body: formData, + }); + + delete requestData.headers['Content-Type']; + + const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service/${serviceId}`, requestData); + + if (!request.ok) { + throw request; + } + + const serviceData = await request.json(); + + return serviceData.data.iconUrl; + } + async reorderService(data) { const request = await window.fetch(`${SERVER_URL}/${API_VERSION}/service/reorder`, this._prepareAuthRequest({ method: 'PUT', diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index ee69d53aa..4f2f98a01 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js @@ -127,6 +127,11 @@ export default class EditServiceForm extends Component { const values = form.values(); let isValid = true; + const files = form.$('customIcon').files; + if (files) { + values.iconFile = files[0]; + } + if (recipe.validateUrl && values.customUrl) { this.setState({ isValidatingCustomUrl: true }); try { @@ -224,7 +229,7 @@ export default class EditServiceForm extends Component {
{/* */} - +
{(recipe.hasTeamId || recipe.hasCustomUrl) && ( diff --git a/src/components/ui/ImageUpload.js b/src/components/ui/ImageUpload.js index d07c3649c..f25d966f4 100644 --- a/src/components/ui/ImageUpload.js +++ b/src/components/ui/ImageUpload.js @@ -12,82 +12,64 @@ export default class ImageUpload extends Component { field: PropTypes.instanceOf(Field).isRequired, className: PropTypes.string, multiple: PropTypes.bool, - // disabled: PropTypes.bool, - // onClick: PropTypes.func, - // type: PropTypes.string, - // buttonType: PropTypes.string, - // loaded: PropTypes.bool, - // htmlForm: PropTypes.string, }; static defaultProps = { className: null, multiple: false, - // disabled: false, - // onClick: () => {}, - // type: 'button', - // buttonType: '', - // loaded: true, - // htmlForm: '', }; - dropzoneRef = null; - state = { path: null, } onDrop(acceptedFiles) { - // const req = request.post('/upload'); acceptedFiles.forEach((file) => { - console.log(file); this.setState({ path: file.path, }); - // req.attach(file.name, file); + this.props.field.onDrop(file); }); - // req.end(callback); } + dropzoneRef = null; + render() { const { field, className, multiple, - // disabled, - // onClick, - // type, - // buttonType, - // loaded, - // htmlForm, } = this.props; const cssClasses = classnames({ 'franz-form__button': true, - // [`franz-form__button--${buttonType}`]: buttonType, [`${className}`]: className, }); return (
{field.label} - {this.state.path ? ( + {(field.value && field.value !== 'delete') || this.state.path ? (
{recipe.message && ( diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js index 3c52152b1..78f043e80 100644 --- a/src/containers/settings/EditServiceScreen.js +++ b/src/containers/settings/EditServiceScreen.js @@ -169,6 +169,14 @@ export default class EditServiceScreen extends Component { } } + clearCache() { + const { clearCache } = this.props.actions.service; + const { activeSettings: service } = this.props.stores.services; + clearCache({ + serviceId: service.id, + }); + } + render() { const { recipes, services, user } = this.props.stores; const { action } = this.props.router.params; @@ -211,8 +219,10 @@ export default class EditServiceScreen extends Component { status={services.actionStatus} isSaving={services.updateServiceRequest.isExecuting || services.createServiceRequest.isExecuting} isDeleting={services.deleteServiceRequest.isExecuting} + isClearingCache={services.clearCacheRequest.isExecuting} onSubmit={d => this.onSubmit(d)} onDelete={() => this.deleteService()} + onClearCache={() => this.clearCache()} /> ); } @@ -234,6 +244,7 @@ EditServiceScreen.wrappedComponent.propTypes = { createService: PropTypes.func.isRequired, updateService: PropTypes.func.isRequired, deleteService: PropTypes.func.isRequired, + clearCache: PropTypes.func.isRequired, }).isRequired, }).isRequired, }; diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 567537d75..2b5a3fc4b 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -124,6 +124,8 @@ "settings.service.form.indirectMessages": "Show message badge for all new messages", "settings.service.form.enableAudio": "Enable audio", "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", + "settings.service.form.buttonClearCache": "Clear cache", + "settings.service.form.buttonClearingCache": "Clearing cache...", "settings.service.form.headlineNotifications": "Notifications", "settings.service.form.headlineBadges": "Unread message badges", "settings.service.form.headlineGeneral": "General", diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 66f37af26..237264bcc 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -16,6 +16,7 @@ export default class ServicesStore extends Store { @observable updateServiceRequest = new Request(this.api.services, 'update'); @observable reorderServicesRequest = new Request(this.api.services, 'reorder'); @observable deleteServiceRequest = new Request(this.api.services, 'delete'); + @observable clearCacheRequest = new Request(this.api.services, 'clearCache'); @observable filterNeedle = null; @@ -31,6 +32,7 @@ export default class ServicesStore extends Store { this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this)); this.actions.service.updateService.listen(this._updateService.bind(this)); this.actions.service.deleteService.listen(this._deleteService.bind(this)); + this.actions.service.clearCache.listen(this._clearCache.bind(this)); this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this)); this.actions.service.focusService.listen(this._focusService.bind(this)); this.actions.service.focusActiveService.listen(this._focusActiveService.bind(this)); @@ -205,6 +207,13 @@ export default class ServicesStore extends Store { gaEvent('Service', 'delete', service.recipe.id); } + @action async _clearCache({ serviceId }) { + this.clearCacheRequest.reset(); + const request = this.clearCacheRequest.execute(serviceId); + await request._promise; + gaEvent('Service', 'clear cache'); + } + @action _setActive({ serviceId }) { const service = this.one(serviceId); 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 @@ } } + &.franz-form__button--warning { + background: $theme-brand-warning; + + &:hover { + background: darken($theme-brand-warning, 5%); + } + + &:active { + background: lighten($theme-brand-warning, 5%); + } + } + &.franz-form__button--inverted { background: none; padding: 10px 20px; -- cgit v1.2.3-70-g09d2 From 33c6e0af4112a927027a2533dea4bea3750e7865 Mon Sep 17 00:00:00 2001 From: Danny Qiu Date: Fri, 29 Dec 2017 01:33:42 -0500 Subject: Add button to clear global cache and all services --- src/actions/app.js | 1 + src/api/server/LocalApi.js | 5 ++++ .../settings/settings/EditSettingsForm.js | 30 ++++++++++++++++++++++ src/containers/settings/EditSettingsScreen.js | 7 +++-- src/i18n/locales/en-US.json | 2 ++ src/stores/AppStore.js | 15 +++++++++++ 6 files changed, 58 insertions(+), 2 deletions(-) (limited to 'src/stores') 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 { overrideSystemMute: PropTypes.bool, }, toggleMuteApp: {}, + clearAllCache: {}, }; diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js index fec89f948..d2ce0c9de 100644 --- a/src/api/server/LocalApi.js +++ b/src/api/server/LocalApi.js @@ -41,4 +41,9 @@ export default class LocalApi { const s = session.fromPartition(`persist:service-${serviceId}`); await new Promise(resolve => s.clearCache(resolve)); } + + async clearAppCache() { + const s = session.defaultSession; + await new Promise(resolve => s.clearCache(resolve)); + } } diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index ff398aa33..074a4b731 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js @@ -40,6 +40,14 @@ const messages = defineMessages({ id: 'settings.app.translationHelp', defaultMessage: '!!!Help us to translate Franz into your language.', }, + buttonClearAllCache: { + id: 'settings.app.buttonClearAllCache', + defaultMessage: '!!!Clear global cache for Franz and all services', + }, + buttonClearingAllCache: { + id: 'settings.app.buttonClearingAllCache', + defaultMessage: '!!!Clearing global cache...', + }, buttonSearchForUpdate: { id: 'settings.app.buttonSearchForUpdate', defaultMessage: '!!!Check for updates', @@ -77,6 +85,8 @@ export default class EditSettingsForm extends Component { isUpdateAvailable: PropTypes.bool.isRequired, noUpdateAvailable: PropTypes.bool.isRequired, updateIsReadyToInstall: PropTypes.bool.isRequired, + isClearingAllCache: PropTypes.bool.isRequired, + onClearAllCache: PropTypes.func.isRequired, }; static contextTypes = { @@ -103,6 +113,8 @@ export default class EditSettingsForm extends Component { isUpdateAvailable, noUpdateAvailable, updateIsReadyToInstall, + isClearingAllCache, + onClearAllCache, } = this.props; const { intl } = this.context; @@ -115,6 +127,23 @@ export default class EditSettingsForm extends Component { updateButtonLabelMessage = messages.buttonSearchForUpdate; } + const clearAllCacheButton = isClearingAllCache ? ( + -
+
+ +
+ {(field.value && field.value !== 'delete') || this.state.path ? ( +
+
+
+ +
+
-
- ) : ( - { this.dropzoneRef = node; }} - onDrop={this.onDrop.bind(this)} - className={cssClasses} - multiple={multiple} - accept="image/jpeg, image/png" - > -

Try dropping some files here, or click to select files to upload.

-
- )} + ) : ( + { this.dropzoneRef = node; }} + onDrop={this.onDrop.bind(this)} + className={cssClasses} + multiple={multiple} + accept="image/jpeg, image/png" + > + +

+ {textUpload} +

+
+ )} +
); } diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js index 8d3a268ad..c26195a1e 100644 --- a/src/containers/settings/EditServiceScreen.js +++ b/src/containers/settings/EditServiceScreen.js @@ -48,7 +48,7 @@ const messages = defineMessages({ }, icon: { id: 'settings.service.form.icon', - defaultMessage: '!!!Icon', + defaultMessage: '!!!Custom icon', }, }); @@ -108,7 +108,7 @@ export default class EditServiceScreen extends Component { }, customIcon: { label: intl.formatMessage(messages.icon), - value: service.hasCustomIcon ? service.icon : false, + value: service.hasCustomUploadedIcon ? service.icon : false, default: null, type: 'file', }, diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index bfb95da04..f3db20aea 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -127,7 +127,9 @@ "settings.service.form.headlineNotifications": "Notifications", "settings.service.form.headlineBadges": "Unread message badges", "settings.service.form.headlineGeneral": "General", - "settings.service.form.icon": "Icon", + "settings.service.form.icon": "Custom icon", + "settings.service.form.iconDelete": "Delete", + "settings.service.form.iconUpload": "Drop your image, or click here", "settings.service.error.headline": "Error", "settings.service.error.goBack": "Back to services", "settings.service.error.message": "Could not load service recipe.", diff --git a/src/models/Service.js b/src/models/Service.js index 652d2594c..423510c7d 100644 --- a/src/models/Service.js +++ b/src/models/Service.js @@ -25,6 +25,7 @@ export default class Service { @observable isBadgeEnabled = true; @observable isIndirectMessageBadgeEnabled = true; @observable iconUrl = ''; + @observable hasCustomUploadedIcon = false; @observable hasCrashed = false; constructor(data, recipe) { @@ -62,6 +63,8 @@ export default class Service { this.isMuted = data.isMuted !== undefined ? data.isMuted : this.isMuted; + this.hasCustomUploadedIcon = data.hasCustomIcon !== undefined ? data.hasCustomIcon : this.hasCustomUploadedIcon; + this.recipe = recipe; autorun(() => { diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 4fb5c9767..f2a8683ba 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js @@ -177,13 +177,21 @@ export default class ServicesStore extends Store { await request._promise; newData.iconUrl = request.result.data.iconUrl; + newData.hasCustomUploadedIcon = true; } this.allServicesRequest.patch((result) => { if (!result) return; + // patch custom icon deletion if (data.customIcon === 'delete') { data.iconUrl = ''; + data.hasCustomUploadedIcon = false; + } + + // patch custom icon url + if (data.customIconUrl) { + data.iconUrl = data.customIconUrl; } Object.assign(result.find(c => c.id === serviceId), newData); @@ -328,7 +336,7 @@ export default class ServicesStore extends Store { } } else if (channel === 'avatar') { const url = args[0]; - if (service.customIconUrl !== url) { + if (service.iconUrl !== url && !service.hasCustomUploadedIcon) { service.customIconUrl = url; this.actions.service.updateService({ diff --git a/src/styles/image-upload.scss b/src/styles/image-upload.scss index 764bb3158..06176a7af 100644 --- a/src/styles/image-upload.scss +++ b/src/styles/image-upload.scss @@ -1,9 +1,12 @@ .image-upload { position: absolute; - width: 100px; - height: 100px; - border-radius: $theme-border-radius; + width: 140px; + height: 140px; + border: 1px solid $theme-gray-lighter; + border-radius: $theme-border-radius-small; + background: $theme-gray-lightest; overflow: hidden; + margin-top: 5px; &__preview, &__action { @@ -19,7 +22,7 @@ background-size: 100%; background-repeat: no-repeat; background-position: center center; - margin: 5px; + border-radius: 3px; } &__action { @@ -27,6 +30,8 @@ z-index: 10; opacity: 0; transition: opacity 0.5s; + display: flex; + justify-content: center; &-background { position: absolute; @@ -41,6 +46,33 @@ button { position: relative; z-index: 100; + color: #FFF; + + .mdi { + color: #FFF; + } + } + } + + &__dropzone { + text-align: center; + border-radius: 5px; + padding: 10px; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + } + + &__dropzone, + button { + .mdi { + margin-bottom: 5px; + } + + p { + font-size: 10px; + line-height: 10px; } } @@ -49,4 +81,11 @@ opacity: 1; } } +} + +.image-upload-wrapper { + .mdi { + font-size: 40px; + color: $theme-gray-light; + } } \ No newline at end of file diff --git a/src/styles/settings.scss b/src/styles/settings.scss index 283913ab7..cb39717ea 100644 --- a/src/styles/settings.scss +++ b/src/styles/settings.scss @@ -121,9 +121,15 @@ } .service-icon { - width: 30%; - min-width: 100px; + width: 140px; + float: right; + margin-top: 30px; margin-left: 40px; + + label { + font-weight: bold; + letter-spacing: -0.1px; + } } } @@ -167,6 +173,7 @@ &__options { margin-top: 20px; + flex: 1; } &__settings-group { -- cgit v1.2.3-70-g09d2 From be801ff55d765e519f58a34d9854fb464a1ff77e Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 3 Jan 2018 23:46:54 +0100 Subject: feat(App): Add option to clear app cache (@dannyqiu) --- package.json | 2 + src/api/LocalApi.js | 4 ++ src/api/server/LocalApi.js | 24 ++++++++-- src/api/server/ServerApi.js | 4 +- .../settings/services/EditServiceForm.js | 30 ------------- .../settings/settings/EditSettingsForm.js | 51 ++++++++++++---------- src/containers/settings/EditServiceScreen.js | 11 ----- src/containers/settings/EditSettingsScreen.js | 14 +++++- src/helpers/service-helpers.js | 9 ++-- src/i18n/locales/en-US.json | 6 +-- src/stores/AppStore.js | 22 ++++++++-- yarn.lock | 14 ++++++ 12 files changed, 109 insertions(+), 82 deletions(-) (limited to 'src/stores') diff --git a/package.json b/package.json index 301b88b81..0a2bd8991 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "babel-polyfill": "^6.23.0", "babel-runtime": "^6.23.0", "classnames": "^2.2.5", + "du": "^0.1.0", "electron-fetch": "^1.1.0", "electron-spellchecker": "^1.1.2", "electron-updater": "^2.4.3", @@ -54,6 +55,7 @@ "mobx-react-router": "^3.1.2", "moment": "^2.17.1", "normalize-url": "^1.9.1", + "pretty-bytes": "^4.0.2", "prop-types": "^15.5.10", "prop-types-extended": "^0.2.1", "react": "^15.4.1", diff --git a/src/api/LocalApi.js b/src/api/LocalApi.js index d52e9cd10..3f84f8a0b 100644 --- a/src/api/LocalApi.js +++ b/src/api/LocalApi.js @@ -16,6 +16,10 @@ export default class LocalApi { return this.local.removeKey(key); } + getAppCacheSize() { + return this.local.getAppCacheSize(); + } + clearAppCache() { return this.local.clearAppCache(); } diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js index d2ce0c9de..e95d750ac 100644 --- a/src/api/server/LocalApi.js +++ b/src/api/server/LocalApi.js @@ -1,4 +1,7 @@ import { remote } from 'electron'; +import du from 'du'; + +import { getServicePartitionsDirectory } from '../../helpers/service-helpers.js'; const { session } = remote; @@ -36,14 +39,29 @@ export default class LocalApi { } // Services + async getAppCacheSize() { + const partitionsDir = getServicePartitionsDirectory(); + return new Promise((resolve, reject) => { + du(partitionsDir, (err, size) => { + if (err) reject(err); + + console.debug('LocalApi::getAppCacheSize resolves', size); + resolve(size); + }); + }); + } + async clearCache(serviceId) { - console.debug(`Clearing cache for persist:service-${serviceId}`); const s = session.fromPartition(`persist:service-${serviceId}`); - await new Promise(resolve => s.clearCache(resolve)); + + console.debug('LocalApi::clearCache resolves', serviceId); + return new Promise(resolve => s.clearCache(resolve)); } async clearAppCache() { const s = session.defaultSession; - await new Promise(resolve => s.clearCache(resolve)); + + console.debug('LocalApi::clearCache clearAppCache'); + return new Promise(resolve => s.clearCache(resolve)); } } diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index 3daa2d8b6..d75d2e559 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js @@ -213,7 +213,9 @@ export default class ServerApi { throw request; } const data = await request.json(); - await removeServicePartitionDirectory(id); + + removeServicePartitionDirectory(id, true); + console.debug('ServerApi::deleteService resolves', data); return data; } diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index b5300f605..4458c4c5a 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js @@ -71,14 +71,6 @@ const messages = defineMessages({ id: 'settings.service.form.isMutedInfo', defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', }, - buttonClearCache: { - id: 'settings.service.form.buttonClearCache', - defaultMessage: '!!!Clear cache', - }, - buttonClearingCache: { - id: 'settings.service.form.buttonClearingCache', - defaultMessage: '!!!Clearing cache', - }, headlineNotifications: { id: 'settings.service.form.headlineNotifications', defaultMessage: '!!!Notifications', @@ -110,10 +102,8 @@ export default class EditServiceForm extends Component { form: PropTypes.instanceOf(Form).isRequired, onSubmit: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, - onClearCache: PropTypes.func.isRequired, isSaving: PropTypes.bool.isRequired, isDeleting: PropTypes.bool.isRequired, - isClearingCache: PropTypes.bool.isRequired, }; static defaultProps = { @@ -168,9 +158,7 @@ export default class EditServiceForm extends Component { form, isSaving, isDeleting, - isClearingCache, onDelete, - onClearCache, } = this.props; const { intl } = this.context; @@ -193,23 +181,6 @@ export default class EditServiceForm extends Component { /> ); - const clearCacheButton = isClearingCache ? ( -
{recipe.message && ( diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 074a4b731..4f027638c 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js @@ -40,13 +40,13 @@ const messages = defineMessages({ id: 'settings.app.translationHelp', defaultMessage: '!!!Help us to translate Franz into your language.', }, + cacheInfo: { + id: 'settings.app.cacheInfo', + defaultMessage: '!!!Franz cache is currently using {size} of disk space.', + }, buttonClearAllCache: { id: 'settings.app.buttonClearAllCache', - defaultMessage: '!!!Clear global cache for Franz and all services', - }, - buttonClearingAllCache: { - id: 'settings.app.buttonClearingAllCache', - defaultMessage: '!!!Clearing global cache...', + defaultMessage: '!!!Clear cache', }, buttonSearchForUpdate: { id: 'settings.app.buttonSearchForUpdate', @@ -87,6 +87,7 @@ export default class EditSettingsForm extends Component { updateIsReadyToInstall: PropTypes.bool.isRequired, isClearingAllCache: PropTypes.bool.isRequired, onClearAllCache: PropTypes.func.isRequired, + cacheSize: PropTypes.string.isRequired, }; static contextTypes = { @@ -115,6 +116,7 @@ export default class EditSettingsForm extends Component { updateIsReadyToInstall, isClearingAllCache, onClearAllCache, + cacheSize, } = this.props; const { intl } = this.context; @@ -127,23 +129,6 @@ export default class EditSettingsForm extends Component { updateButtonLabelMessage = messages.buttonSearchForUpdate; } - const clearAllCacheButton = isClearingAllCache ? ( -