aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Bennett <hello@vantezzen.io>2020-02-26 12:12:02 +0100
committerLibravatar GitHub <noreply@github.com>2020-02-26 12:12:02 +0100
commitce988c280f5ca6dfc202d209e66d7cfce257c43b (patch)
treef469740cba0141af5b15a50b764a79f0b9b84a91
parentAvoid AppVeyor builds on i18n-only commits (diff)
downloadferdium-app-ce988c280f5ca6dfc202d209e66d7cfce257c43b.tar.gz
ferdium-app-ce988c280f5ca6dfc202d209e66d7cfce257c43b.tar.zst
ferdium-app-ce988c280f5ca6dfc202d209e66d7cfce257c43b.zip
Implement user.css and user.js (#401)
* #83 Implement user.css and user.js * Fix button layout in settings * Fix user script not loading Co-authored-by: Amine <amine@mouafik.fr>
-rw-r--r--README.md1
-rw-r--r--src/actions/service.js3
-rw-r--r--src/components/settings/services/EditServiceForm.js65
-rw-r--r--src/containers/settings/EditServiceScreen.js26
-rw-r--r--src/i18n/locales/defaultMessages.json119
-rw-r--r--src/i18n/locales/en-US.json3
-rw-r--r--src/i18n/messages/src/components/settings/services/EditServiceForm.json119
-rw-r--r--src/stores/ServicesStore.js12
-rw-r--r--src/styles/settings.scss10
-rw-r--r--src/webview/recipe.js33
10 files changed, 261 insertions, 130 deletions
diff --git a/README.md b/README.md
index 5c4ca92dc..6a3a8ff54 100644
--- a/README.md
+++ b/README.md
@@ -112,6 +112,7 @@ If you use an AUR Helper e.g. yay, simply install it via `yay -S ferdi`.
112- [x] Add Process Manager to find services using a lot of resources 112- [x] Add Process Manager to find services using a lot of resources
113- [x] Add "npm run prepare-code" command for development to lint and beautify code 113- [x] Add "npm run prepare-code" command for development to lint and beautify code
114- [x] Add button to open darkmode.css for a service 114- [x] Add button to open darkmode.css for a service
115- [x] [Add `user.css` and `user.js` that allows users to inject custom code into services](https://github.com/getferdi/ferdi/wiki/Using-user.css-and-user.js) ([#83](https://github.com/getferdi/ferdi/issues/83))
115- [x] Allow SVGs for service custom icon 116- [x] Allow SVGs for service custom icon
116- [x] Switch to [`electron-spellchecker`](https://github.com/electron-userland/electron-spellchecker) to improve application size 117- [x] Switch to [`electron-spellchecker`](https://github.com/electron-userland/electron-spellchecker) to improve application size
117- [x] Improve "About Ferdi" screen to better display versions 118- [x] Improve "About Ferdi" screen to better display versions
diff --git a/src/actions/service.js b/src/actions/service.js
index d32bb80d3..f0c42e8aa 100644
--- a/src/actions/service.js
+++ b/src/actions/service.js
@@ -28,8 +28,9 @@ export default {
28 serviceId: PropTypes.string.isRequired, 28 serviceId: PropTypes.string.isRequired,
29 redirect: PropTypes.string, 29 redirect: PropTypes.string,
30 }, 30 },
31 openDarkmodeCss: { 31 openRecipeFile: {
32 recipe: PropTypes.string.isRequired, 32 recipe: PropTypes.string.isRequired,
33 file: PropTypes.string.isRequired,
33 }, 34 },
34 clearCache: { 35 clearCache: {
35 serviceId: PropTypes.string.isRequired, 36 serviceId: PropTypes.string.isRequired,
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 89c82c7f8..98051d78f 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -33,6 +33,18 @@ const messages = defineMessages({
33 id: 'settings.service.form.openDarkmodeCss', 33 id: 'settings.service.form.openDarkmodeCss',
34 defaultMessage: '!!!Open darkmode.css', 34 defaultMessage: '!!!Open darkmode.css',
35 }, 35 },
36 openUserCss: {
37 id: 'settings.service.form.openUserCss',
38 defaultMessage: '!!!Open user.css',
39 },
40 openUserJs: {
41 id: 'settings.service.form.openUserJs',
42 defaultMessage: '!!!Open user.js',
43 },
44 recipeFileInfo: {
45 id: 'settings.service.form.recipeFileInfo',
46 defaultMessage: '!!!Your user files will be inserted into the webpage so you can customize services in any way you like. User files are only stored locally and are not transferred to other computers using the same account.',
47 },
36 availableServices: { 48 availableServices: {
37 id: 'settings.service.form.availableServices', 49 id: 'settings.service.form.availableServices',
38 defaultMessage: '!!!Available services', 50 defaultMessage: '!!!Available services',
@@ -131,8 +143,7 @@ export default @observer class EditServiceForm extends Component {
131 form: PropTypes.instanceOf(Form).isRequired, 143 form: PropTypes.instanceOf(Form).isRequired,
132 onSubmit: PropTypes.func.isRequired, 144 onSubmit: PropTypes.func.isRequired,
133 onDelete: PropTypes.func.isRequired, 145 onDelete: PropTypes.func.isRequired,
134 openDarkmodeCss: PropTypes.func.isRequired, 146 openRecipeFile: PropTypes.func.isRequired,
135 isOpeningDarkModeCss: PropTypes.bool.isRequired,
136 isSaving: PropTypes.bool.isRequired, 147 isSaving: PropTypes.bool.isRequired,
137 isDeleting: PropTypes.bool.isRequired, 148 isDeleting: PropTypes.bool.isRequired,
138 isProxyFeatureEnabled: PropTypes.bool.isRequired, 149 isProxyFeatureEnabled: PropTypes.bool.isRequired,
@@ -199,8 +210,7 @@ export default @observer class EditServiceForm extends Component {
199 isSaving, 210 isSaving,
200 isDeleting, 211 isDeleting,
201 onDelete, 212 onDelete,
202 openDarkmodeCss, 213 openRecipeFile,
203 isOpeningDarkModeCss,
204 isProxyFeatureEnabled, 214 isProxyFeatureEnabled,
205 isServiceProxyIncludedInCurrentPlan, 215 isServiceProxyIncludedInCurrentPlan,
206 isSpellcheckerIncludedInCurrentPlan, 216 isSpellcheckerIncludedInCurrentPlan,
@@ -226,23 +236,6 @@ export default @observer class EditServiceForm extends Component {
226 /> 236 />
227 ); 237 );
228 238
229 const openDarkmodeCssButton = isOpeningDarkModeCss ? (
230 <Button
231 label={intl.formatMessage(messages.openDarkmodeCss)}
232 loaded={false}
233 buttonType="secondary"
234 className="settings__open-dark-mode-button"
235 disabled
236 />
237 ) : (
238 <Button
239 buttonType="secondary"
240 label={intl.formatMessage(messages.openDarkmodeCss)}
241 className="settings__open-dark-mode-button"
242 onClick={openDarkmodeCss}
243 />
244 );
245
246 let activeTabIndex = 0; 239 let activeTabIndex = 0;
247 if (recipe.hasHostedOption && service.team) { 240 if (recipe.hasHostedOption && service.team) {
248 activeTabIndex = 1; 241 activeTabIndex = 1;
@@ -430,11 +423,39 @@ export default @observer class EditServiceForm extends Component {
430 </PremiumFeatureContainer> 423 </PremiumFeatureContainer>
431 )} 424 )}
432 </form> 425 </form>
426
427 {action === 'edit' && (
428 <>
429 <div className="settings__open-recipe-file-container">
430 <Button
431 buttonType="secondary"
432 label={intl.formatMessage(messages.openDarkmodeCss)}
433 className="settings__open-recipe-file-button"
434 onClick={() => openRecipeFile('darkmode.css')}
435 />
436 <Button
437 buttonType="secondary"
438 label={intl.formatMessage(messages.openUserCss)}
439 className="settings__open-recipe-file-button"
440 onClick={() => openRecipeFile('user.css')}
441 />
442 <Button
443 buttonType="secondary"
444 label={intl.formatMessage(messages.openUserJs)}
445 className="settings__open-recipe-file-button"
446 onClick={() => openRecipeFile('user.js')}
447 />
448 </div>
449 <p style={{ marginTop: 10 }}>
450 <span className="mdi mdi-information" />
451 {intl.formatMessage(messages.recipeFileInfo)}
452 </p>
453 </>
454 )}
433 </div> 455 </div>
434 <div className="settings__controls"> 456 <div className="settings__controls">
435 {/* Delete Button */} 457 {/* Delete Button */}
436 {action === 'edit' && deleteButton} 458 {action === 'edit' && deleteButton}
437 {action === 'edit' && openDarkmodeCssButton}
438 459
439 {/* Save Button */} 460 {/* Save Button */}
440 {isSaving || isValidatingCustomUrl ? ( 461 {isSaving || isValidatingCustomUrl ? (
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index 99c40b086..a7d33a3ea 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -92,10 +92,6 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
92 intl: intlShape, 92 intl: intlShape,
93 }; 93 };
94 94
95 state = {
96 isOpeningDarkModeCss: false,
97 }
98
99 onSubmit(data) { 95 onSubmit(data) {
100 const { action } = this.props.router.params; 96 const { action } = this.props.router.params;
101 const { recipes, services } = this.props.stores; 97 const { recipes, services } = this.props.stores;
@@ -282,25 +278,16 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
282 } 278 }
283 } 279 }
284 280
285 openDarkmodeCss() { 281 openRecipeFile(file) {
286 const { openDarkmodeCss } = this.props.actions.service; 282 const { openRecipeFile } = this.props.actions.service;
287 const { action } = this.props.router.params; 283 const { action } = this.props.router.params;
288 284
289 if (action === 'edit') { 285 if (action === 'edit') {
290 this.setState({
291 isOpeningDarkModeCss: true,
292 });
293
294 const { activeSettings: service } = this.props.stores.services; 286 const { activeSettings: service } = this.props.stores.services;
295 openDarkmodeCss({ 287 openRecipeFile({
296 recipe: service.recipe.id, 288 recipe: service.recipe.id,
289 file,
297 }); 290 });
298
299 setTimeout(() => {
300 this.setState({
301 isOpeningDarkModeCss: false,
302 });
303 }, 2500);
304 } 291 }
305 } 292 }
306 293
@@ -355,8 +342,7 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
355 isDeleting={services.deleteServiceRequest.isExecuting} 342 isDeleting={services.deleteServiceRequest.isExecuting}
356 onSubmit={d => this.onSubmit(d)} 343 onSubmit={d => this.onSubmit(d)}
357 onDelete={() => this.deleteService()} 344 onDelete={() => this.deleteService()}
358 openDarkmodeCss={() => this.openDarkmodeCss()} 345 openRecipeFile={file => this.openRecipeFile(file)}
359 isOpeningDarkModeCss={this.state.isOpeningDarkModeCss}
360 isProxyFeatureEnabled={proxyFeature.isEnabled} 346 isProxyFeatureEnabled={proxyFeature.isEnabled}
361 isServiceProxyIncludedInCurrentPlan={proxyFeature.isIncludedInCurrentPlan} 347 isServiceProxyIncludedInCurrentPlan={proxyFeature.isIncludedInCurrentPlan}
362 isSpellcheckerIncludedInCurrentPlan={spellcheckerFeature.isIncludedInCurrentPlan} 348 isSpellcheckerIncludedInCurrentPlan={spellcheckerFeature.isIncludedInCurrentPlan}
@@ -384,7 +370,7 @@ EditServiceScreen.wrappedComponent.propTypes = {
384 createService: PropTypes.func.isRequired, 370 createService: PropTypes.func.isRequired,
385 updateService: PropTypes.func.isRequired, 371 updateService: PropTypes.func.isRequired,
386 deleteService: PropTypes.func.isRequired, 372 deleteService: PropTypes.func.isRequired,
387 openDarkmodeCss: PropTypes.func.isRequired, 373 openRecipeFile: PropTypes.func.isRequired,
388 }).isRequired, 374 }).isRequired,
389 // settings: PropTypes.shape({ 375 // settings: PropTypes.shape({
390 // update: PropTypes.func.isRequred, 376 // update: PropTypes.func.isRequred,
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json
index 44411cdad..0ea45b977 100644
--- a/src/i18n/locales/defaultMessages.json
+++ b/src/i18n/locales/defaultMessages.json
@@ -2143,263 +2143,302 @@
2143 } 2143 }
2144 }, 2144 },
2145 { 2145 {
2146 "defaultMessage": "!!!Available services", 2146 "defaultMessage": "!!!Open user.css",
2147 "end": { 2147 "end": {
2148 "column": 3, 2148 "column": 3,
2149 "line": 39 2149 "line": 39
2150 }, 2150 },
2151 "file": "src/components/settings/services/EditServiceForm.js", 2151 "file": "src/components/settings/services/EditServiceForm.js",
2152 "id": "settings.service.form.openUserCss",
2153 "start": {
2154 "column": 15,
2155 "line": 36
2156 }
2157 },
2158 {
2159 "defaultMessage": "!!!Open user.js",
2160 "end": {
2161 "column": 3,
2162 "line": 43
2163 },
2164 "file": "src/components/settings/services/EditServiceForm.js",
2165 "id": "settings.service.form.openUserJs",
2166 "start": {
2167 "column": 14,
2168 "line": 40
2169 }
2170 },
2171 {
2172 "defaultMessage": "!!!Your user files will be inserted into the webpage so you can customize services in any way you like. User files are only stored locally and are not transferred to other computers using the same account.",
2173 "end": {
2174 "column": 3,
2175 "line": 47
2176 },
2177 "file": "src/components/settings/services/EditServiceForm.js",
2178 "id": "settings.service.form.recipeFileInfo",
2179 "start": {
2180 "column": 18,
2181 "line": 44
2182 }
2183 },
2184 {
2185 "defaultMessage": "!!!Available services",
2186 "end": {
2187 "column": 3,
2188 "line": 51
2189 },
2190 "file": "src/components/settings/services/EditServiceForm.js",
2152 "id": "settings.service.form.availableServices", 2191 "id": "settings.service.form.availableServices",
2153 "start": { 2192 "start": {
2154 "column": 21, 2193 "column": 21,
2155 "line": 36 2194 "line": 48
2156 } 2195 }
2157 }, 2196 },
2158 { 2197 {
2159 "defaultMessage": "!!!Your services", 2198 "defaultMessage": "!!!Your services",
2160 "end": { 2199 "end": {
2161 "column": 3, 2200 "column": 3,
2162 "line": 43 2201 "line": 55
2163 }, 2202 },
2164 "file": "src/components/settings/services/EditServiceForm.js", 2203 "file": "src/components/settings/services/EditServiceForm.js",
2165 "id": "settings.service.form.yourServices", 2204 "id": "settings.service.form.yourServices",
2166 "start": { 2205 "start": {
2167 "column": 16, 2206 "column": 16,
2168 "line": 40 2207 "line": 52
2169 } 2208 }
2170 }, 2209 },
2171 { 2210 {
2172 "defaultMessage": "!!!Add {name}", 2211 "defaultMessage": "!!!Add {name}",
2173 "end": { 2212 "end": {
2174 "column": 3, 2213 "column": 3,
2175 "line": 47 2214 "line": 59
2176 }, 2215 },
2177 "file": "src/components/settings/services/EditServiceForm.js", 2216 "file": "src/components/settings/services/EditServiceForm.js",
2178 "id": "settings.service.form.addServiceHeadline", 2217 "id": "settings.service.form.addServiceHeadline",
2179 "start": { 2218 "start": {
2180 "column": 22, 2219 "column": 22,
2181 "line": 44 2220 "line": 56
2182 } 2221 }
2183 }, 2222 },
2184 { 2223 {
2185 "defaultMessage": "!!!Edit {name}", 2224 "defaultMessage": "!!!Edit {name}",
2186 "end": { 2225 "end": {
2187 "column": 3, 2226 "column": 3,
2188 "line": 51 2227 "line": 63
2189 }, 2228 },
2190 "file": "src/components/settings/services/EditServiceForm.js", 2229 "file": "src/components/settings/services/EditServiceForm.js",
2191 "id": "settings.service.form.editServiceHeadline", 2230 "id": "settings.service.form.editServiceHeadline",
2192 "start": { 2231 "start": {
2193 "column": 23, 2232 "column": 23,
2194 "line": 48 2233 "line": 60
2195 } 2234 }
2196 }, 2235 },
2197 { 2236 {
2198 "defaultMessage": "!!!Hosted", 2237 "defaultMessage": "!!!Hosted",
2199 "end": { 2238 "end": {
2200 "column": 3, 2239 "column": 3,
2201 "line": 55 2240 "line": 67
2202 }, 2241 },
2203 "file": "src/components/settings/services/EditServiceForm.js", 2242 "file": "src/components/settings/services/EditServiceForm.js",
2204 "id": "settings.service.form.tabHosted", 2243 "id": "settings.service.form.tabHosted",
2205 "start": { 2244 "start": {
2206 "column": 13, 2245 "column": 13,
2207 "line": 52 2246 "line": 64
2208 } 2247 }
2209 }, 2248 },
2210 { 2249 {
2211 "defaultMessage": "!!!Self hosted ⭐️", 2250 "defaultMessage": "!!!Self hosted ⭐️",
2212 "end": { 2251 "end": {
2213 "column": 3, 2252 "column": 3,
2214 "line": 59 2253 "line": 71
2215 }, 2254 },
2216 "file": "src/components/settings/services/EditServiceForm.js", 2255 "file": "src/components/settings/services/EditServiceForm.js",
2217 "id": "settings.service.form.tabOnPremise", 2256 "id": "settings.service.form.tabOnPremise",
2218 "start": { 2257 "start": {
2219 "column": 16, 2258 "column": 16,
2220 "line": 56 2259 "line": 68
2221 } 2260 }
2222 }, 2261 },
2223 { 2262 {
2224 "defaultMessage": "!!!Use the hosted {name} service.", 2263 "defaultMessage": "!!!Use the hosted {name} service.",
2225 "end": { 2264 "end": {
2226 "column": 3, 2265 "column": 3,
2227 "line": 63 2266 "line": 75
2228 }, 2267 },
2229 "file": "src/components/settings/services/EditServiceForm.js", 2268 "file": "src/components/settings/services/EditServiceForm.js",
2230 "id": "settings.service.form.useHostedService", 2269 "id": "settings.service.form.useHostedService",
2231 "start": { 2270 "start": {
2232 "column": 20, 2271 "column": 20,
2233 "line": 60 2272 "line": 72
2234 } 2273 }
2235 }, 2274 },
2236 { 2275 {
2237 "defaultMessage": "!!!Could not validate custom {name} server.", 2276 "defaultMessage": "!!!Could not validate custom {name} server.",
2238 "end": { 2277 "end": {
2239 "column": 3, 2278 "column": 3,
2240 "line": 67 2279 "line": 79
2241 }, 2280 },
2242 "file": "src/components/settings/services/EditServiceForm.js", 2281 "file": "src/components/settings/services/EditServiceForm.js",
2243 "id": "settings.service.form.customUrlValidationError", 2282 "id": "settings.service.form.customUrlValidationError",
2244 "start": { 2283 "start": {
2245 "column": 28, 2284 "column": 28,
2246 "line": 64 2285 "line": 76
2247 } 2286 }
2248 }, 2287 },
2249 { 2288 {
2250 "defaultMessage": "!!!To add self hosted services, you need a Ferdi Premium Supporter Account.", 2289 "defaultMessage": "!!!To add self hosted services, you need a Ferdi Premium Supporter Account.",
2251 "end": { 2290 "end": {
2252 "column": 3, 2291 "column": 3,
2253 "line": 71 2292 "line": 83
2254 }, 2293 },
2255 "file": "src/components/settings/services/EditServiceForm.js", 2294 "file": "src/components/settings/services/EditServiceForm.js",
2256 "id": "settings.service.form.customUrlPremiumInfo", 2295 "id": "settings.service.form.customUrlPremiumInfo",
2257 "start": { 2296 "start": {
2258 "column": 24, 2297 "column": 24,
2259 "line": 68 2298 "line": 80
2260 } 2299 }
2261 }, 2300 },
2262 { 2301 {
2263 "defaultMessage": "!!!Upgrade your account", 2302 "defaultMessage": "!!!Upgrade your account",
2264 "end": { 2303 "end": {
2265 "column": 3, 2304 "column": 3,
2266 "line": 75 2305 "line": 87
2267 }, 2306 },
2268 "file": "src/components/settings/services/EditServiceForm.js", 2307 "file": "src/components/settings/services/EditServiceForm.js",
2269 "id": "settings.service.form.customUrlUpgradeAccount", 2308 "id": "settings.service.form.customUrlUpgradeAccount",
2270 "start": { 2309 "start": {
2271 "column": 27, 2310 "column": 27,
2272 "line": 72 2311 "line": 84
2273 } 2312 }
2274 }, 2313 },
2275 { 2314 {
2276 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...", 2315 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...",
2277 "end": { 2316 "end": {
2278 "column": 3, 2317 "column": 3,
2279 "line": 79 2318 "line": 91
2280 }, 2319 },
2281 "file": "src/components/settings/services/EditServiceForm.js", 2320 "file": "src/components/settings/services/EditServiceForm.js",
2282 "id": "settings.service.form.indirectMessageInfo", 2321 "id": "settings.service.form.indirectMessageInfo",
2283 "start": { 2322 "start": {
2284 "column": 23, 2323 "column": 23,
2285 "line": 76 2324 "line": 88
2286 } 2325 }
2287 }, 2326 },
2288 { 2327 {
2289 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted", 2328 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted",
2290 "end": { 2329 "end": {
2291 "column": 3, 2330 "column": 3,
2292 "line": 83 2331 "line": 95
2293 }, 2332 },
2294 "file": "src/components/settings/services/EditServiceForm.js", 2333 "file": "src/components/settings/services/EditServiceForm.js",
2295 "id": "settings.service.form.isMutedInfo", 2334 "id": "settings.service.form.isMutedInfo",
2296 "start": { 2335 "start": {
2297 "column": 15, 2336 "column": 15,
2298 "line": 80 2337 "line": 92
2299 } 2338 }
2300 }, 2339 },
2301 { 2340 {
2302 "defaultMessage": "!!!Notifications", 2341 "defaultMessage": "!!!Notifications",
2303 "end": { 2342 "end": {
2304 "column": 3, 2343 "column": 3,
2305 "line": 87 2344 "line": 99
2306 }, 2345 },
2307 "file": "src/components/settings/services/EditServiceForm.js", 2346 "file": "src/components/settings/services/EditServiceForm.js",
2308 "id": "settings.service.form.headlineNotifications", 2347 "id": "settings.service.form.headlineNotifications",
2309 "start": { 2348 "start": {
2310 "column": 25, 2349 "column": 25,
2311 "line": 84 2350 "line": 96
2312 } 2351 }
2313 }, 2352 },
2314 { 2353 {
2315 "defaultMessage": "!!!Unread message badges", 2354 "defaultMessage": "!!!Unread message badges",
2316 "end": { 2355 "end": {
2317 "column": 3, 2356 "column": 3,
2318 "line": 91 2357 "line": 103
2319 }, 2358 },
2320 "file": "src/components/settings/services/EditServiceForm.js", 2359 "file": "src/components/settings/services/EditServiceForm.js",
2321 "id": "settings.service.form.headlineBadges", 2360 "id": "settings.service.form.headlineBadges",
2322 "start": { 2361 "start": {
2323 "column": 18, 2362 "column": 18,
2324 "line": 88 2363 "line": 100
2325 } 2364 }
2326 }, 2365 },
2327 { 2366 {
2328 "defaultMessage": "!!!General", 2367 "defaultMessage": "!!!General",
2329 "end": { 2368 "end": {
2330 "column": 3, 2369 "column": 3,
2331 "line": 95 2370 "line": 107
2332 }, 2371 },
2333 "file": "src/components/settings/services/EditServiceForm.js", 2372 "file": "src/components/settings/services/EditServiceForm.js",
2334 "id": "settings.service.form.headlineGeneral", 2373 "id": "settings.service.form.headlineGeneral",
2335 "start": { 2374 "start": {
2336 "column": 19, 2375 "column": 19,
2337 "line": 92 2376 "line": 104
2338 } 2377 }
2339 }, 2378 },
2340 { 2379 {
2341 "defaultMessage": "!!!Delete", 2380 "defaultMessage": "!!!Delete",
2342 "end": { 2381 "end": {
2343 "column": 3, 2382 "column": 3,
2344 "line": 99 2383 "line": 111
2345 }, 2384 },
2346 "file": "src/components/settings/services/EditServiceForm.js", 2385 "file": "src/components/settings/services/EditServiceForm.js",
2347 "id": "settings.service.form.iconDelete", 2386 "id": "settings.service.form.iconDelete",
2348 "start": { 2387 "start": {
2349 "column": 14, 2388 "column": 14,
2350 "line": 96 2389 "line": 108
2351 } 2390 }
2352 }, 2391 },
2353 { 2392 {
2354 "defaultMessage": "!!!Drop your image, or click here", 2393 "defaultMessage": "!!!Drop your image, or click here",
2355 "end": { 2394 "end": {
2356 "column": 3, 2395 "column": 3,
2357 "line": 103 2396 "line": 115
2358 }, 2397 },
2359 "file": "src/components/settings/services/EditServiceForm.js", 2398 "file": "src/components/settings/services/EditServiceForm.js",
2360 "id": "settings.service.form.iconUpload", 2399 "id": "settings.service.form.iconUpload",
2361 "start": { 2400 "start": {
2362 "column": 14, 2401 "column": 14,
2363 "line": 100 2402 "line": 112
2364 } 2403 }
2365 }, 2404 },
2366 { 2405 {
2367 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", 2406 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings",
2368 "end": { 2407 "end": {
2369 "column": 3, 2408 "column": 3,
2370 "line": 107 2409 "line": 119
2371 }, 2410 },
2372 "file": "src/components/settings/services/EditServiceForm.js", 2411 "file": "src/components/settings/services/EditServiceForm.js",
2373 "id": "settings.service.form.proxy.headline", 2412 "id": "settings.service.form.proxy.headline",
2374 "start": { 2413 "start": {
2375 "column": 17, 2414 "column": 17,
2376 "line": 104 2415 "line": 116
2377 } 2416 }
2378 }, 2417 },
2379 { 2418 {
2380 "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.", 2419 "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.",
2381 "end": { 2420 "end": {
2382 "column": 3, 2421 "column": 3,
2383 "line": 111 2422 "line": 123
2384 }, 2423 },
2385 "file": "src/components/settings/services/EditServiceForm.js", 2424 "file": "src/components/settings/services/EditServiceForm.js",
2386 "id": "settings.service.form.proxy.restartInfo", 2425 "id": "settings.service.form.proxy.restartInfo",
2387 "start": { 2426 "start": {
2388 "column": 20, 2427 "column": 20,
2389 "line": 108 2428 "line": 120
2390 } 2429 }
2391 }, 2430 },
2392 { 2431 {
2393 "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.", 2432 "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.",
2394 "end": { 2433 "end": {
2395 "column": 3, 2434 "column": 3,
2396 "line": 115 2435 "line": 127
2397 }, 2436 },
2398 "file": "src/components/settings/services/EditServiceForm.js", 2437 "file": "src/components/settings/services/EditServiceForm.js",
2399 "id": "settings.service.form.proxy.info", 2438 "id": "settings.service.form.proxy.info",
2400 "start": { 2439 "start": {
2401 "column": 13, 2440 "column": 13,
2402 "line": 112 2441 "line": 124
2403 } 2442 }
2404 } 2443 }
2405 ], 2444 ],
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 97be15fed..935b8550c 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -374,6 +374,8 @@
374 "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", 374 "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted",
375 "settings.service.form.name": "Name", 375 "settings.service.form.name": "Name",
376 "settings.service.form.openDarkmodeCss": "Open darkmode.css", 376 "settings.service.form.openDarkmodeCss": "Open darkmode.css",
377 "settings.service.form.openUserCss": "Open user.css",
378 "settings.service.form.openUserJs": "Open user.js",
377 "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings", 379 "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings",
378 "settings.service.form.proxy.host": "Proxy Host/IP", 380 "settings.service.form.proxy.host": "Proxy Host/IP",
379 "settings.service.form.proxy.info": "Proxy settings will not synced with the Ferdi servers.", 381 "settings.service.form.proxy.info": "Proxy settings will not synced with the Ferdi servers.",
@@ -382,6 +384,7 @@
382 "settings.service.form.proxy.port": "Port", 384 "settings.service.form.proxy.port": "Port",
383 "settings.service.form.proxy.restartInfo": "Please restart Ferdi after changing proxy Settings.", 385 "settings.service.form.proxy.restartInfo": "Please restart Ferdi after changing proxy Settings.",
384 "settings.service.form.proxy.user": "User (optional)", 386 "settings.service.form.proxy.user": "User (optional)",
387 "settings.service.form.recipeFileInfo": "Your user files will be inserted into the webpage so you can customize services in any way you like. User files are only stored locally and are not transferred to other computers using the same account.",
385 "settings.service.form.saveButton": "Save service", 388 "settings.service.form.saveButton": "Save service",
386 "settings.service.form.tabHosted": "Hosted", 389 "settings.service.form.tabHosted": "Hosted",
387 "settings.service.form.tabOnPremise": "Self hosted ⭐️", 390 "settings.service.form.tabOnPremise": "Self hosted ⭐️",
diff --git a/src/i18n/messages/src/components/settings/services/EditServiceForm.json b/src/i18n/messages/src/components/settings/services/EditServiceForm.json
index f4692ed29..df64c8a5f 100644
--- a/src/i18n/messages/src/components/settings/services/EditServiceForm.json
+++ b/src/i18n/messages/src/components/settings/services/EditServiceForm.json
@@ -39,15 +39,54 @@
39 } 39 }
40 }, 40 },
41 { 41 {
42 "id": "settings.service.form.openUserCss",
43 "defaultMessage": "!!!Open user.css",
44 "file": "src/components/settings/services/EditServiceForm.js",
45 "start": {
46 "line": 36,
47 "column": 15
48 },
49 "end": {
50 "line": 39,
51 "column": 3
52 }
53 },
54 {
55 "id": "settings.service.form.openUserJs",
56 "defaultMessage": "!!!Open user.js",
57 "file": "src/components/settings/services/EditServiceForm.js",
58 "start": {
59 "line": 40,
60 "column": 14
61 },
62 "end": {
63 "line": 43,
64 "column": 3
65 }
66 },
67 {
68 "id": "settings.service.form.recipeFileInfo",
69 "defaultMessage": "!!!Your user files will be inserted into the webpage so you can customize services in any way you like. User files are only stored locally and are not transferred to other computers using the same account.",
70 "file": "src/components/settings/services/EditServiceForm.js",
71 "start": {
72 "line": 44,
73 "column": 18
74 },
75 "end": {
76 "line": 47,
77 "column": 3
78 }
79 },
80 {
42 "id": "settings.service.form.availableServices", 81 "id": "settings.service.form.availableServices",
43 "defaultMessage": "!!!Available services", 82 "defaultMessage": "!!!Available services",
44 "file": "src/components/settings/services/EditServiceForm.js", 83 "file": "src/components/settings/services/EditServiceForm.js",
45 "start": { 84 "start": {
46 "line": 36, 85 "line": 48,
47 "column": 21 86 "column": 21
48 }, 87 },
49 "end": { 88 "end": {
50 "line": 39, 89 "line": 51,
51 "column": 3 90 "column": 3
52 } 91 }
53 }, 92 },
@@ -56,11 +95,11 @@
56 "defaultMessage": "!!!Your services", 95 "defaultMessage": "!!!Your services",
57 "file": "src/components/settings/services/EditServiceForm.js", 96 "file": "src/components/settings/services/EditServiceForm.js",
58 "start": { 97 "start": {
59 "line": 40, 98 "line": 52,
60 "column": 16 99 "column": 16
61 }, 100 },
62 "end": { 101 "end": {
63 "line": 43, 102 "line": 55,
64 "column": 3 103 "column": 3
65 } 104 }
66 }, 105 },
@@ -69,11 +108,11 @@
69 "defaultMessage": "!!!Add {name}", 108 "defaultMessage": "!!!Add {name}",
70 "file": "src/components/settings/services/EditServiceForm.js", 109 "file": "src/components/settings/services/EditServiceForm.js",
71 "start": { 110 "start": {
72 "line": 44, 111 "line": 56,
73 "column": 22 112 "column": 22
74 }, 113 },
75 "end": { 114 "end": {
76 "line": 47, 115 "line": 59,
77 "column": 3 116 "column": 3
78 } 117 }
79 }, 118 },
@@ -82,11 +121,11 @@
82 "defaultMessage": "!!!Edit {name}", 121 "defaultMessage": "!!!Edit {name}",
83 "file": "src/components/settings/services/EditServiceForm.js", 122 "file": "src/components/settings/services/EditServiceForm.js",
84 "start": { 123 "start": {
85 "line": 48, 124 "line": 60,
86 "column": 23 125 "column": 23
87 }, 126 },
88 "end": { 127 "end": {
89 "line": 51, 128 "line": 63,
90 "column": 3 129 "column": 3
91 } 130 }
92 }, 131 },
@@ -95,11 +134,11 @@
95 "defaultMessage": "!!!Hosted", 134 "defaultMessage": "!!!Hosted",
96 "file": "src/components/settings/services/EditServiceForm.js", 135 "file": "src/components/settings/services/EditServiceForm.js",
97 "start": { 136 "start": {
98 "line": 52, 137 "line": 64,
99 "column": 13 138 "column": 13
100 }, 139 },
101 "end": { 140 "end": {
102 "line": 55, 141 "line": 67,
103 "column": 3 142 "column": 3
104 } 143 }
105 }, 144 },
@@ -108,11 +147,11 @@
108 "defaultMessage": "!!!Self hosted ⭐️", 147 "defaultMessage": "!!!Self hosted ⭐️",
109 "file": "src/components/settings/services/EditServiceForm.js", 148 "file": "src/components/settings/services/EditServiceForm.js",
110 "start": { 149 "start": {
111 "line": 56, 150 "line": 68,
112 "column": 16 151 "column": 16
113 }, 152 },
114 "end": { 153 "end": {
115 "line": 59, 154 "line": 71,
116 "column": 3 155 "column": 3
117 } 156 }
118 }, 157 },
@@ -121,11 +160,11 @@
121 "defaultMessage": "!!!Use the hosted {name} service.", 160 "defaultMessage": "!!!Use the hosted {name} service.",
122 "file": "src/components/settings/services/EditServiceForm.js", 161 "file": "src/components/settings/services/EditServiceForm.js",
123 "start": { 162 "start": {
124 "line": 60, 163 "line": 72,
125 "column": 20 164 "column": 20
126 }, 165 },
127 "end": { 166 "end": {
128 "line": 63, 167 "line": 75,
129 "column": 3 168 "column": 3
130 } 169 }
131 }, 170 },
@@ -134,11 +173,11 @@
134 "defaultMessage": "!!!Could not validate custom {name} server.", 173 "defaultMessage": "!!!Could not validate custom {name} server.",
135 "file": "src/components/settings/services/EditServiceForm.js", 174 "file": "src/components/settings/services/EditServiceForm.js",
136 "start": { 175 "start": {
137 "line": 64, 176 "line": 76,
138 "column": 28 177 "column": 28
139 }, 178 },
140 "end": { 179 "end": {
141 "line": 67, 180 "line": 79,
142 "column": 3 181 "column": 3
143 } 182 }
144 }, 183 },
@@ -147,11 +186,11 @@
147 "defaultMessage": "!!!To add self hosted services, you need a Ferdi Premium Supporter Account.", 186 "defaultMessage": "!!!To add self hosted services, you need a Ferdi Premium Supporter Account.",
148 "file": "src/components/settings/services/EditServiceForm.js", 187 "file": "src/components/settings/services/EditServiceForm.js",
149 "start": { 188 "start": {
150 "line": 68, 189 "line": 80,
151 "column": 24 190 "column": 24
152 }, 191 },
153 "end": { 192 "end": {
154 "line": 71, 193 "line": 83,
155 "column": 3 194 "column": 3
156 } 195 }
157 }, 196 },
@@ -160,11 +199,11 @@
160 "defaultMessage": "!!!Upgrade your account", 199 "defaultMessage": "!!!Upgrade your account",
161 "file": "src/components/settings/services/EditServiceForm.js", 200 "file": "src/components/settings/services/EditServiceForm.js",
162 "start": { 201 "start": {
163 "line": 72, 202 "line": 84,
164 "column": 27 203 "column": 27
165 }, 204 },
166 "end": { 205 "end": {
167 "line": 75, 206 "line": 87,
168 "column": 3 207 "column": 3
169 } 208 }
170 }, 209 },
@@ -173,11 +212,11 @@
173 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...", 212 "defaultMessage": "!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...",
174 "file": "src/components/settings/services/EditServiceForm.js", 213 "file": "src/components/settings/services/EditServiceForm.js",
175 "start": { 214 "start": {
176 "line": 76, 215 "line": 88,
177 "column": 23 216 "column": 23
178 }, 217 },
179 "end": { 218 "end": {
180 "line": 79, 219 "line": 91,
181 "column": 3 220 "column": 3
182 } 221 }
183 }, 222 },
@@ -186,11 +225,11 @@
186 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted", 225 "defaultMessage": "!!!When disabled, all notification sounds and audio playback are muted",
187 "file": "src/components/settings/services/EditServiceForm.js", 226 "file": "src/components/settings/services/EditServiceForm.js",
188 "start": { 227 "start": {
189 "line": 80, 228 "line": 92,
190 "column": 15 229 "column": 15
191 }, 230 },
192 "end": { 231 "end": {
193 "line": 83, 232 "line": 95,
194 "column": 3 233 "column": 3
195 } 234 }
196 }, 235 },
@@ -199,11 +238,11 @@
199 "defaultMessage": "!!!Notifications", 238 "defaultMessage": "!!!Notifications",
200 "file": "src/components/settings/services/EditServiceForm.js", 239 "file": "src/components/settings/services/EditServiceForm.js",
201 "start": { 240 "start": {
202 "line": 84, 241 "line": 96,
203 "column": 25 242 "column": 25
204 }, 243 },
205 "end": { 244 "end": {
206 "line": 87, 245 "line": 99,
207 "column": 3 246 "column": 3
208 } 247 }
209 }, 248 },
@@ -212,11 +251,11 @@
212 "defaultMessage": "!!!Unread message badges", 251 "defaultMessage": "!!!Unread message badges",
213 "file": "src/components/settings/services/EditServiceForm.js", 252 "file": "src/components/settings/services/EditServiceForm.js",
214 "start": { 253 "start": {
215 "line": 88, 254 "line": 100,
216 "column": 18 255 "column": 18
217 }, 256 },
218 "end": { 257 "end": {
219 "line": 91, 258 "line": 103,
220 "column": 3 259 "column": 3
221 } 260 }
222 }, 261 },
@@ -225,11 +264,11 @@
225 "defaultMessage": "!!!General", 264 "defaultMessage": "!!!General",
226 "file": "src/components/settings/services/EditServiceForm.js", 265 "file": "src/components/settings/services/EditServiceForm.js",
227 "start": { 266 "start": {
228 "line": 92, 267 "line": 104,
229 "column": 19 268 "column": 19
230 }, 269 },
231 "end": { 270 "end": {
232 "line": 95, 271 "line": 107,
233 "column": 3 272 "column": 3
234 } 273 }
235 }, 274 },
@@ -238,11 +277,11 @@
238 "defaultMessage": "!!!Delete", 277 "defaultMessage": "!!!Delete",
239 "file": "src/components/settings/services/EditServiceForm.js", 278 "file": "src/components/settings/services/EditServiceForm.js",
240 "start": { 279 "start": {
241 "line": 96, 280 "line": 108,
242 "column": 14 281 "column": 14
243 }, 282 },
244 "end": { 283 "end": {
245 "line": 99, 284 "line": 111,
246 "column": 3 285 "column": 3
247 } 286 }
248 }, 287 },
@@ -251,11 +290,11 @@
251 "defaultMessage": "!!!Drop your image, or click here", 290 "defaultMessage": "!!!Drop your image, or click here",
252 "file": "src/components/settings/services/EditServiceForm.js", 291 "file": "src/components/settings/services/EditServiceForm.js",
253 "start": { 292 "start": {
254 "line": 100, 293 "line": 112,
255 "column": 14 294 "column": 14
256 }, 295 },
257 "end": { 296 "end": {
258 "line": 103, 297 "line": 115,
259 "column": 3 298 "column": 3
260 } 299 }
261 }, 300 },
@@ -264,11 +303,11 @@
264 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", 303 "defaultMessage": "!!!HTTP/HTTPS Proxy Settings",
265 "file": "src/components/settings/services/EditServiceForm.js", 304 "file": "src/components/settings/services/EditServiceForm.js",
266 "start": { 305 "start": {
267 "line": 104, 306 "line": 116,
268 "column": 17 307 "column": 17
269 }, 308 },
270 "end": { 309 "end": {
271 "line": 107, 310 "line": 119,
272 "column": 3 311 "column": 3
273 } 312 }
274 }, 313 },
@@ -277,11 +316,11 @@
277 "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.", 316 "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.",
278 "file": "src/components/settings/services/EditServiceForm.js", 317 "file": "src/components/settings/services/EditServiceForm.js",
279 "start": { 318 "start": {
280 "line": 108, 319 "line": 120,
281 "column": 20 320 "column": 20
282 }, 321 },
283 "end": { 322 "end": {
284 "line": 111, 323 "line": 123,
285 "column": 3 324 "column": 3
286 } 325 }
287 }, 326 },
@@ -290,11 +329,11 @@
290 "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.", 329 "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.",
291 "file": "src/components/settings/services/EditServiceForm.js", 330 "file": "src/components/settings/services/EditServiceForm.js",
292 "start": { 331 "start": {
293 "line": 112, 332 "line": 124,
294 "column": 13 333 "column": 13
295 }, 334 },
296 "end": { 335 "end": {
297 "line": 115, 336 "line": 127,
298 "column": 3 337 "column": 3
299 } 338 }
300 } 339 }
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js
index e38b82f03..ae6ba11c5 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -58,7 +58,7 @@ export default class ServicesStore extends Store {
58 this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this)); 58 this.actions.service.createFromLegacyService.listen(this._createFromLegacyService.bind(this));
59 this.actions.service.updateService.listen(this._updateService.bind(this)); 59 this.actions.service.updateService.listen(this._updateService.bind(this));
60 this.actions.service.deleteService.listen(this._deleteService.bind(this)); 60 this.actions.service.deleteService.listen(this._deleteService.bind(this));
61 this.actions.service.openDarkmodeCss.listen(this._openDarkmodeCss.bind(this)); 61 this.actions.service.openRecipeFile.listen(this._openRecipeFile.bind(this));
62 this.actions.service.clearCache.listen(this._clearCache.bind(this)); 62 this.actions.service.clearCache.listen(this._clearCache.bind(this));
63 this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this)); 63 this.actions.service.setWebviewReference.listen(this._setWebviewReference.bind(this));
64 this.actions.service.detachService.listen(this._detachService.bind(this)); 64 this.actions.service.detachService.listen(this._detachService.bind(this));
@@ -334,7 +334,7 @@ export default class ServicesStore extends Store {
334 this.actionStatus = request.result.status; 334 this.actionStatus = request.result.status;
335 } 335 }
336 336
337 @action async _openDarkmodeCss({ recipe }) { 337 @action async _openRecipeFile({ recipe, file }) {
338 // Get directory for recipe 338 // Get directory for recipe
339 const normalDirectory = getRecipeDirectory(recipe); 339 const normalDirectory = getRecipeDirectory(recipe);
340 const devDirectory = getDevRecipeDirectory(recipe); 340 const devDirectory = getDevRecipeDirectory(recipe);
@@ -349,10 +349,10 @@ export default class ServicesStore extends Store {
349 return; 349 return;
350 } 350 }
351 351
352 // Create and open darkmode.css 352 // Create and open file
353 const file = path.join(directory, 'darkmode.css'); 353 const filePath = path.join(directory, file);
354 await fs.ensureFile(file); 354 await fs.ensureFile(filePath);
355 shell.showItemInFolder(file); 355 shell.showItemInFolder(filePath);
356 } 356 }
357 357
358 @action async _clearCache({ serviceId }) { 358 @action async _clearCache({ serviceId }) {
diff --git a/src/styles/settings.scss b/src/styles/settings.scss
index 305450fd2..14cc91f87 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -306,7 +306,15 @@
306 } 306 }
307 307
308 .settings__delete-button { right: 0; } 308 .settings__delete-button { right: 0; }
309 .settings__open-dark-mode-button { right: 0; cursor:pointer; } 309 .settings__open-recipe-file-button {
310 cursor:pointer;
311 margin-right: 10px;
312 }
313 .settings__open-recipe-file-container {
314 margin-top: 20px;
315 display: flex;
316 height: auto !important;
317 }
310 318
311 .settings__empty-state { 319 .settings__empty-state {
312 align-items: center; 320 align-items: center;
diff --git a/src/webview/recipe.js b/src/webview/recipe.js
index e95cae18b..1a22542d8 100644
--- a/src/webview/recipe.js
+++ b/src/webview/recipe.js
@@ -99,6 +99,39 @@ class RecipeController {
99 } catch (err) { 99 } catch (err) {
100 console.error('Recipe initialization failed', err); 100 console.error('Recipe initialization failed', err);
101 } 101 }
102
103 this.loadUserFiles(recipe, config);
104 }
105
106 async loadUserFiles(recipe, config) {
107 const userCss = path.join(recipe.path, 'user.css');
108 if (await fs.exists(userCss)) {
109 const data = await fs.readFile(userCss);
110 const styles = document.createElement('style');
111 styles.innerHTML = data.toString();
112
113 document.querySelector('head').appendChild(styles);
114 }
115
116 const userJs = path.join(recipe.path, 'user.js');
117 if (await fs.exists(userJs)) {
118 const loadUserJs = () => {
119 // eslint-disable-next-line
120 const userJsModule = require(userJs);
121
122 if (typeof userJsModule === 'function') {
123 userJsModule(config);
124 }
125 };
126
127 if (document.readyState !== 'loading') {
128 loadUserJs();
129 } else {
130 document.addEventListener('DOMContentLoaded', () => {
131 loadUserJs();
132 });
133 }
134 }
102 } 135 }
103 136
104 update() { 137 update() {