diff options
29 files changed, 471 insertions, 93 deletions
diff --git a/gulpfile.babel.js b/gulpfile.babel.js index b50001b2d..95b026f66 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js | |||
@@ -112,8 +112,6 @@ export function watch() { | |||
112 | export function webserver() { | 112 | export function webserver() { |
113 | gulp.src([ | 113 | gulp.src([ |
114 | paths.dest, | 114 | paths.dest, |
115 | `!${paths.dest}/electron/**`, | ||
116 | `!${paths.dest}/webview/**`, | ||
117 | ]) | 115 | ]) |
118 | .pipe(server({ | 116 | .pipe(server({ |
119 | livereload: true, | 117 | livereload: true, |
diff --git a/package.json b/package.json index 5dae5bc7c..5f5dd4182 100644 --- a/package.json +++ b/package.json | |||
@@ -34,8 +34,9 @@ | |||
34 | "babel-polyfill": "^6.23.0", | 34 | "babel-polyfill": "^6.23.0", |
35 | "babel-runtime": "^6.23.0", | 35 | "babel-runtime": "^6.23.0", |
36 | "classnames": "^2.2.5", | 36 | "classnames": "^2.2.5", |
37 | "du": "^0.1.0", | ||
37 | "electron-fetch": "^1.1.0", | 38 | "electron-fetch": "^1.1.0", |
38 | "electron-spellchecker": "^1.2.0", | 39 | "electron-spellchecker": "^1.1.2", |
39 | "electron-updater": "^2.4.3", | 40 | "electron-updater": "^2.4.3", |
40 | "electron-window-state": "^4.1.0", | 41 | "electron-window-state": "^4.1.0", |
41 | "fs-extra": "^3.0.1", | 42 | "fs-extra": "^3.0.1", |
@@ -54,6 +55,7 @@ | |||
54 | "mobx-react-router": "^3.1.2", | 55 | "mobx-react-router": "^3.1.2", |
55 | "moment": "^2.17.1", | 56 | "moment": "^2.17.1", |
56 | "normalize-url": "^1.9.1", | 57 | "normalize-url": "^1.9.1", |
58 | "pretty-bytes": "^4.0.2", | ||
57 | "prop-types": "^15.5.10", | 59 | "prop-types": "^15.5.10", |
58 | "prop-types-extended": "^0.2.1", | 60 | "prop-types-extended": "^0.2.1", |
59 | "react": "^15.4.1", | 61 | "react": "^15.4.1", |
@@ -105,7 +107,7 @@ | |||
105 | "gulp-sass": "^3.1.0", | 107 | "gulp-sass": "^3.1.0", |
106 | "gulp-sass-variables": "^1.1.1", | 108 | "gulp-sass-variables": "^1.1.1", |
107 | "gulp-server-livereload": "^1.9.2", | 109 | "gulp-server-livereload": "^1.9.2", |
108 | "node-sass": "^4.5.3" | 110 | "node-sass": "^4.7.2" |
109 | }, | 111 | }, |
110 | "config": { | 112 | "config": { |
111 | "commitizen": { | 113 | "commitizen": { |
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 @@ | |||
1 | export default class ServicesApi { | 1 | export 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 @@ | |||
1 | import { remote } from 'electron'; | ||
2 | import du from 'du'; | ||
3 | |||
4 | import { getServicePartitionsDirectory } from '../../helpers/service-helpers.js'; | ||
5 | |||
6 | const { session } = remote; | ||
7 | |||
1 | export default class LocalApi { | 8 | export 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 6b96f709e..a684ff98b 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 | ||
25 | import { | ||
26 | removeServicePartitionDirectory, | ||
27 | } from '../../helpers/service-helpers.js'; | ||
28 | |||
25 | module.paths.unshift( | 29 | module.paths.unshift( |
26 | getDevRecipeDirectory(), | 30 | getDevRecipeDirectory(), |
27 | getRecipeDirectory(), | 31 | getRecipeDirectory(), |
@@ -248,6 +252,8 @@ export default class ServerApi { | |||
248 | } | 252 | } |
249 | const data = await request.json(); | 253 | const data = await request.json(); |
250 | 254 | ||
255 | removeServicePartitionDirectory(id, true); | ||
256 | |||
251 | console.debug('ServerApi::deleteService resolves', data); | 257 | console.debug('ServerApi::deleteService resolves', data); |
252 | return data; | 258 | return data; |
253 | } | 259 | } |
diff --git a/src/app.js b/src/app.js index a0b88611c..8e62776d2 100644 --- a/src/app.js +++ b/src/app.js | |||
@@ -105,3 +105,5 @@ window.addEventListener('load', () => { | |||
105 | // Prevent drag and drop into window from redirecting | 105 | // Prevent drag and drop into window from redirecting |
106 | window.addEventListener('dragover', event => event.preventDefault()); | 106 | window.addEventListener('dragover', event => event.preventDefault()); |
107 | window.addEventListener('drop', event => event.preventDefault()); | 107 | window.addEventListener('drop', event => event.preventDefault()); |
108 | window.addEventListener('dragover', event => event.stopPropagation()); | ||
109 | window.addEventListener('drop', event => event.stopPropagation()); | ||
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index b6ade5da4..4610c69a5 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -16,6 +16,10 @@ const messages = defineMessages({ | |||
16 | id: 'settings.recipes.headline', | 16 | id: 'settings.recipes.headline', |
17 | defaultMessage: '!!!Available Services', | 17 | defaultMessage: '!!!Available Services', |
18 | }, | 18 | }, |
19 | searchService: { | ||
20 | id: 'settings.searchService', | ||
21 | defaultMessage: '!!!Search service', | ||
22 | }, | ||
19 | mostPopularRecipes: { | 23 | mostPopularRecipes: { |
20 | id: 'settings.recipes.mostPopular', | 24 | id: 'settings.recipes.mostPopular', |
21 | defaultMessage: '!!!Most popular', | 25 | defaultMessage: '!!!Most popular', |
@@ -81,13 +85,7 @@ export default class RecipesDashboard extends Component { | |||
81 | return ( | 85 | return ( |
82 | <div className="settings__main"> | 86 | <div className="settings__main"> |
83 | <div className="settings__header"> | 87 | <div className="settings__header"> |
84 | <SearchInput | 88 | <h1>{intl.formatMessage(messages.headline)}</h1> |
85 | className="settings__search-header" | ||
86 | defaultValue={intl.formatMessage(messages.headline)} | ||
87 | onChange={e => searchRecipes(e)} | ||
88 | onReset={() => resetSearch()} | ||
89 | throttle | ||
90 | /> | ||
91 | </div> | 89 | </div> |
92 | <div className="settings__body recipes"> | 90 | <div className="settings__body recipes"> |
93 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( | 91 | {serviceStatus.length > 0 && serviceStatus.includes('created') && ( |
@@ -101,7 +99,13 @@ export default class RecipesDashboard extends Component { | |||
101 | </Infobox> | 99 | </Infobox> |
102 | </Appear> | 100 | </Appear> |
103 | )} | 101 | )} |
104 | {/* {!searchNeedle && ( */} | 102 | <SearchInput |
103 | placeholder={intl.formatMessage(messages.searchService)} | ||
104 | onChange={e => searchRecipes(e)} | ||
105 | onReset={() => resetSearch()} | ||
106 | autoFocus | ||
107 | throttle | ||
108 | /> | ||
105 | <div className="recipes__navigation"> | 109 | <div className="recipes__navigation"> |
106 | <Link | 110 | <Link |
107 | to="/settings/recipes" | 111 | to="/settings/recipes" |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index c0a993736..f6f2df2f3 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -78,7 +78,7 @@ const messages = defineMessages({ | |||
78 | }, | 78 | }, |
79 | headlineBadges: { | 79 | headlineBadges: { |
80 | id: 'settings.service.form.headlineBadges', | 80 | id: 'settings.service.form.headlineBadges', |
81 | defaultMessage: '!!!Unread message dadges', | 81 | defaultMessage: '!!!Unread message badges', |
82 | }, | 82 | }, |
83 | headlineGeneral: { | 83 | headlineGeneral: { |
84 | id: 'settings.service.form.headlineGeneral', | 84 | id: 'settings.service.form.headlineGeneral', |
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js index 5f146b5f3..20e451f01 100644 --- a/src/components/settings/services/ServicesDashboard.js +++ b/src/components/settings/services/ServicesDashboard.js | |||
@@ -15,10 +15,18 @@ const messages = defineMessages({ | |||
15 | id: 'settings.services.headline', | 15 | id: 'settings.services.headline', |
16 | defaultMessage: '!!!Your services', | 16 | defaultMessage: '!!!Your services', |
17 | }, | 17 | }, |
18 | searchService: { | ||
19 | id: 'settings.searchService', | ||
20 | defaultMessage: '!!!Search service', | ||
21 | }, | ||
18 | noServicesAdded: { | 22 | noServicesAdded: { |
19 | id: 'settings.services.noServicesAdded', | 23 | id: 'settings.services.noServicesAdded', |
20 | defaultMessage: '!!!You haven\'t added any services yet.', | 24 | defaultMessage: '!!!You haven\'t added any services yet.', |
21 | }, | 25 | }, |
26 | noServiceFound: { | ||
27 | id: 'settings.recipes.nothingFound', | ||
28 | defaultMessage: '!!!Sorry, but no service matched your search term.', | ||
29 | }, | ||
22 | discoverServices: { | 30 | discoverServices: { |
23 | id: 'settings.services.discoverServices', | 31 | id: 'settings.services.discoverServices', |
24 | defaultMessage: '!!!Discover services', | 32 | defaultMessage: '!!!Discover services', |
@@ -53,7 +61,13 @@ export default class ServicesDashboard extends Component { | |||
53 | servicesRequestFailed: PropTypes.bool.isRequired, | 61 | servicesRequestFailed: PropTypes.bool.isRequired, |
54 | retryServicesRequest: PropTypes.func.isRequired, | 62 | retryServicesRequest: PropTypes.func.isRequired, |
55 | status: MobxPropTypes.arrayOrObservableArray.isRequired, | 63 | status: MobxPropTypes.arrayOrObservableArray.isRequired, |
64 | searchNeedle: PropTypes.string, | ||
56 | }; | 65 | }; |
66 | |||
67 | static defaultProps = { | ||
68 | searchNeedle: '', | ||
69 | } | ||
70 | |||
57 | static contextTypes = { | 71 | static contextTypes = { |
58 | intl: intlShape, | 72 | intl: intlShape, |
59 | }; | 73 | }; |
@@ -69,20 +83,24 @@ export default class ServicesDashboard extends Component { | |||
69 | servicesRequestFailed, | 83 | servicesRequestFailed, |
70 | retryServicesRequest, | 84 | retryServicesRequest, |
71 | status, | 85 | status, |
86 | searchNeedle, | ||
72 | } = this.props; | 87 | } = this.props; |
73 | const { intl } = this.context; | 88 | const { intl } = this.context; |
74 | 89 | ||
75 | return ( | 90 | return ( |
76 | <div className="settings__main"> | 91 | <div className="settings__main"> |
77 | <div className="settings__header"> | 92 | <div className="settings__header"> |
78 | <SearchInput | 93 | <h1>{intl.formatMessage(messages.headline)}</h1> |
79 | className="settings__search-header" | ||
80 | defaultValue={intl.formatMessage(messages.headline)} | ||
81 | onChange={needle => filterServices({ needle })} | ||
82 | onReset={() => resetFilter()} | ||
83 | /> | ||
84 | </div> | 94 | </div> |
85 | <div className="settings__body"> | 95 | <div className="settings__body"> |
96 | {!isLoading && ( | ||
97 | <SearchInput | ||
98 | placeholder={intl.formatMessage(messages.searchService)} | ||
99 | onChange={needle => filterServices({ needle })} | ||
100 | onReset={() => resetFilter()} | ||
101 | autoFocus | ||
102 | /> | ||
103 | )} | ||
86 | {!isLoading && servicesRequestFailed && ( | 104 | {!isLoading && servicesRequestFailed && ( |
87 | <div> | 105 | <div> |
88 | <Infobox | 106 | <Infobox |
@@ -121,7 +139,7 @@ export default class ServicesDashboard extends Component { | |||
121 | </Appear> | 139 | </Appear> |
122 | )} | 140 | )} |
123 | 141 | ||
124 | {!isLoading && services.length === 0 && ( | 142 | {!isLoading && services.length === 0 && !searchNeedle && ( |
125 | <div className="align-middle settings__empty-state"> | 143 | <div className="align-middle settings__empty-state"> |
126 | <p className="settings__empty-text"> | 144 | <p className="settings__empty-text"> |
127 | <span className="emoji"> | 145 | <span className="emoji"> |
@@ -132,6 +150,16 @@ export default class ServicesDashboard extends Component { | |||
132 | <Link to="/settings/recipes" className="button">{intl.formatMessage(messages.discoverServices)}</Link> | 150 | <Link to="/settings/recipes" className="button">{intl.formatMessage(messages.discoverServices)}</Link> |
133 | </div> | 151 | </div> |
134 | )} | 152 | )} |
153 | {!isLoading && services.length === 0 && searchNeedle && ( | ||
154 | <div className="align-middle settings__empty-state"> | ||
155 | <p className="settings__empty-text"> | ||
156 | <span className="emoji"> | ||
157 | <img src="./assets/images/emoji/dontknow.png" alt="" /> | ||
158 | </span> | ||
159 | {intl.formatMessage(messages.noServiceFound)} | ||
160 | </p> | ||
161 | </div> | ||
162 | )} | ||
135 | {isLoading ? ( | 163 | {isLoading ? ( |
136 | <Loader /> | 164 | <Loader /> |
137 | ) : ( | 165 | ) : ( |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index ff398aa33..72aa5a8af 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -40,6 +40,18 @@ 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 | subheadlineCache: { | ||
44 | id: 'settings.app.subheadlineCache', | ||
45 | defaultMessage: '!!!Cache', | ||
46 | }, | ||
47 | cacheInfo: { | ||
48 | id: 'settings.app.cacheInfo', | ||
49 | defaultMessage: '!!!Franz cache is currently using {size} of disk space.', | ||
50 | }, | ||
51 | buttonClearAllCache: { | ||
52 | id: 'settings.app.buttonClearAllCache', | ||
53 | defaultMessage: '!!!Clear cache', | ||
54 | }, | ||
43 | buttonSearchForUpdate: { | 55 | buttonSearchForUpdate: { |
44 | id: 'settings.app.buttonSearchForUpdate', | 56 | id: 'settings.app.buttonSearchForUpdate', |
45 | defaultMessage: '!!!Check for updates', | 57 | defaultMessage: '!!!Check for updates', |
@@ -77,6 +89,9 @@ export default class EditSettingsForm extends Component { | |||
77 | isUpdateAvailable: PropTypes.bool.isRequired, | 89 | isUpdateAvailable: PropTypes.bool.isRequired, |
78 | noUpdateAvailable: PropTypes.bool.isRequired, | 90 | noUpdateAvailable: PropTypes.bool.isRequired, |
79 | updateIsReadyToInstall: PropTypes.bool.isRequired, | 91 | updateIsReadyToInstall: PropTypes.bool.isRequired, |
92 | isClearingAllCache: PropTypes.bool.isRequired, | ||
93 | onClearAllCache: PropTypes.func.isRequired, | ||
94 | cacheSize: PropTypes.string.isRequired, | ||
80 | }; | 95 | }; |
81 | 96 | ||
82 | static contextTypes = { | 97 | static contextTypes = { |
@@ -103,6 +118,9 @@ export default class EditSettingsForm extends Component { | |||
103 | isUpdateAvailable, | 118 | isUpdateAvailable, |
104 | noUpdateAvailable, | 119 | noUpdateAvailable, |
105 | updateIsReadyToInstall, | 120 | updateIsReadyToInstall, |
121 | isClearingAllCache, | ||
122 | onClearAllCache, | ||
123 | cacheSize, | ||
106 | } = this.props; | 124 | } = this.props; |
107 | const { intl } = this.context; | 125 | const { intl } = this.context; |
108 | 126 | ||
@@ -155,6 +173,25 @@ export default class EditSettingsForm extends Component { | |||
155 | <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2> | 173 | <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2> |
156 | <Toggle field={form.$('enableSpellchecking')} /> | 174 | <Toggle field={form.$('enableSpellchecking')} /> |
157 | {/* <Select field={form.$('spellcheckingLanguage')} /> */} | 175 | {/* <Select field={form.$('spellcheckingLanguage')} /> */} |
176 | <div className="settings__settings-group"> | ||
177 | <h3> | ||
178 | {intl.formatMessage(messages.subheadlineCache)} | ||
179 | </h3> | ||
180 | <p> | ||
181 | {intl.formatMessage(messages.cacheInfo, { | ||
182 | size: cacheSize, | ||
183 | })} | ||
184 | </p> | ||
185 | <p> | ||
186 | <Button | ||
187 | buttonType="secondary" | ||
188 | label={intl.formatMessage(messages.buttonClearAllCache)} | ||
189 | onClick={onClearAllCache} | ||
190 | disabled={isClearingAllCache} | ||
191 | loaded={!isClearingAllCache} | ||
192 | /> | ||
193 | </p> | ||
194 | </div> | ||
158 | 195 | ||
159 | {/* Updates */} | 196 | {/* Updates */} |
160 | <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> | 197 | <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> |
@@ -165,6 +202,7 @@ export default class EditSettingsForm extends Component { | |||
165 | /> | 202 | /> |
166 | ) : ( | 203 | ) : ( |
167 | <Button | 204 | <Button |
205 | buttonType="secondary" | ||
168 | label={intl.formatMessage(updateButtonLabelMessage)} | 206 | label={intl.formatMessage(updateButtonLabelMessage)} |
169 | onClick={checkForUpdates} | 207 | onClick={checkForUpdates} |
170 | disabled={isCheckingForUpdates || isUpdateAvailable} | 208 | disabled={isCheckingForUpdates || isUpdateAvailable} |
diff --git a/src/components/ui/SearchInput.js b/src/components/ui/SearchInput.js index bca412cef..a94cde201 100644 --- a/src/components/ui/SearchInput.js +++ b/src/components/ui/SearchInput.js | |||
@@ -9,36 +9,46 @@ import { debounce } from 'lodash'; | |||
9 | export default class SearchInput extends Component { | 9 | export default class SearchInput extends Component { |
10 | static propTypes = { | 10 | static propTypes = { |
11 | value: PropTypes.string, | 11 | value: PropTypes.string, |
12 | defaultValue: PropTypes.string, | 12 | placeholder: PropTypes.string, |
13 | className: PropTypes.string, | 13 | className: PropTypes.string, |
14 | onChange: PropTypes.func, | 14 | onChange: PropTypes.func, |
15 | onReset: PropTypes.func, | 15 | onReset: PropTypes.func, |
16 | name: PropTypes.string, | 16 | name: PropTypes.string, |
17 | throttle: PropTypes.bool, | 17 | throttle: PropTypes.bool, |
18 | throttleDelay: PropTypes.number, | 18 | throttleDelay: PropTypes.number, |
19 | autoFocus: PropTypes.bool, | ||
19 | }; | 20 | }; |
20 | 21 | ||
21 | static defaultProps = { | 22 | static defaultProps = { |
22 | value: '', | 23 | value: '', |
23 | defaultValue: '', | 24 | placeholder: '', |
24 | className: '', | 25 | className: '', |
25 | name: uuidv1(), | 26 | name: uuidv1(), |
26 | throttle: false, | 27 | throttle: false, |
27 | throttleDelay: 250, | 28 | throttleDelay: 250, |
28 | onChange: () => null, | 29 | onChange: () => null, |
29 | onReset: () => null, | 30 | onReset: () => null, |
31 | autoFocus: false, | ||
30 | } | 32 | } |
31 | 33 | ||
32 | constructor(props) { | 34 | constructor(props) { |
33 | super(props); | 35 | super(props); |
34 | 36 | ||
35 | this.state = { | 37 | this.state = { |
36 | value: props.value || props.defaultValue, | 38 | value: props.value, |
37 | }; | 39 | }; |
38 | 40 | ||
39 | this.throttledOnChange = debounce(this.throttledOnChange, this.props.throttleDelay); | 41 | this.throttledOnChange = debounce(this.throttledOnChange, this.props.throttleDelay); |
40 | } | 42 | } |
41 | 43 | ||
44 | componentDidMount() { | ||
45 | const { autoFocus } = this.props; | ||
46 | |||
47 | if (autoFocus) { | ||
48 | this.input.focus(); | ||
49 | } | ||
50 | } | ||
51 | |||
42 | onChange(e) { | 52 | onChange(e) { |
43 | const { throttle, onChange } = this.props; | 53 | const { throttle, onChange } = this.props; |
44 | const { value } = e.target; | 54 | const { value } = e.target; |
@@ -52,26 +62,6 @@ export default class SearchInput extends Component { | |||
52 | } | 62 | } |
53 | } | 63 | } |
54 | 64 | ||
55 | onClick() { | ||
56 | const { defaultValue } = this.props; | ||
57 | const { value } = this.state; | ||
58 | |||
59 | if (value === defaultValue) { | ||
60 | this.setState({ value: '' }); | ||
61 | } | ||
62 | |||
63 | this.input.focus(); | ||
64 | } | ||
65 | |||
66 | onBlur() { | ||
67 | const { defaultValue } = this.props; | ||
68 | const { value } = this.state; | ||
69 | |||
70 | if (value === '') { | ||
71 | this.setState({ value: defaultValue }); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | throttledOnChange(e) { | 65 | throttledOnChange(e) { |
76 | const { onChange } = this.props; | 66 | const { onChange } = this.props; |
77 | 67 | ||
@@ -79,8 +69,8 @@ export default class SearchInput extends Component { | |||
79 | } | 69 | } |
80 | 70 | ||
81 | reset() { | 71 | reset() { |
82 | const { defaultValue, onReset } = this.props; | 72 | const { onReset } = this.props; |
83 | this.setState({ value: defaultValue }); | 73 | this.setState({ value: '' }); |
84 | 74 | ||
85 | onReset(); | 75 | onReset(); |
86 | } | 76 | } |
@@ -88,7 +78,7 @@ export default class SearchInput extends Component { | |||
88 | input = null; | 78 | input = null; |
89 | 79 | ||
90 | render() { | 80 | render() { |
91 | const { className, name, defaultValue } = this.props; | 81 | const { className, name, placeholder } = this.props; |
92 | const { value } = this.state; | 82 | const { value } = this.state; |
93 | 83 | ||
94 | return ( | 84 | return ( |
@@ -101,18 +91,16 @@ export default class SearchInput extends Component { | |||
101 | <label | 91 | <label |
102 | htmlFor={name} | 92 | htmlFor={name} |
103 | className="mdi mdi-magnify" | 93 | className="mdi mdi-magnify" |
104 | onClick={() => this.onClick()} | ||
105 | /> | 94 | /> |
106 | <input | 95 | <input |
107 | name={name} | 96 | name={name} |
108 | type="text" | 97 | type="text" |
98 | placeholder={placeholder} | ||
109 | value={value} | 99 | value={value} |
110 | onChange={e => this.onChange(e)} | 100 | onChange={e => this.onChange(e)} |
111 | onClick={() => this.onClick()} | ||
112 | onBlur={() => this.onBlur()} | ||
113 | ref={(ref) => { this.input = ref; }} | 101 | ref={(ref) => { this.input = ref; }} |
114 | /> | 102 | /> |
115 | {value !== defaultValue && value.length > 0 && ( | 103 | {value.length > 0 && ( |
116 | <span | 104 | <span |
117 | className="mdi mdi-close-circle-outline" | 105 | className="mdi mdi-close-circle-outline" |
118 | onClick={() => this.reset()} | 106 | onClick={() => this.reset()} |
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/containers/settings/ServicesScreen.js b/src/containers/settings/ServicesScreen.js index 8cfe5efbf..12db1bcd3 100644 --- a/src/containers/settings/ServicesScreen.js +++ b/src/containers/settings/ServicesScreen.js | |||
@@ -53,6 +53,7 @@ export default class ServicesScreen extends Component { | |||
53 | goTo={router.push} | 53 | goTo={router.push} |
54 | servicesRequestFailed={services.allServicesRequest.wasExecuted && services.allServicesRequest.isError} | 54 | servicesRequestFailed={services.allServicesRequest.wasExecuted && services.allServicesRequest.isError} |
55 | retryServicesRequest={() => services.allServicesRequest.reload()} | 55 | retryServicesRequest={() => services.allServicesRequest.reload()} |
56 | searchNeedle={services.filterNeedle} | ||
56 | /> | 57 | /> |
57 | ); | 58 | ); |
58 | } | 59 | } |
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 @@ | |||
1 | import { app, ipcMain } from 'electron'; | 1 | import { app, ipcMain } from 'electron'; |
2 | import { autoUpdater } from 'electron-updater'; | 2 | import { autoUpdater } from 'electron-updater'; |
3 | import { isDevMode } from '../../environment.js'; | ||
3 | 4 | ||
4 | export default (params) => { | 5 | export 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 @@ | |||
1 | import path from 'path'; | ||
2 | import { remote } from 'electron'; | ||
3 | import fs from 'fs-extra'; | ||
4 | |||
5 | const app = remote.app; | ||
6 | |||
7 | export function getServicePartitionsDirectory() { | ||
8 | return path.join(app.getPath('userData'), 'Partitions'); | ||
9 | } | ||
10 | |||
11 | export function removeServicePartitionDirectory(id = '', addServicePrefix = false) { | ||
12 | const servicePartition = path.join(getServicePartitionsDirectory(), `${addServicePrefix ? 'service-' : ''}${id}`); | ||
13 | |||
14 | return fs.remove(servicePartition); | ||
15 | } | ||
16 | |||
17 | export 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 f3db20aea..7fc9eac1c 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json | |||
@@ -66,6 +66,7 @@ | |||
66 | "sidebar.unmuteApp": "Enable notifications & audio", | 66 | "sidebar.unmuteApp": "Enable notifications & audio", |
67 | "services.welcome": "Welcome to Franz", | 67 | "services.welcome": "Welcome to Franz", |
68 | "services.getStarted": "Get started", | 68 | "services.getStarted": "Get started", |
69 | "settings.searchService": "Search service", | ||
69 | "settings.account.headline": "Account", | 70 | "settings.account.headline": "Account", |
70 | "settings.account.headlineSubscription": "Your subscription", | 71 | "settings.account.headlineSubscription": "Your subscription", |
71 | "settings.account.headlineUpgrade": "Upgrade your account & support Franz", | 72 | "settings.account.headlineUpgrade": "Upgrade your account & support Franz", |
@@ -152,6 +153,9 @@ | |||
152 | "settings.app.updateStatusSearching": "Is searching for update", | 153 | "settings.app.updateStatusSearching": "Is searching for update", |
153 | "settings.app.updateStatusAvailable": "Update available, downloading...", | 154 | "settings.app.updateStatusAvailable": "Update available, downloading...", |
154 | "settings.app.updateStatusUpToDate": "You are using the latest version of Franz", | 155 | "settings.app.updateStatusUpToDate": "You are using the latest version of Franz", |
156 | "settings.app.subheadlineCache": "Cache", | ||
157 | "settings.app.cacheInfo": "Franz cache is currently using {size} of disk space.", | ||
158 | "settings.app.buttonClearAllCache": "Clear cache", | ||
155 | "settings.app.form.autoLaunchOnStart": "Launch Franz on start", | 159 | "settings.app.form.autoLaunchOnStart": "Launch Franz on start", |
156 | "settings.app.form.autoLaunchInBackground": "Open in background", | 160 | "settings.app.form.autoLaunchInBackground": "Open in background", |
157 | "settings.app.form.enableSystemTray": "Show Franz in system tray", | 161 | "settings.app.form.enableSystemTray": "Show Franz in system tray", |
diff --git a/src/index.html b/src/index.html index 05a93e37b..9e5acd705 100644 --- a/src/index.html +++ b/src/index.html | |||
@@ -23,6 +23,24 @@ | |||
23 | s.async = true; | 23 | s.async = true; |
24 | s.setAttribute('src', lrHost + '/livereload.js'); | 24 | s.setAttribute('src', lrHost + '/livereload.js'); |
25 | document.body.appendChild(s); | 25 | document.body.appendChild(s); |
26 | |||
27 | s.onload = () => { | ||
28 | console.log('livereload loaded'); | ||
29 | const originalReloadBehaviour = window._onLiveReloadFileChanged; | ||
30 | |||
31 | window._onLiveReloadFileChanged = (file) => { | ||
32 | if (!file.path.includes('/build/webview/') && !file.path.includes('/build/index.js') && !file.path.includes('/build/electron/')) { | ||
33 | originalReloadBehaviour(file); | ||
34 | } else { | ||
35 | if (file.path.includes('/build/webview/')) { | ||
36 | console.log('Livereload: Reloading all webvies'); | ||
37 | const webviews = document.querySelectorAll('webview').forEach(webview => webview.reload()); | ||
38 | } else { | ||
39 | console.log('Livereload: skip reload as only main process files have changed'); | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | } | ||
26 | })(); | 44 | })(); |
27 | } | 45 | } |
28 | </script> | 46 | </script> |
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 | ||
46 | if (isSecondInstance) { | 46 | if (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 5a6c12ee1..e33f50f05 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js | |||
@@ -1,10 +1,11 @@ | |||
1 | import { remote, ipcRenderer, shell } from 'electron'; | 1 | import { remote, ipcRenderer, shell } from 'electron'; |
2 | import { action, observable } from 'mobx'; | 2 | import { action, computed, observable } from 'mobx'; |
3 | import moment from 'moment'; | 3 | import moment from 'moment'; |
4 | import key from 'keymaster'; | 4 | import key from 'keymaster'; |
5 | import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; | 5 | import { getDoNotDisturb } from '@meetfranz/electron-notification-state'; |
6 | import idleTimer from '@paulcbetts/system-idle-time'; | 6 | import idleTimer from '@paulcbetts/system-idle-time'; |
7 | import AutoLaunch from 'auto-launch'; | 7 | import AutoLaunch from 'auto-launch'; |
8 | import prettyBytes from 'pretty-bytes'; | ||
8 | 9 | ||
9 | import Store from './lib/Store'; | 10 | import Store from './lib/Store'; |
10 | import Request from './lib/Request'; | 11 | import Request from './lib/Request'; |
@@ -14,7 +15,10 @@ import locales from '../i18n/translations'; | |||
14 | import { gaEvent } from '../lib/analytics'; | 15 | import { gaEvent } from '../lib/analytics'; |
15 | import Miner from '../lib/Miner'; | 16 | import Miner from '../lib/Miner'; |
16 | 17 | ||
17 | const { app, powerMonitor } = remote; | 18 | import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; |
19 | |||
20 | const { app } = remote; | ||
21 | |||
18 | const defaultLocale = DEFAULT_APP_SETTINGS.locale; | 22 | const defaultLocale = DEFAULT_APP_SETTINGS.locale; |
19 | const autoLauncher = new AutoLaunch({ | 23 | const 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), |
@@ -124,15 +133,23 @@ export default class AppStore extends Store { | |||
124 | this.stores.router.push(data.url); | 133 | this.stores.router.push(data.url); |
125 | }); | 134 | }); |
126 | 135 | ||
136 | const TIMEOUT = 5000; | ||
127 | // Check system idle time every minute | 137 | // Check system idle time every minute |
128 | setInterval(() => { | 138 | setInterval(() => { |
129 | this.idleTime = idleTimer.getIdleTime(); | 139 | this.idleTime = idleTimer.getIdleTime(); |
130 | }, 60000); | 140 | }, TIMEOUT); |
131 | 141 | ||
132 | // Reload all services after a healthy nap | 142 | // Reload all services after a healthy nap |
133 | powerMonitor.on('resume', () => { | 143 | // Alternative solution for powerMonitor as the resume event is not fired |
134 | setTimeout(window.location.reload, 5000); | 144 | // More information: https://github.com/electron/electron/issues/1615 |
135 | }); | 145 | let lastTime = (new Date()).getTime(); |
146 | setInterval(() => { | ||
147 | const currentTime = (new Date()).getTime(); | ||
148 | if (currentTime > (lastTime + TIMEOUT + 2000)) { | ||
149 | this._reactivateServices(); | ||
150 | } | ||
151 | lastTime = currentTime; | ||
152 | }, TIMEOUT); | ||
136 | 153 | ||
137 | // Set active the next service | 154 | // Set active the next service |
138 | key( | 155 | key( |
@@ -157,6 +174,10 @@ export default class AppStore extends Store { | |||
157 | this._healthCheck(); | 174 | this._healthCheck(); |
158 | } | 175 | } |
159 | 176 | ||
177 | @computed get cacheSize() { | ||
178 | return prettyBytes(this.getAppCacheSizeRequest.execute().result || 0); | ||
179 | } | ||
180 | |||
160 | // Actions | 181 | // Actions |
161 | @action _notify({ title, options, notificationId, serviceId = null }) { | 182 | @action _notify({ title, options, notificationId, serviceId = null }) { |
162 | if (this.stores.settings.all.isAppMuted) return; | 183 | if (this.stores.settings.all.isAppMuted) return; |
@@ -247,6 +268,23 @@ export default class AppStore extends Store { | |||
247 | this._muteApp({ isMuted: !this.stores.settings.all.isAppMuted }); | 268 | this._muteApp({ isMuted: !this.stores.settings.all.isAppMuted }); |
248 | } | 269 | } |
249 | 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 | |||
250 | // Reactions | 288 | // Reactions |
251 | _offlineCheck() { | 289 | _offlineCheck() { |
252 | if (!this.isOnline) { | 290 | if (!this.isOnline) { |
@@ -357,6 +395,16 @@ export default class AppStore extends Store { | |||
357 | return autoLauncher.isEnabled() || false; | 395 | return autoLauncher.isEnabled() || false; |
358 | } | 396 | } |
359 | 397 | ||
398 | _reactivateServices(retryCount = 0) { | ||
399 | if (!this.isOnline) { | ||
400 | console.debug('reactivateServices: computer is offline, trying again in 5s, retries:', retryCount); | ||
401 | setTimeout(() => this._reactivateServices(retryCount + 1), 5000); | ||
402 | } else { | ||
403 | console.debug('reactivateServices: reload all services'); | ||
404 | this.actions.service.reloadAll(); | ||
405 | } | ||
406 | } | ||
407 | |||
360 | _systemDND() { | 408 | _systemDND() { |
361 | const dnd = getDoNotDisturb(); | 409 | const dnd = getDoNotDisturb(); |
362 | if (dnd === this.stores.settings.all.isAppMuted || !this.isSystemMuteOverriden) { | 410 | if (dnd === this.stores.settings.all.isAppMuted || !this.isSystemMuteOverriden) { |
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index f2a8683ba..7300a76c8 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)); |
@@ -225,6 +227,13 @@ export default class ServicesStore extends Store { | |||
225 | gaEvent('Service', 'delete', service.recipe.id); | 227 | gaEvent('Service', 'delete', service.recipe.id); |
226 | } | 228 | } |
227 | 229 | ||
230 | @action async _clearCache({ serviceId }) { | ||
231 | this.clearCacheRequest.reset(); | ||
232 | const request = this.clearCacheRequest.execute(serviceId); | ||
233 | await request._promise; | ||
234 | gaEvent('Service', 'clear cache'); | ||
235 | } | ||
236 | |||
228 | @action _setActive({ serviceId }) { | 237 | @action _setActive({ serviceId }) { |
229 | const service = this.one(serviceId); | 238 | const service = this.one(serviceId); |
230 | 239 | ||
@@ -347,6 +356,10 @@ export default class ServicesStore extends Store { | |||
347 | redirect: false, | 356 | redirect: false, |
348 | }); | 357 | }); |
349 | } | 358 | } |
359 | } else if (channel === 'new-window') { | ||
360 | const url = args[0]; | ||
361 | |||
362 | this.actions.app.openExternalUrl({ url }); | ||
350 | } | 363 | } |
351 | } | 364 | } |
352 | 365 | ||
@@ -388,7 +401,7 @@ export default class ServicesStore extends Store { | |||
388 | const service = this.one(serviceId); | 401 | const service = this.one(serviceId); |
389 | service.resetMessageCount(); | 402 | service.resetMessageCount(); |
390 | 403 | ||
391 | service.webview.reload(); | 404 | service.webview.loadURL(service.url); |
392 | } | 405 | } |
393 | 406 | ||
394 | @action _reloadActive() { | 407 | @action _reloadActive() { |
@@ -517,12 +530,13 @@ export default class ServicesStore extends Store { | |||
517 | .reduce((a, b) => a + b, 0); | 530 | .reduce((a, b) => a + b, 0); |
518 | 531 | ||
519 | const unreadIndirectMessageCount = this.allDisplayed | 532 | const unreadIndirectMessageCount = this.allDisplayed |
520 | .filter(s => (showMessageBadgeWhenMuted || s.isIndirectMessageBadgeEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled) | 533 | .filter(s => (showMessageBadgeWhenMuted && showMessageBadgesEvenWhenMuted) && (s.isBadgeEnabled && s.isIndirectMessageBadgeEnabled)) |
521 | .map(s => s.unreadIndirectMessageCount) | 534 | .map(s => s.unreadIndirectMessageCount) |
522 | .reduce((a, b) => a + b, 0); | 535 | .reduce((a, b) => a + b, 0); |
523 | 536 | ||
524 | // We can't just block this earlier, otherwise the mobx reaction won't be aware of the vars to watch in some cases | 537 | // We can't just block this earlier, otherwise the mobx reaction won't be aware of the vars to watch in some cases |
525 | if (showMessageBadgesEvenWhenMuted) { | 538 | if (showMessageBadgesEvenWhenMuted) { |
539 | console.log('set badge', unreadDirectMessageCount, unreadIndirectMessageCount); | ||
526 | this.actions.app.setBadge({ | 540 | this.actions.app.setBadge({ |
527 | unreadDirectMessageCount, | 541 | unreadDirectMessageCount, |
528 | unreadIndirectMessageCount, | 542 | unreadIndirectMessageCount, |
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; |
diff --git a/src/styles/searchInput.scss b/src/styles/searchInput.scss index 28ff09fc4..633a31e09 100644 --- a/src/styles/searchInput.scss +++ b/src/styles/searchInput.scss | |||
@@ -1,4 +1,20 @@ | |||
1 | .search-input { | 1 | .search-input { |
2 | width: 100%; | 2 | width: 100%; |
3 | height: auto; | 3 | height: auto; |
4 | display: flex; | ||
5 | align-items: center; | ||
6 | padding: 0 10px; | ||
7 | border-radius: 30px; | ||
8 | background: $theme-gray-lightest; | ||
9 | padding: 5px 10px; | ||
10 | @extend %headline; | ||
11 | color: $theme-gray-light; | ||
12 | |||
13 | input { | ||
14 | padding-left: 10px; | ||
15 | background: none; | ||
16 | border: 0; | ||
17 | flex: 1; | ||
18 | color: $theme-gray-light; | ||
19 | } | ||
4 | } | 20 | } |
diff --git a/src/styles/settings.scss b/src/styles/settings.scss index cb39717ea..2182c9b5f 100644 --- a/src/styles/settings.scss +++ b/src/styles/settings.scss | |||
@@ -149,26 +149,8 @@ | |||
149 | } | 149 | } |
150 | } | 150 | } |
151 | 151 | ||
152 | .settings__search-header { | 152 | .search-input { |
153 | display: flex; | 153 | margin-bottom: 30px; |
154 | align-items: center; | ||
155 | padding: 0 10px; | ||
156 | border-radius: $theme-border-radius; | ||
157 | transition: background $theme-transition-time; | ||
158 | @extend %headline; | ||
159 | font-size: 22px; | ||
160 | |||
161 | &:hover { | ||
162 | background: darken($theme-gray-lighter, 5%); | ||
163 | } | ||
164 | |||
165 | input { | ||
166 | padding-left: 10px; | ||
167 | background: none; | ||
168 | border: 0; | ||
169 | flex: 1; | ||
170 | @extend %headline; | ||
171 | } | ||
172 | } | 154 | } |
173 | 155 | ||
174 | &__options { | 156 | &__options { |
diff --git a/src/webview/notifications.js b/src/webview/notifications.js index 4f602bfdb..2020bbdc6 100644 --- a/src/webview/notifications.js +++ b/src/webview/notifications.js | |||
@@ -16,7 +16,9 @@ class Notification { | |||
16 | })); | 16 | })); |
17 | 17 | ||
18 | ipcRenderer.once(`notification-onclick:${this.notificationId}`, () => { | 18 | ipcRenderer.once(`notification-onclick:${this.notificationId}`, () => { |
19 | this.onclick(); | 19 | if (typeof this.onclick === 'function') { |
20 | this.onclick(); | ||
21 | } | ||
20 | }); | 22 | }); |
21 | } | 23 | } |
22 | 24 | ||
diff --git a/src/webview/plugin.js b/src/webview/plugin.js index 9903ee07a..d9e021e6d 100644 --- a/src/webview/plugin.js +++ b/src/webview/plugin.js | |||
@@ -5,8 +5,8 @@ import path from 'path'; | |||
5 | import { isDevMode } from '../environment'; | 5 | import { isDevMode } from '../environment'; |
6 | import RecipeWebview from './lib/RecipeWebview'; | 6 | import RecipeWebview from './lib/RecipeWebview'; |
7 | 7 | ||
8 | import Spellchecker from './spellchecker.js'; | 8 | import Spellchecker from './spellchecker'; |
9 | import './notifications.js'; | 9 | import './notifications'; |
10 | 10 | ||
11 | ipcRenderer.on('initializeRecipe', (e, data) => { | 11 | ipcRenderer.on('initializeRecipe', (e, data) => { |
12 | const modulePath = path.join(data.recipe.path, 'webview.js'); | 12 | const modulePath = path.join(data.recipe.path, 'webview.js'); |
@@ -39,3 +39,15 @@ ipcRenderer.on('settings-update', (e, data) => { | |||
39 | document.addEventListener('DOMContentLoaded', () => { | 39 | document.addEventListener('DOMContentLoaded', () => { |
40 | ipcRenderer.sendToHost('hello'); | 40 | ipcRenderer.sendToHost('hello'); |
41 | }, false); | 41 | }, false); |
42 | |||
43 | // Patching window.open | ||
44 | const originalWindowOpen = window.open; | ||
45 | |||
46 | window.open = (url, frameName, features) => { | ||
47 | // We need to differentiate if the link should be opened in a popup or in the systems default browser | ||
48 | if (!frameName && !features) { | ||
49 | return ipcRenderer.sendToHost('new-window', url); | ||
50 | } | ||
51 | |||
52 | return originalWindowOpen(url, frameName, features); | ||
53 | }; | ||
@@ -39,7 +39,7 @@ | |||
39 | rimraf "^2.4.0" | 39 | rimraf "^2.4.0" |
40 | underscore "^1.6.0" | 40 | underscore "^1.6.0" |
41 | 41 | ||
42 | "@paulcbetts/spellchecker@^4.0.5": | 42 | "@paulcbetts/spellchecker@^4.0.6": |
43 | version "4.0.6" | 43 | version "4.0.6" |
44 | resolved "https://registry.yarnpkg.com/@paulcbetts/spellchecker/-/spellchecker-4.0.6.tgz#79ef1f9c19c5a3156921ccaa9ffdc3efbbee47e3" | 44 | resolved "https://registry.yarnpkg.com/@paulcbetts/spellchecker/-/spellchecker-4.0.6.tgz#79ef1f9c19c5a3156921ccaa9ffdc3efbbee47e3" |
45 | dependencies: | 45 | dependencies: |
@@ -333,6 +333,10 @@ async@^0.9.0: | |||
333 | version "0.9.2" | 333 | version "0.9.2" |
334 | resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" | 334 | resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" |
335 | 335 | ||
336 | async@~0.1.22: | ||
337 | version "0.1.22" | ||
338 | resolved "https://registry.yarnpkg.com/async/-/async-0.1.22.tgz#0fc1aaa088a0e3ef0ebe2d8831bab0dcf8845061" | ||
339 | |||
336 | asynckit@^0.4.0: | 340 | asynckit@^0.4.0: |
337 | version "0.4.0" | 341 | version "0.4.0" |
338 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" | 342 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" |
@@ -1307,6 +1311,10 @@ capture-stack-trace@^1.0.0: | |||
1307 | version "1.0.0" | 1311 | version "1.0.0" |
1308 | resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" | 1312 | resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" |
1309 | 1313 | ||
1314 | caseless@~0.11.0: | ||
1315 | version "0.11.0" | ||
1316 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" | ||
1317 | |||
1310 | caseless@~0.12.0: | 1318 | caseless@~0.12.0: |
1311 | version "0.12.0" | 1319 | version "0.12.0" |
1312 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" | 1320 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" |
@@ -1783,6 +1791,12 @@ dotenv@^4.0.0: | |||
1783 | version "4.0.0" | 1791 | version "4.0.0" |
1784 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" | 1792 | resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" |
1785 | 1793 | ||
1794 | du@^0.1.0: | ||
1795 | version "0.1.0" | ||
1796 | resolved "https://registry.yarnpkg.com/du/-/du-0.1.0.tgz#f26e340a09c7bc5b6fd69af6dbadea60fa8c6f4d" | ||
1797 | dependencies: | ||
1798 | async "~0.1.22" | ||
1799 | |||
1786 | duplexer2@0.0.2, duplexer2@~0.0.2: | 1800 | duplexer2@0.0.2, duplexer2@~0.0.2: |
1787 | version "0.0.2" | 1801 | version "0.0.2" |
1788 | resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" | 1802 | resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" |
@@ -2017,12 +2031,12 @@ electron-remote@^1.1.1: | |||
2017 | rxjs "^5.0.0-beta.12" | 2031 | rxjs "^5.0.0-beta.12" |
2018 | xmlhttprequest "^1.8.0" | 2032 | xmlhttprequest "^1.8.0" |
2019 | 2033 | ||
2020 | electron-spellchecker@^1.2.0: | 2034 | electron-spellchecker@^1.1.2: |
2021 | version "1.2.0" | 2035 | version "1.1.2" |
2022 | resolved "https://registry.yarnpkg.com/electron-spellchecker/-/electron-spellchecker-1.2.0.tgz#f6306afd4078244c1e6311370667d95b873fbcbb" | 2036 | resolved "https://registry.yarnpkg.com/electron-spellchecker/-/electron-spellchecker-1.1.2.tgz#5fbe1e65d246b77e6e7433ee2387d9d26010f7a8" |
2023 | dependencies: | 2037 | dependencies: |
2024 | "@paulcbetts/cld" "^2.4.6" | 2038 | "@paulcbetts/cld" "^2.4.6" |
2025 | "@paulcbetts/spellchecker" "^4.0.5" | 2039 | "@paulcbetts/spellchecker" "^4.0.6" |
2026 | bcp47 "^1.1.2" | 2040 | bcp47 "^1.1.2" |
2027 | debug "^2.6.3" | 2041 | debug "^2.6.3" |
2028 | electron-remote "^1.1.1" | 2042 | electron-remote "^1.1.1" |
@@ -2728,6 +2742,16 @@ gaze@^1.0.0: | |||
2728 | dependencies: | 2742 | dependencies: |
2729 | globule "^1.0.0" | 2743 | globule "^1.0.0" |
2730 | 2744 | ||
2745 | generate-function@^2.0.0: | ||
2746 | version "2.0.0" | ||
2747 | resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" | ||
2748 | |||
2749 | generate-object-property@^1.1.0: | ||
2750 | version "1.2.0" | ||
2751 | resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" | ||
2752 | dependencies: | ||
2753 | is-property "^1.0.0" | ||
2754 | |||
2731 | get-caller-file@^1.0.1: | 2755 | get-caller-file@^1.0.1: |
2732 | version "1.0.2" | 2756 | version "1.0.2" |
2733 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" | 2757 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" |
@@ -3122,6 +3146,15 @@ har-schema@^1.0.5: | |||
3122 | version "1.0.5" | 3146 | version "1.0.5" |
3123 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" | 3147 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" |
3124 | 3148 | ||
3149 | har-validator@~2.0.6: | ||
3150 | version "2.0.6" | ||
3151 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" | ||
3152 | dependencies: | ||
3153 | chalk "^1.1.1" | ||
3154 | commander "^2.9.0" | ||
3155 | is-my-json-valid "^2.12.4" | ||
3156 | pinkie-promise "^2.0.0" | ||
3157 | |||
3125 | har-validator@~4.2.1: | 3158 | har-validator@~4.2.1: |
3126 | version "4.2.1" | 3159 | version "4.2.1" |
3127 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" | 3160 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" |
@@ -3437,6 +3470,15 @@ is-glob@^3.1.0: | |||
3437 | dependencies: | 3470 | dependencies: |
3438 | is-extglob "^2.1.0" | 3471 | is-extglob "^2.1.0" |
3439 | 3472 | ||
3473 | is-my-json-valid@^2.12.4: | ||
3474 | version "2.17.1" | ||
3475 | resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz#3da98914a70a22f0a8563ef1511a246c6fc55471" | ||
3476 | dependencies: | ||
3477 | generate-function "^2.0.0" | ||
3478 | generate-object-property "^1.1.0" | ||
3479 | jsonpointer "^4.0.0" | ||
3480 | xtend "^4.0.0" | ||
3481 | |||
3440 | is-npm@^1.0.0: | 3482 | is-npm@^1.0.0: |
3441 | version "1.0.0" | 3483 | version "1.0.0" |
3442 | resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" | 3484 | resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" |
@@ -3495,6 +3537,10 @@ is-promise@^2.1.0: | |||
3495 | version "2.1.0" | 3537 | version "2.1.0" |
3496 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" | 3538 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" |
3497 | 3539 | ||
3540 | is-property@^1.0.0: | ||
3541 | version "1.0.2" | ||
3542 | resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" | ||
3543 | |||
3498 | is-redirect@^1.0.0: | 3544 | is-redirect@^1.0.0: |
3499 | version "1.0.0" | 3545 | version "1.0.0" |
3500 | resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" | 3546 | resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" |
@@ -3690,6 +3736,10 @@ jsonify@~0.0.0: | |||
3690 | version "0.0.0" | 3736 | version "0.0.0" |
3691 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" | 3737 | resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" |
3692 | 3738 | ||
3739 | jsonpointer@^4.0.0: | ||
3740 | version "4.0.1" | ||
3741 | resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" | ||
3742 | |||
3693 | jsonwebtoken@^7.4.1: | 3743 | jsonwebtoken@^7.4.1: |
3694 | version "7.4.3" | 3744 | version "7.4.3" |
3695 | resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz#77f5021de058b605a1783fa1283e99812e645638" | 3745 | resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz#77f5021de058b605a1783fa1283e99812e645638" |
@@ -4358,7 +4408,7 @@ node-pre-gyp@^0.6.36: | |||
4358 | tar "^2.2.1" | 4408 | tar "^2.2.1" |
4359 | tar-pack "^3.4.0" | 4409 | tar-pack "^3.4.0" |
4360 | 4410 | ||
4361 | node-sass@^4.2.0, node-sass@^4.5.3: | 4411 | node-sass@^4.2.0: |
4362 | version "4.5.3" | 4412 | version "4.5.3" |
4363 | resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568" | 4413 | resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.5.3.tgz#d09c9d1179641239d1b97ffc6231fdcec53e1568" |
4364 | dependencies: | 4414 | dependencies: |
@@ -4381,6 +4431,30 @@ node-sass@^4.2.0, node-sass@^4.5.3: | |||
4381 | sass-graph "^2.1.1" | 4431 | sass-graph "^2.1.1" |
4382 | stdout-stream "^1.4.0" | 4432 | stdout-stream "^1.4.0" |
4383 | 4433 | ||
4434 | node-sass@^4.7.2: | ||
4435 | version "4.7.2" | ||
4436 | resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" | ||
4437 | dependencies: | ||
4438 | async-foreach "^0.1.3" | ||
4439 | chalk "^1.1.1" | ||
4440 | cross-spawn "^3.0.0" | ||
4441 | gaze "^1.0.0" | ||
4442 | get-stdin "^4.0.1" | ||
4443 | glob "^7.0.3" | ||
4444 | in-publish "^2.0.0" | ||
4445 | lodash.assign "^4.2.0" | ||
4446 | lodash.clonedeep "^4.3.2" | ||
4447 | lodash.mergewith "^4.6.0" | ||
4448 | meow "^3.7.0" | ||
4449 | mkdirp "^0.5.1" | ||
4450 | nan "^2.3.2" | ||
4451 | node-gyp "^3.3.1" | ||
4452 | npmlog "^4.0.0" | ||
4453 | request "~2.79.0" | ||
4454 | sass-graph "^2.2.4" | ||
4455 | stdout-stream "^1.4.0" | ||
4456 | "true-case-path" "^1.0.2" | ||
4457 | |||
4384 | node-watch@^0.3.4: | 4458 | node-watch@^0.3.4: |
4385 | version "0.3.5" | 4459 | version "0.3.5" |
4386 | resolved "https://registry.yarnpkg.com/node-watch/-/node-watch-0.3.5.tgz#a07f253a4f538de9d4ca522dd7f1996eeec0d97e" | 4460 | resolved "https://registry.yarnpkg.com/node-watch/-/node-watch-0.3.5.tgz#a07f253a4f538de9d4ca522dd7f1996eeec0d97e" |
@@ -4835,6 +4909,10 @@ pretty-bytes@^1.0.2, pretty-bytes@^1.0.4: | |||
4835 | get-stdin "^4.0.1" | 4909 | get-stdin "^4.0.1" |
4836 | meow "^3.1.0" | 4910 | meow "^3.1.0" |
4837 | 4911 | ||
4912 | pretty-bytes@^4.0.2: | ||
4913 | version "4.0.2" | ||
4914 | resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" | ||
4915 | |||
4838 | pretty-hrtime@^1.0.0: | 4916 | pretty-hrtime@^1.0.0: |
4839 | version "1.0.3" | 4917 | version "1.0.3" |
4840 | resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" | 4918 | resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" |
@@ -4911,6 +4989,10 @@ q@^1.1.2: | |||
4911 | version "1.5.0" | 4989 | version "1.5.0" |
4912 | resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" | 4990 | resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" |
4913 | 4991 | ||
4992 | qs@~6.3.0: | ||
4993 | version "6.3.2" | ||
4994 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" | ||
4995 | |||
4914 | qs@~6.4.0: | 4996 | qs@~6.4.0: |
4915 | version "6.4.0" | 4997 | version "6.4.0" |
4916 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" | 4998 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" |
@@ -5262,6 +5344,31 @@ request@2, request@^2.45.0, request@^2.54.0, request@^2.79.0, request@^2.81.0: | |||
5262 | tunnel-agent "^0.6.0" | 5344 | tunnel-agent "^0.6.0" |
5263 | uuid "^3.0.0" | 5345 | uuid "^3.0.0" |
5264 | 5346 | ||
5347 | request@~2.79.0: | ||
5348 | version "2.79.0" | ||
5349 | resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" | ||
5350 | dependencies: | ||
5351 | aws-sign2 "~0.6.0" | ||
5352 | aws4 "^1.2.1" | ||
5353 | caseless "~0.11.0" | ||
5354 | combined-stream "~1.0.5" | ||
5355 | extend "~3.0.0" | ||
5356 | forever-agent "~0.6.1" | ||
5357 | form-data "~2.1.1" | ||
5358 | har-validator "~2.0.6" | ||
5359 | hawk "~3.1.3" | ||
5360 | http-signature "~1.1.0" | ||
5361 | is-typedarray "~1.0.0" | ||
5362 | isstream "~0.1.2" | ||
5363 | json-stringify-safe "~5.0.1" | ||
5364 | mime-types "~2.1.7" | ||
5365 | oauth-sign "~0.8.1" | ||
5366 | qs "~6.3.0" | ||
5367 | stringstream "~0.0.4" | ||
5368 | tough-cookie "~2.3.0" | ||
5369 | tunnel-agent "~0.4.1" | ||
5370 | uuid "^3.0.0" | ||
5371 | |||
5265 | require-directory@^2.1.1: | 5372 | require-directory@^2.1.1: |
5266 | version "2.1.1" | 5373 | version "2.1.1" |
5267 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" | 5374 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" |
@@ -5365,7 +5472,7 @@ sanitize-filename@^1.6.0, sanitize-filename@^1.6.1: | |||
5365 | dependencies: | 5472 | dependencies: |
5366 | truncate-utf8-bytes "^1.0.0" | 5473 | truncate-utf8-bytes "^1.0.0" |
5367 | 5474 | ||
5368 | sass-graph@^2.1.1: | 5475 | sass-graph@^2.1.1, sass-graph@^2.2.4: |
5369 | version "2.2.4" | 5476 | version "2.2.4" |
5370 | resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" | 5477 | resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" |
5371 | dependencies: | 5478 | dependencies: |
@@ -5935,6 +6042,12 @@ trim-right@^1.0.1: | |||
5935 | version "1.0.1" | 6042 | version "1.0.1" |
5936 | resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" | 6043 | resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" |
5937 | 6044 | ||
6045 | "true-case-path@^1.0.2": | ||
6046 | version "1.0.2" | ||
6047 | resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" | ||
6048 | dependencies: | ||
6049 | glob "^6.0.4" | ||
6050 | |||
5938 | truncate-utf8-bytes@^1.0.0: | 6051 | truncate-utf8-bytes@^1.0.0: |
5939 | version "1.0.2" | 6052 | version "1.0.2" |
5940 | resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" | 6053 | resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" |
@@ -5951,6 +6064,10 @@ tunnel-agent@^0.6.0: | |||
5951 | dependencies: | 6064 | dependencies: |
5952 | safe-buffer "^5.0.1" | 6065 | safe-buffer "^5.0.1" |
5953 | 6066 | ||
6067 | tunnel-agent@~0.4.1: | ||
6068 | version "0.4.3" | ||
6069 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" | ||
6070 | |||
5954 | tweetnacl@^0.14.3, tweetnacl@~0.14.0: | 6071 | tweetnacl@^0.14.3, tweetnacl@~0.14.0: |
5955 | version "0.14.5" | 6072 | version "0.14.5" |
5956 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" | 6073 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" |
@@ -6310,7 +6427,7 @@ xmlhttprequest@^1.8.0: | |||
6310 | version "1.8.0" | 6427 | version "1.8.0" |
6311 | resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" | 6428 | resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" |
6312 | 6429 | ||
6313 | "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: | 6430 | "xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: |
6314 | version "4.0.1" | 6431 | version "4.0.1" |
6315 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" | 6432 | resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" |
6316 | 6433 | ||