From cf0f8b382001d14fbfecf04f8cd176f8dced0d52 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 5 Dec 2017 17:12:45 +0100 Subject: Added debug info and sleep in between actions --- src/api/server/ServerApi.js | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) (limited to 'src/api/server') diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index 644bf20cd..edea991e2 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js @@ -12,6 +12,8 @@ import NewsModel from '../../models/News'; import UserModel from '../../models/User'; import OrderModel from '../../models/Order'; +import { sleep } from '../../helpers/async-helpers'; + import { API } from '../../environment'; import { @@ -303,21 +305,36 @@ export default class ServerApi { fs.ensureDirSync(recipeTempDirectory); const res = await fetch(packageUrl); + console.debug('Recipe downloaded', recipeId); const buffer = await res.buffer(); fs.writeFileSync(archivePath, buffer); + console.debug('Recipe written to filesystem', recipeId); + + await sleep(10); - tar.x({ + await tar.x({ file: archivePath, cwd: recipeTempDirectory, - sync: true, + preservePaths: true, + unlink: true, + onwarn: x => console.log('warn', recipeId, x), }); + console.debug('Recipe extracted to', recipeTempDirectory); + + await sleep(10); const { id } = fs.readJsonSync(path.join(recipeTempDirectory, 'package.json')); - const recipeDirectory = path.join(recipesDirectory, id); + console.debug('Recipe config parsed', id); + const recipeDirectory = path.join(recipesDirectory, id); fs.copySync(recipeTempDirectory, recipeDirectory); + console.debug('Recipe moved to', recipeDirectory); + fs.remove(recipeTempDirectory); + console.debug('Recipe temp directory removed', recipeTempDirectory); + fs.remove(path.join(recipesDirectory, recipeId, 'recipe.tar.gz')); + console.debug('Recipe tar.gz removed', path.join(recipesDirectory, recipeId, 'recipe.tar.gz')); return id; } catch (err) { -- cgit v1.2.3-70-g09d2 From 14046eeb692d55cf6bdec4195c1fa52a3d35e797 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 6 Dec 2017 13:28:58 +0100 Subject: explicitly set tar extract option preserveOwner to false --- src/api/server/ServerApi.js | 1 + 1 file changed, 1 insertion(+) (limited to 'src/api/server') diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index edea991e2..33f998b90 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js @@ -317,6 +317,7 @@ export default class ServerApi { cwd: recipeTempDirectory, preservePaths: true, unlink: true, + preserveOwner: false, onwarn: x => console.log('warn', recipeId, x), }); console.debug('Recipe extracted to', recipeTempDirectory); -- cgit v1.2.3-70-g09d2 From 2a71bce9bed827090f084fad90c14daca5f678c1 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 6 Dec 2017 22:23:22 +0100 Subject: remove excessive debug logs --- src/api/server/ServerApi.js | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src/api/server') diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index 33f998b90..8b3136d27 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js @@ -308,7 +308,6 @@ export default class ServerApi { console.debug('Recipe downloaded', recipeId); const buffer = await res.buffer(); fs.writeFileSync(archivePath, buffer); - console.debug('Recipe written to filesystem', recipeId); await sleep(10); @@ -320,22 +319,14 @@ export default class ServerApi { preserveOwner: false, onwarn: x => console.log('warn', recipeId, x), }); - console.debug('Recipe extracted to', recipeTempDirectory); await sleep(10); const { id } = fs.readJsonSync(path.join(recipeTempDirectory, 'package.json')); - console.debug('Recipe config parsed', id); - const recipeDirectory = path.join(recipesDirectory, id); fs.copySync(recipeTempDirectory, recipeDirectory); - console.debug('Recipe moved to', recipeDirectory); - fs.remove(recipeTempDirectory); - console.debug('Recipe temp directory removed', recipeTempDirectory); - fs.remove(path.join(recipesDirectory, recipeId, 'recipe.tar.gz')); - console.debug('Recipe tar.gz removed', path.join(recipesDirectory, recipeId, 'recipe.tar.gz')); return id; } catch (err) { -- cgit v1.2.3-70-g09d2 From fcbf398c9a5473cd20f6560c7012bb4dc43f90de Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Wed, 13 Dec 2017 11:18:13 +0100 Subject: fix mobx issue with settings model --- src/api/server/LocalApi.js | 4 +--- src/models/Settings.js | 6 +++++- src/stores/SettingsStore.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'src/api/server') diff --git a/src/api/server/LocalApi.js b/src/api/server/LocalApi.js index eba236f16..79ac6e12f 100644 --- a/src/api/server/LocalApi.js +++ b/src/api/server/LocalApi.js @@ -1,5 +1,3 @@ -import SettingsModel from '../../models/Settings'; - export default class LocalApi { // App async updateAppSettings(data) { @@ -15,7 +13,7 @@ export default class LocalApi { async getAppSettings() { const settingsString = localStorage.getItem('app'); try { - const settings = new SettingsModel(JSON.parse(settingsString) || {}); + const settings = JSON.parse(settingsString) || {}; console.debug('LocalApi::getAppSettings resolves', settings); return settings; diff --git a/src/models/Settings.js b/src/models/Settings.js index 35bfe0d05..ca44da258 100644 --- a/src/models/Settings.js +++ b/src/models/Settings.js @@ -1,4 +1,4 @@ -import { observable } from 'mobx'; +import { observable, extendObservable } from 'mobx'; import { DEFAULT_APP_SETTINGS } from '../config'; export default class Settings { @@ -17,4 +17,8 @@ export default class Settings { constructor(data) { Object.assign(this, data); } + + update(data) { + extendObservable(this, data); + } } diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index 33473f16d..da99a720f 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js @@ -26,7 +26,7 @@ export default class SettingsStore extends Store { } @computed get all() { - return this.allSettingsRequest.result || new SettingsModel(); + return new SettingsModel(this.allSettingsRequest.result); } @action async _update({ settings }) { -- 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/api/server') 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 c6e2e18430e3eef7ec545704dded359d9c706683 Mon Sep 17 00:00:00 2001 From: Danny Qiu Date: Fri, 29 Dec 2017 01:33:22 -0500 Subject: Delete session partition with service --- src/api/server/ServerApi.js | 6 +++++- src/helpers/service-helpers.js | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 src/helpers/service-helpers.js (limited to 'src/api/server') diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index 8b3136d27..3daa2d8b6 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js @@ -22,6 +22,10 @@ import { loadRecipeConfig, } from '../../helpers/recipe-helpers'; +import { + removeServicePartitionDirectory, +} from '../../helpers/service-helpers.js'; + module.paths.unshift( getDevRecipeDirectory(), getRecipeDirectory(), @@ -209,7 +213,7 @@ export default class ServerApi { throw request; } const data = await request.json(); - + await removeServicePartitionDirectory(id); console.debug('ServerApi::deleteService resolves', data); return data; } diff --git a/src/helpers/service-helpers.js b/src/helpers/service-helpers.js new file mode 100644 index 000000000..eb804103e --- /dev/null +++ b/src/helpers/service-helpers.js @@ -0,0 +1,19 @@ +import path from 'path'; +import { remote } from 'electron'; +import fs from 'fs-extra'; + +const app = remote.app; + +function getServicePartitionsDirectory() { + return path.join(app.getPath('userData'), 'Partitions'); +} + +export function removeServicePartitionDirectory(id = '') { + const servicePartition = path.join(getServicePartitionsDirectory(), `service-${id}`); + return fs.remove(servicePartition); +} + +export async function getServiceIdsFromPartitions() { + const files = await fs.readdir(getServicePartitionsDirectory()); + return files.map(filename => filename.replace('service-', '')); +} -- 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/api/server') 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 ? ( +
{/* Updates */}

{intl.formatMessage(messages.headlineUpdates)}

@@ -195,6 +199,7 @@ export default class EditSettingsForm extends Component { /> ) : (