aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Stefan Malzner <stefan@adlk.io>2018-01-04 09:56:12 +0100
committerLibravatar Stefan Malzner <stefan@adlk.io>2018-01-04 09:56:12 +0100
commitaa7cb9c84979adb58b4d6179467e6353cfb51d02 (patch)
tree6322d2207545464064e24f6bdd2f9724d1e4b813
parentReplace cache string with localized string (diff)
parentMerge branch 'develop' into service-cache-cleanup (diff)
downloadferdium-app-aa7cb9c84979adb58b4d6179467e6353cfb51d02.tar.gz
ferdium-app-aa7cb9c84979adb58b4d6179467e6353cfb51d02.tar.zst
ferdium-app-aa7cb9c84979adb58b4d6179467e6353cfb51d02.zip
Merge branch 'service-cache-cleanup' of https://github.com/dannyqiu/franz into feature/service-cache-cleanup
-rw-r--r--src/app.js2
-rw-r--r--src/components/settings/recipes/RecipesDashboard.js20
-rw-r--r--src/components/settings/services/ServicesDashboard.js42
-rw-r--r--src/components/ui/SearchInput.js48
-rw-r--r--src/containers/settings/ServicesScreen.js1
-rw-r--r--src/i18n/locales/en-US.json1
-rw-r--r--src/stores/AppStore.js29
-rw-r--r--src/stores/ServicesStore.js5
-rw-r--r--src/styles/searchInput.scss16
-rw-r--r--src/styles/settings.scss22
10 files changed, 114 insertions, 72 deletions
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
106window.addEventListener('dragover', event => event.preventDefault()); 106window.addEventListener('dragover', event => event.preventDefault());
107window.addEventListener('drop', event => event.preventDefault()); 107window.addEventListener('drop', event => event.preventDefault());
108window.addEventListener('dragover', event => event.stopPropagation());
109window.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/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/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';
9export default class SearchInput extends Component { 9export 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/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/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 6a8943f18..2bfbf2455 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",
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js
index 63130a3ba..e33f50f05 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.js
@@ -17,7 +17,8 @@ import Miner from '../lib/Miner';
17 17
18import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js'; 18import { getServiceIdsFromPartitions, removeServicePartitionDirectory } from '../helpers/service-helpers.js';
19 19
20const { app, powerMonitor } = remote; 20const { app } = remote;
21
21const defaultLocale = DEFAULT_APP_SETTINGS.locale; 22const defaultLocale = DEFAULT_APP_SETTINGS.locale;
22const autoLauncher = new AutoLaunch({ 23const autoLauncher = new AutoLaunch({
23 name: 'Franz', 24 name: 'Franz',
@@ -132,15 +133,23 @@ export default class AppStore extends Store {
132 this.stores.router.push(data.url); 133 this.stores.router.push(data.url);
133 }); 134 });
134 135
136 const TIMEOUT = 5000;
135 // Check system idle time every minute 137 // Check system idle time every minute
136 setInterval(() => { 138 setInterval(() => {
137 this.idleTime = idleTimer.getIdleTime(); 139 this.idleTime = idleTimer.getIdleTime();
138 }, 60000); 140 }, TIMEOUT);
139 141
140 // Reload all services after a healthy nap 142 // Reload all services after a healthy nap
141 powerMonitor.on('resume', () => { 143 // Alternative solution for powerMonitor as the resume event is not fired
142 setTimeout(window.location.reload, 5000); 144 // More information: https://github.com/electron/electron/issues/1615
143 }); 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);
144 153
145 // Set active the next service 154 // Set active the next service
146 key( 155 key(
@@ -386,6 +395,16 @@ export default class AppStore extends Store {
386 return autoLauncher.isEnabled() || false; 395 return autoLauncher.isEnabled() || false;
387 } 396 }
388 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
389 _systemDND() { 408 _systemDND() {
390 const dnd = getDoNotDisturb(); 409 const dnd = getDoNotDisturb();
391 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 237264bcc..87ee57a0d 100644
--- a/src/stores/ServicesStore.js
+++ b/src/stores/ServicesStore.js
@@ -377,7 +377,7 @@ export default class ServicesStore extends Store {
377 const service = this.one(serviceId); 377 const service = this.one(serviceId);
378 service.resetMessageCount(); 378 service.resetMessageCount();
379 379
380 service.webview.reload(); 380 service.webview.loadURL(service.url);
381 } 381 }
382 382
383 @action _reloadActive() { 383 @action _reloadActive() {
@@ -506,12 +506,13 @@ export default class ServicesStore extends Store {
506 .reduce((a, b) => a + b, 0); 506 .reduce((a, b) => a + b, 0);
507 507
508 const unreadIndirectMessageCount = this.allDisplayed 508 const unreadIndirectMessageCount = this.allDisplayed
509 .filter(s => (showMessageBadgeWhenMuted || s.isIndirectMessageBadgeEnabled) && showMessageBadgesEvenWhenMuted && s.isBadgeEnabled) 509 .filter(s => (showMessageBadgeWhenMuted && showMessageBadgesEvenWhenMuted) && (s.isBadgeEnabled && s.isIndirectMessageBadgeEnabled))
510 .map(s => s.unreadIndirectMessageCount) 510 .map(s => s.unreadIndirectMessageCount)
511 .reduce((a, b) => a + b, 0); 511 .reduce((a, b) => a + b, 0);
512 512
513 // We can't just block this earlier, otherwise the mobx reaction won't be aware of the vars to watch in some cases 513 // We can't just block this earlier, otherwise the mobx reaction won't be aware of the vars to watch in some cases
514 if (showMessageBadgesEvenWhenMuted) { 514 if (showMessageBadgesEvenWhenMuted) {
515 console.log('set badge', unreadDirectMessageCount, unreadIndirectMessageCount);
515 this.actions.app.setBadge({ 516 this.actions.app.setBadge({
516 unreadDirectMessageCount, 517 unreadDirectMessageCount,
517 unreadIndirectMessageCount, 518 unreadIndirectMessageCount,
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 b29ed5468..2da56c930 100644
--- a/src/styles/settings.scss
+++ b/src/styles/settings.scss
@@ -129,26 +129,8 @@
129 } 129 }
130 } 130 }
131 131
132 .settings__search-header { 132 .search-input {
133 display: flex; 133 margin-bottom: 30px;
134 align-items: center;
135 padding: 0 10px;
136 border-radius: $theme-border-radius;
137 transition: background $theme-transition-time;
138 @extend %headline;
139 font-size: 22px;
140
141 &:hover {
142 background: darken($theme-gray-lighter, 5%);
143 }
144
145 input {
146 padding-left: 10px;
147 background: none;
148 border: 0;
149 flex: 1;
150 @extend %headline;
151 }
152 } 134 }
153 135
154 &__options { 136 &__options {