aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Ricardo Cino <ricardo@cino.io>2022-06-22 00:32:18 +0200
committerLibravatar GitHub <noreply@github.com>2022-06-21 22:32:18 +0000
commit73ba955e344c8ccedd43235495ef8b72b5a2b6fd (patch)
tree03766ab32fefe7e83026a14393527f1dcbaed849 /src
parentdocs: add cino as a contributor for infra [skip ci] (#330) (diff)
downloadferdium-app-73ba955e344c8ccedd43235495ef8b72b5a2b6fd.tar.gz
ferdium-app-73ba955e344c8ccedd43235495ef8b72b5a2b6fd.tar.zst
ferdium-app-73ba955e344c8ccedd43235495ef8b72b5a2b6fd.zip
chore: Transform AppStore.js into Typescript (#329)
* turn actions into typescript * correct tsconfig * added TypedStore
Diffstat (limited to 'src')
-rw-r--r--src/I18n.tsx8
-rw-r--r--src/actions/app.ts3
-rw-r--r--src/actions/lib/actions.ts25
-rw-r--r--src/actions/recipe.ts3
-rw-r--r--src/actions/recipePreview.ts3
-rw-r--r--src/actions/requests.ts4
-rw-r--r--src/actions/service.ts3
-rw-r--r--src/actions/settings.ts3
-rw-r--r--src/actions/ui.ts3
-rw-r--r--src/actions/user.ts3
-rw-r--r--src/api/index.ts12
-rw-r--r--src/app.js4
-rw-r--r--src/config.ts8
-rw-r--r--src/electron-util.ts2
-rw-r--r--src/index.ts2
-rw-r--r--src/models/User.ts4
-rw-r--r--src/stores.types.ts44
-rw-r--r--src/stores/AppStore.ts (renamed from src/stores/AppStore.js)40
-rw-r--r--src/stores/index.ts13
-rw-r--r--src/stores/lib/Reaction.ts14
-rw-r--r--src/stores/lib/Store.js13
-rw-r--r--src/stores/lib/TypedStore.ts46
22 files changed, 197 insertions, 63 deletions
diff --git a/src/I18n.tsx b/src/I18n.tsx
index 0e63d1086..bf4b08cd1 100644
--- a/src/I18n.tsx
+++ b/src/I18n.tsx
@@ -10,7 +10,7 @@ const translations = generatedTranslations();
10 10
11type Props = { 11type Props = {
12 stores: { 12 stores: {
13 app: typeof AppStore; 13 app: AppStore;
14 user: typeof UserStore; 14 user: typeof UserStore;
15 }; 15 };
16 children: ReactNode; 16 children: ReactNode;
@@ -21,14 +21,16 @@ class I18N extends Component<Props> {
21 window['ferdium'].menu.rebuild(); 21 window['ferdium'].menu.rebuild();
22 } 22 }
23 23
24 render() { 24 render(): ReactNode {
25 const { stores, children } = this.props; 25 const { stores, children } = this.props;
26 const { locale } = stores.app; 26 const { locale } = stores.app;
27 return ( 27 return (
28 <IntlProvider 28 <IntlProvider
29 {...{ locale, key: locale, messages: translations[locale] }} 29 {...{ locale, key: locale, messages: translations[locale] }}
30 ref={intlProvider => { 30 ref={intlProvider => {
31 window['ferdium'].intl = intlProvider ? intlProvider.state.intl : null; 31 window['ferdium'].intl = intlProvider
32 ? intlProvider.state.intl
33 : null;
32 }} 34 }}
33 > 35 >
34 {children} 36 {children}
diff --git a/src/actions/app.ts b/src/actions/app.ts
index e6f7f22ba..a798a8b0b 100644
--- a/src/actions/app.ts
+++ b/src/actions/app.ts
@@ -1,6 +1,7 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import { ActionDefinitions } from './lib/actions';
2 3
3export default { 4export default <ActionDefinitions>{
4 setBadge: { 5 setBadge: {
5 unreadDirectMessageCount: PropTypes.number.isRequired, 6 unreadDirectMessageCount: PropTypes.number.isRequired,
6 unreadIndirectMessageCount: PropTypes.number, 7 unreadIndirectMessageCount: PropTypes.number,
diff --git a/src/actions/lib/actions.ts b/src/actions/lib/actions.ts
index 412a0d895..378cef574 100644
--- a/src/actions/lib/actions.ts
+++ b/src/actions/lib/actions.ts
@@ -1,4 +1,27 @@
1export const createActionsFromDefinitions = (actionDefinitions, validate) => { 1import PropTypes from 'prop-types';
2
3export interface ActionDefinitions {
4 [key: string]: {
5 [key: string]: PropTypes.InferType<any>;
6 };
7}
8
9export interface Actions {
10 [key: string]: {
11 [key: string]: {
12 (...args: any[]): void;
13 listeners: Array<Function>;
14 notify: (params: any) => void;
15 listen: (listener: (params: any) => void) => void;
16 off: (listener: (params: any) => void) => void;
17 };
18 };
19}
20
21export const createActionsFromDefinitions = (
22 actionDefinitions: ActionDefinitions,
23 validate: any,
24) => {
2 const actions = {}; 25 const actions = {};
3 // eslint-disable-next-line unicorn/no-array-for-each 26 // eslint-disable-next-line unicorn/no-array-for-each
4 Object.keys(actionDefinitions).forEach(actionName => { 27 Object.keys(actionDefinitions).forEach(actionName => {
diff --git a/src/actions/recipe.ts b/src/actions/recipe.ts
index 29b0a151f..0dd92737f 100644
--- a/src/actions/recipe.ts
+++ b/src/actions/recipe.ts
@@ -1,6 +1,7 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import { ActionDefinitions } from './lib/actions';
2 3
3export default { 4export default <ActionDefinitions>{
4 install: { 5 install: {
5 recipeId: PropTypes.string.isRequired, 6 recipeId: PropTypes.string.isRequired,
6 update: PropTypes.bool, 7 update: PropTypes.bool,
diff --git a/src/actions/recipePreview.ts b/src/actions/recipePreview.ts
index 36de3d844..053b363e9 100644
--- a/src/actions/recipePreview.ts
+++ b/src/actions/recipePreview.ts
@@ -1,6 +1,7 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import { ActionDefinitions } from './lib/actions';
2 3
3export default { 4export default <ActionDefinitions>{
4 search: { 5 search: {
5 needle: PropTypes.string.isRequired, 6 needle: PropTypes.string.isRequired,
6 }, 7 },
diff --git a/src/actions/requests.ts b/src/actions/requests.ts
index 89296e7ec..0b4c905ee 100644
--- a/src/actions/requests.ts
+++ b/src/actions/requests.ts
@@ -1,3 +1,5 @@
1export default { 1import { ActionDefinitions } from './lib/actions';
2
3export default <ActionDefinitions>{
2 retryRequiredRequests: {}, 4 retryRequiredRequests: {},
3}; 5};
diff --git a/src/actions/service.ts b/src/actions/service.ts
index aa02c860a..4b43fc2ca 100644
--- a/src/actions/service.ts
+++ b/src/actions/service.ts
@@ -1,7 +1,8 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import ServiceModel from '../models/Service'; 2import ServiceModel from '../models/Service';
3import { ActionDefinitions } from './lib/actions';
3 4
4export default { 5export default <ActionDefinitions>{
5 setActive: { 6 setActive: {
6 serviceId: PropTypes.string.isRequired, 7 serviceId: PropTypes.string.isRequired,
7 keepActiveRoute: PropTypes.bool, 8 keepActiveRoute: PropTypes.bool,
diff --git a/src/actions/settings.ts b/src/actions/settings.ts
index fd29b798b..4796f6a02 100644
--- a/src/actions/settings.ts
+++ b/src/actions/settings.ts
@@ -1,6 +1,7 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import { ActionDefinitions } from './lib/actions';
2 3
3export default { 4export default <ActionDefinitions>{
4 update: { 5 update: {
5 type: PropTypes.string.isRequired, 6 type: PropTypes.string.isRequired,
6 data: PropTypes.object.isRequired, 7 data: PropTypes.object.isRequired,
diff --git a/src/actions/ui.ts b/src/actions/ui.ts
index b913b430b..7d2dbccfa 100644
--- a/src/actions/ui.ts
+++ b/src/actions/ui.ts
@@ -1,6 +1,7 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import { ActionDefinitions } from './lib/actions';
2 3
3export default { 4export default <ActionDefinitions>{
4 openSettings: { 5 openSettings: {
5 path: PropTypes.string, 6 path: PropTypes.string,
6 }, 7 },
diff --git a/src/actions/user.ts b/src/actions/user.ts
index 15a9216bd..c0ede619e 100644
--- a/src/actions/user.ts
+++ b/src/actions/user.ts
@@ -1,6 +1,7 @@
1import PropTypes from 'prop-types'; 1import PropTypes from 'prop-types';
2import { ActionDefinitions } from './lib/actions';
2 3
3export default { 4export default <ActionDefinitions>{
4 login: { 5 login: {
5 email: PropTypes.string.isRequired, 6 email: PropTypes.string.isRequired,
6 password: PropTypes.string.isRequired, 7 password: PropTypes.string.isRequired,
diff --git a/src/api/index.ts b/src/api/index.ts
index 59fad194a..5ca6ba132 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -6,7 +6,17 @@ import UserApi from './UserApi';
6import LocalApi from './LocalApi'; 6import LocalApi from './LocalApi';
7import FeaturesApi from './FeaturesApi'; 7import FeaturesApi from './FeaturesApi';
8 8
9export default (server: any, local: any) => ({ 9export interface ApiInterface {
10 app: AppApi;
11 services: ServicesApi;
12 recipePreviews: RecipePreviewsApi;
13 recipes: RecipesApi;
14 features: FeaturesApi;
15 user: UserApi;
16 local: LocalApi;
17}
18
19export default (server: any, local: any): ApiInterface => ({
10 app: new AppApi(server), 20 app: new AppApi(server),
11 services: new ServicesApi(server, local), 21 services: new ServicesApi(server, local),
12 recipePreviews: new RecipePreviewsApi(server), 22 recipePreviews: new RecipePreviewsApi(server),
diff --git a/src/app.js b/src/app.js
index 54fba0c71..c92d044e6 100644
--- a/src/app.js
+++ b/src/app.js
@@ -53,8 +53,8 @@ window.addEventListener('load', () => {
53// TODO: send this request to the recipe.js 53// TODO: send this request to the recipe.js
54window.addEventListener('mouseup', e => { 54window.addEventListener('mouseup', e => {
55 if (e.button === 3 || e.button === 4) { 55 if (e.button === 3 || e.button === 4) {
56 e.preventDefault() 56 e.preventDefault();
57 e.stopPropagation() 57 e.stopPropagation();
58 } 58 }
59}); 59});
60 60
diff --git a/src/config.ts b/src/config.ts
index 62b4f4e68..d29480dc6 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -86,7 +86,7 @@ export const SEARCH_ENGINE_NAMES = {
86 86
87export const SEARCH_ENGINE_URLS = { 87export const SEARCH_ENGINE_URLS = {
88 [SEARCH_ENGINE_STARTPAGE]: ({ searchTerm }) => 88 [SEARCH_ENGINE_STARTPAGE]: ({ searchTerm }) =>
89 `https://www.startpage.com/sp/search?query=${searchTerm}`, 89 `https://www.startpage.com/sp/search?query=${searchTerm}`,
90 [SEARCH_ENGINE_GOOGLE]: ({ searchTerm }) => 90 [SEARCH_ENGINE_GOOGLE]: ({ searchTerm }) =>
91 `https://www.google.com/search?q=${searchTerm}`, 91 `https://www.google.com/search?q=${searchTerm}`,
92 [SEARCH_ENGINE_DDG]: ({ searchTerm }) => 92 [SEARCH_ENGINE_DDG]: ({ searchTerm }) =>
@@ -105,7 +105,7 @@ const TODO_RTM_URL = 'https://www.rememberthemilk.com/';
105const TODO_ANYDO_URL = 'https://desktop.any.do/'; 105const TODO_ANYDO_URL = 'https://desktop.any.do/';
106const TODO_GOOGLETASKS_URL = 106const TODO_GOOGLETASKS_URL =
107 'https://tasks.google.com/embed/?origin=https%3A%2F%2Fcalendar.google.com&fullWidth=1'; 107 'https://tasks.google.com/embed/?origin=https%3A%2F%2Fcalendar.google.com&fullWidth=1';
108const TODO_GOOGLEKEEP_URL = 'https://keep.google.com/' 108const TODO_GOOGLEKEEP_URL = 'https://keep.google.com/';
109 109
110export const TODO_SERVICE_RECIPE_IDS = { 110export const TODO_SERVICE_RECIPE_IDS = {
111 [TODO_TODOIST_URL]: 'todoist', 111 [TODO_TODOIST_URL]: 'todoist',
@@ -152,8 +152,8 @@ export const SIDEBAR_SERVICES_LOCATION_BOTTOMRIGHT = 2;
152export const SIDEBAR_SERVICES_LOCATION = { 152export const SIDEBAR_SERVICES_LOCATION = {
153 [SIDEBAR_SERVICES_LOCATION_TOPLEFT]: 'Top/Left', 153 [SIDEBAR_SERVICES_LOCATION_TOPLEFT]: 'Top/Left',
154 [SIDEBAR_SERVICES_LOCATION_CENTER]: 'Center', 154 [SIDEBAR_SERVICES_LOCATION_CENTER]: 'Center',
155 [SIDEBAR_SERVICES_LOCATION_BOTTOMRIGHT]: 'Bottom/Right' 155 [SIDEBAR_SERVICES_LOCATION_BOTTOMRIGHT]: 'Bottom/Right',
156} 156};
157 157
158export const ICON_SIZES = { 158export const ICON_SIZES = {
159 0: 'Very small icons', 159 0: 'Very small icons',
diff --git a/src/electron-util.ts b/src/electron-util.ts
index eede55786..4576de9a6 100644
--- a/src/electron-util.ts
+++ b/src/electron-util.ts
@@ -15,7 +15,7 @@ export const initializeRemote = () => {
15 15
16export const enableWebContents = (webContents: electron.WebContents) => { 16export const enableWebContents = (webContents: electron.WebContents) => {
17 enable(webContents); 17 enable(webContents);
18} 18};
19 19
20export const remote = new Proxy( 20export const remote = new Proxy(
21 {}, 21 {},
diff --git a/src/index.ts b/src/index.ts
index df044ab7e..fa957bf10 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -69,7 +69,7 @@ function onDidLoad(fn: {
69 (window: BrowserWindow): void; 69 (window: BrowserWindow): void;
70 (window: BrowserWindow): void; 70 (window: BrowserWindow): void;
71 (arg0: BrowserWindow): void; 71 (arg0: BrowserWindow): void;
72}) { 72}): void {
73 if (onDidLoadFns) { 73 if (onDidLoadFns) {
74 onDidLoadFns.push(fn); 74 onDidLoadFns.push(fn);
75 } else if (mainWindow) { 75 } else if (mainWindow) {
diff --git a/src/models/User.ts b/src/models/User.ts
index 8c8f3e490..14481fbb6 100644
--- a/src/models/User.ts
+++ b/src/models/User.ts
@@ -8,7 +8,7 @@ interface IUser {
8 organization: string | null; 8 organization: string | null;
9 accountType: string | null; 9 accountType: string | null;
10 beta: boolean; 10 beta: boolean;
11 locale: boolean; 11 locale: string;
12 isSubscriptionOwner: boolean; 12 isSubscriptionOwner: boolean;
13 team: object; 13 team: object;
14} 14}
@@ -37,7 +37,7 @@ export default class User {
37 37
38 @observable beta = false; 38 @observable beta = false;
39 39
40 @observable locale = false; 40 @observable locale: string | null = null;
41 41
42 @observable team = {}; 42 @observable team = {};
43 43
diff --git a/src/stores.types.ts b/src/stores.types.ts
index d09916653..14ae32133 100644
--- a/src/stores.types.ts
+++ b/src/stores.types.ts
@@ -1,3 +1,9 @@
1import Workspace from './features/workspaces/models/Workspace';
2import Recipe from './models/Recipe';
3import Service from './models/Service';
4import User from './models/User';
5import { CachedRequest } from './stores/lib/CachedRequest';
6
1export interface FerdiumStores { 7export interface FerdiumStores {
2 app: AppStore; 8 app: AppStore;
3 communityRecipes: CommunityRecipesStore; 9 communityRecipes: CommunityRecipesStore;
@@ -15,7 +21,7 @@ export interface FerdiumStores {
15 workspaces: WorkspacesStore; 21 workspaces: WorkspacesStore;
16} 22}
17 23
18interface Stores { 24export interface Stores {
19 app: AppStore; 25 app: AppStore;
20 communityRecipes: CommunityRecipesStore; 26 communityRecipes: CommunityRecipesStore;
21 features: FeaturesStore; 27 features: FeaturesStore;
@@ -62,9 +68,11 @@ interface AppStore {
62 api: Api; 68 api: Api;
63 authRequestFailed: () => void; 69 authRequestFailed: () => void;
64 autoLaunchOnStart: () => void; 70 autoLaunchOnStart: () => void;
71 automaticUpdates: boolean;
65 clearAppCacheRequest: () => void; 72 clearAppCacheRequest: () => void;
66 dictionaries: []; 73 dictionaries: [];
67 fetchDataInterval: 4; 74 fetchDataInterval: 4;
75 get(key: string): any;
68 getAppCacheSizeRequest: () => void; 76 getAppCacheSizeRequest: () => void;
69 healthCheckRequest: () => void; 77 healthCheckRequest: () => void;
70 isClearingAllCache: () => void; 78 isClearingAllCache: () => void;
@@ -74,6 +82,8 @@ interface AppStore {
74 isSystemDarkModeEnabled: () => void; 82 isSystemDarkModeEnabled: () => void;
75 isSystemMuteOverridden: () => void; 83 isSystemMuteOverridden: () => void;
76 locale: () => void; 84 locale: () => void;
85 reloadAfterResume: boolean;
86 reloadAfterResumeTime: number;
77 stores: Stores; 87 stores: Stores;
78 timeOfflineStart: () => void; 88 timeOfflineStart: () => void;
79 timeSuspensionStart: () => void; 89 timeSuspensionStart: () => void;
@@ -95,8 +105,8 @@ interface AppStore {
95interface CommunityRecipesStore { 105interface CommunityRecipesStore {
96 actions: Actions; 106 actions: Actions;
97 stores: Stores; 107 stores: Stores;
98 _actions: []; 108 _actions: any[];
99 _reactions: []; 109 _reactions: any[];
100 communityRecipes: () => void; 110 communityRecipes: () => void;
101} 111}
102 112
@@ -105,7 +115,7 @@ interface FeaturesStore {
105 api: Api; 115 api: Api;
106 defaultFeaturesRequest: () => void; 116 defaultFeaturesRequest: () => void;
107 features: () => void; 117 features: () => void;
108 featuresRequest: () => void; 118 featuresRequest: CachedRequest;
109 stores: Stores; 119 stores: Stores;
110 _reactions: any[]; 120 _reactions: any[];
111 _status: () => void; 121 _status: () => void;
@@ -136,8 +146,8 @@ interface RecipePreviewsStore {
136 _reactions: []; 146 _reactions: [];
137 _status: () => void; 147 _status: () => void;
138 actionStatus: () => void; 148 actionStatus: () => void;
139 all: () => void; 149 all: Recipe[];
140 dev: () => void; 150 dev: Recipe[];
141 searchResults: () => void; 151 searchResults: () => void;
142} 152}
143 153
@@ -152,7 +162,7 @@ interface RecipeStore {
152 _status: () => void; 162 _status: () => void;
153 actionStatus: () => void; 163 actionStatus: () => void;
154 active: () => void; 164 active: () => void;
155 all: () => void; 165 all: Recipe[];
156 recipeIdForServices: () => void; 166 recipeIdForServices: () => void;
157} 167}
158 168
@@ -179,7 +189,7 @@ interface RouterStore {
179 goForward: () => void; 189 goForward: () => void;
180 history: () => void; 190 history: () => void;
181 location: () => void; 191 location: () => void;
182 push: () => void; 192 push(path: string): void;
183 replace: () => void; 193 replace: () => void;
184} 194}
185 195
@@ -200,7 +210,7 @@ export interface ServicesStore {
200 actionStatus: () => void; 210 actionStatus: () => void;
201 active: () => void; 211 active: () => void;
202 activeSettings: () => void; 212 activeSettings: () => void;
203 all: () => void; 213 all: Service[];
204 allDisplayed: () => void; 214 allDisplayed: () => void;
205 allDisplayedUnordered: () => void; 215 allDisplayedUnordered: () => void;
206 enabled: () => void; 216 enabled: () => void;
@@ -209,6 +219,11 @@ export interface ServicesStore {
209 isTodosServiceAdded: () => void; 219 isTodosServiceAdded: () => void;
210} 220}
211 221
222// TODO: Create actual type based on the default config in config.ts
223interface ISettings {
224 [key: string]: any;
225}
226
212interface SettingsStore { 227interface SettingsStore {
213 actions: Actions; 228 actions: Actions;
214 api: Api; 229 api: Api;
@@ -220,7 +235,7 @@ interface SettingsStore {
220 _reactions: []; 235 _reactions: [];
221 _status: () => void; 236 _status: () => void;
222 actionStatus: () => void; 237 actionStatus: () => void;
223 all: () => void; 238 all: ISettings;
224 app: AppStore; 239 app: AppStore;
225 migration: () => void; 240 migration: () => void;
226 proxy: () => void; 241 proxy: () => void;
@@ -301,7 +316,7 @@ interface UserStore {
301 deleteAccountRequest: () => void; 316 deleteAccountRequest: () => void;
302 fetchUserInfoInterval: null; 317 fetchUserInfoInterval: null;
303 getLegacyServicesRequest: () => void; 318 getLegacyServicesRequest: () => void;
304 getUserInfoRequest: () => void; 319 getUserInfoRequest: CachedRequest;
305 hasCompletedSignup: () => void; 320 hasCompletedSignup: () => void;
306 id: () => void; 321 id: () => void;
307 inviteRequest: () => void; 322 inviteRequest: () => void;
@@ -320,11 +335,11 @@ interface UserStore {
320 _requireAuthenticatedUser: () => void; 335 _requireAuthenticatedUser: () => void;
321 _status: () => void; 336 _status: () => void;
322 changeServerRoute: () => void; 337 changeServerRoute: () => void;
323 data: () => void; 338 data: User;
324 importRoute: () => void; 339 importRoute: () => void;
325 inviteRoute: () => void; 340 inviteRoute: () => void;
326 isLoggedIn: () => void; 341 isLoggedIn: boolean;
327 isTokenExpired: () => void; 342 isTokenExpired: () => boolean;
328 legacyServices: () => void; 343 legacyServices: () => void;
329 loginRoute: () => void; 344 loginRoute: () => void;
330 logoutRoute: () => void; 345 logoutRoute: () => void;
@@ -348,6 +363,7 @@ export interface WorkspacesStore {
348 isWorkspaceDrawerOpen: () => void; 363 isWorkspaceDrawerOpen: () => void;
349 nextWorkspace: () => void; 364 nextWorkspace: () => void;
350 stores: Stores; 365 stores: Stores;
366 workspaces: Workspace[];
351 workspaceBeingEdited: () => void; 367 workspaceBeingEdited: () => void;
352 _actions: any[]; 368 _actions: any[];
353 _activateLastUsedWorkspaceReaction: () => void; 369 _activateLastUsedWorkspaceReaction: () => void;
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.ts
index a8e1ce247..5659460c6 100644
--- a/src/stores/AppStore.js
+++ b/src/stores/AppStore.ts
@@ -14,12 +14,19 @@ import ms from 'ms';
14import { URL } from 'url'; 14import { URL } from 'url';
15import { readJsonSync } from 'fs-extra'; 15import { readJsonSync } from 'fs-extra';
16 16
17import Store from './lib/Store'; 17import { Stores } from 'src/stores.types';
18import { ApiInterface } from 'src/api';
19import { Actions } from 'src/actions/lib/actions';
20import TypedStore from './lib/TypedStore';
18import Request from './lib/Request'; 21import Request from './lib/Request';
19import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config'; 22import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config';
20import { cleanseJSObject } from '../jsUtils'; 23import { cleanseJSObject } from '../jsUtils';
21import { isMac, isWindows, electronVersion, osRelease } from '../environment'; 24import { isMac, isWindows, electronVersion, osRelease } from '../environment';
22import { ferdiumVersion, userDataPath, ferdiumLocale } from '../environment-remote'; 25import {
26 ferdiumVersion,
27 userDataPath,
28 ferdiumLocale,
29} from '../environment-remote';
23import { generatedTranslations } from '../i18n/translations'; 30import { generatedTranslations } from '../i18n/translations';
24import { getLocale } from '../helpers/i18n-helpers'; 31import { getLocale } from '../helpers/i18n-helpers';
25 32
@@ -45,7 +52,7 @@ const CATALINA_NOTIFICATION_HACK_KEY =
45 52
46const locales = generatedTranslations(); 53const locales = generatedTranslations();
47 54
48export default class AppStore extends Store { 55export default class AppStore extends TypedStore {
49 updateStatusTypes = { 56 updateStatusTypes = {
50 CHECKING: 'CHECKING', 57 CHECKING: 'CHECKING',
51 AVAILABLE: 'AVAILABLE', 58 AVAILABLE: 'AVAILABLE',
@@ -89,10 +96,10 @@ export default class AppStore extends Store {
89 96
90 dictionaries = []; 97 dictionaries = [];
91 98
92 fetchDataInterval = null; 99 fetchDataInterval: null | NodeJS.Timer = null;
93 100
94 constructor(...args) { 101 constructor(stores: Stores, api: ApiInterface, actions: Actions) {
95 super(...args); 102 super(stores, api, actions);
96 103
97 // Register action handlers 104 // Register action handlers
98 this.actions.app.notify.listen(this._notify.bind(this)); 105 this.actions.app.notify.listen(this._notify.bind(this));
@@ -118,7 +125,7 @@ export default class AppStore extends Store {
118 ]); 125 ]);
119 } 126 }
120 127
121 async setup() { 128 async setup(): Promise<void> {
122 this._appStartsCounter(); 129 this._appStartsCounter();
123 // Focus the active service 130 // Focus the active service
124 window.addEventListener('focus', this.actions.service.focusActiveService); 131 window.addEventListener('focus', this.actions.service.focusActiveService);
@@ -162,7 +169,7 @@ export default class AppStore extends Store {
162 setInterval(() => this._checkForUpdates(), CHECK_INTERVAL); 169 setInterval(() => this._checkForUpdates(), CHECK_INTERVAL);
163 // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues) 170 // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues)
164 setTimeout(() => this._checkForUpdates(), ms('30s')); 171 setTimeout(() => this._checkForUpdates(), ms('30s'));
165 ipcRenderer.on('autoUpdate', (event, data) => { 172 ipcRenderer.on('autoUpdate', (_, data) => {
166 if (this.updateStatus !== this.updateStatusTypes.FAILED) { 173 if (this.updateStatus !== this.updateStatusTypes.FAILED) {
167 if (data.available) { 174 if (data.available) {
168 this.updateStatus = this.updateStatusTypes.AVAILABLE; 175 this.updateStatus = this.updateStatusTypes.AVAILABLE;
@@ -185,7 +192,10 @@ export default class AppStore extends Store {
185 if (data.error) { 192 if (data.error) {
186 if (data.error.message && data.error.message.startsWith('404')) { 193 if (data.error.message && data.error.message.startsWith('404')) {
187 this.updateStatus = this.updateStatusTypes.NOT_AVAILABLE; 194 this.updateStatus = this.updateStatusTypes.NOT_AVAILABLE;
188 console.warn('Updater warning: there seems to be unpublished pre-release(s) available on GitHub', data.error); 195 console.warn(
196 'Updater warning: there seems to be unpublished pre-release(s) available on GitHub',
197 data.error,
198 );
189 } else { 199 } else {
190 console.error('Updater error:', data.error); 200 console.error('Updater error:', data.error);
191 this.updateStatus = this.updateStatusTypes.FAILED; 201 this.updateStatus = this.updateStatusTypes.FAILED;
@@ -195,7 +205,7 @@ export default class AppStore extends Store {
195 }); 205 });
196 206
197 // Handle deep linking (ferdium://) 207 // Handle deep linking (ferdium://)
198 ipcRenderer.on('navigateFromDeepLink', (event, data) => { 208 ipcRenderer.on('navigateFromDeepLink', (_, data) => {
199 debug('Navigate from deep link', data); 209 debug('Navigate from deep link', data);
200 let { url } = data; 210 let { url } = data;
201 if (!url) return; 211 if (!url) return;
@@ -217,7 +227,7 @@ export default class AppStore extends Store {
217 227
218 this.isSystemDarkModeEnabled = nativeTheme.shouldUseDarkColors; 228 this.isSystemDarkModeEnabled = nativeTheme.shouldUseDarkColors;
219 229
220 ipcRenderer.on('isWindowFocused', (event, isFocused) => { 230 ipcRenderer.on('isWindowFocused', (_, isFocused) => {
221 debug('Setting is focused to', isFocused); 231 debug('Setting is focused to', isFocused);
222 this.isFocused = isFocused; 232 this.isFocused = isFocused;
223 }); 233 });
@@ -391,7 +401,11 @@ export default class AppStore extends Store {
391 } 401 }
392 402
393 @action _checkForUpdates() { 403 @action _checkForUpdates() {
394 if (this.isOnline && this.stores.settings.app.automaticUpdates && (isMac || isWindows || process.env.APPIMAGE)) { 404 if (
405 this.isOnline &&
406 this.stores.settings.app.automaticUpdates &&
407 (isMac || isWindows || process.env.APPIMAGE)
408 ) {
395 debug('_checkForUpdates: sending event to autoUpdate:check'); 409 debug('_checkForUpdates: sending event to autoUpdate:check');
396 this.updateStatus = this.updateStatusTypes.CHECKING; 410 this.updateStatus = this.updateStatusTypes.CHECKING;
397 ipcRenderer.send('autoUpdate', { 411 ipcRenderer.send('autoUpdate', {
@@ -526,7 +540,7 @@ export default class AppStore extends Store {
526 } 540 }
527 541
528 _handleLogout() { 542 _handleLogout() {
529 if (!this.stores.user.isLoggedIn) { 543 if (!this.stores.user.isLoggedIn && this.fetchDataInterval !== null) {
530 clearInterval(this.fetchDataInterval); 544 clearInterval(this.fetchDataInterval);
531 } 545 }
532 } 546 }
diff --git a/src/stores/index.ts b/src/stores/index.ts
index 6ad898d85..a5b1a7452 100644
--- a/src/stores/index.ts
+++ b/src/stores/index.ts
@@ -1,3 +1,7 @@
1import { Stores } from 'src/stores.types';
2import { RouterStore } from 'mobx-react-router';
3import { ApiInterface } from 'src/api';
4import { Actions } from 'src/actions/lib/actions';
1import AppStore from './AppStore'; 5import AppStore from './AppStore';
2import UserStore from './UserStore'; 6import UserStore from './UserStore';
3import FeaturesStore from './FeaturesStore'; 7import FeaturesStore from './FeaturesStore';
@@ -12,8 +16,12 @@ import { workspaceStore } from '../features/workspaces';
12import { communityRecipesStore } from '../features/communityRecipes'; 16import { communityRecipesStore } from '../features/communityRecipes';
13import { todosStore } from '../features/todos'; 17import { todosStore } from '../features/todos';
14 18
15export default (api, actions, router) => { 19export default (
16 const stores = {}; 20 api: ApiInterface,
21 actions: Actions,
22 router: RouterStore,
23): Stores => {
24 const stores: Stores | any = {};
17 Object.assign(stores, { 25 Object.assign(stores, {
18 router, 26 router,
19 app: new AppStore(stores, api, actions), 27 app: new AppStore(stores, api, actions),
@@ -37,5 +45,6 @@ export default (api, actions, router) => {
37 stores[name].initialize(); 45 stores[name].initialize();
38 } 46 }
39 } 47 }
48
40 return stores; 49 return stores;
41}; 50};
diff --git a/src/stores/lib/Reaction.ts b/src/stores/lib/Reaction.ts
index 0ca24a6fa..3966c8073 100644
--- a/src/stores/lib/Reaction.ts
+++ b/src/stores/lib/Reaction.ts
@@ -1,24 +1,24 @@
1import { autorun } from 'mobx'; 1import { autorun, IReactionDisposer, IReactionPublic } from 'mobx';
2 2
3export default class Reaction { 3export default class Reaction {
4 reaction; 4 public reaction: (r: IReactionPublic) => any;
5 5
6 isRunning = false; 6 private isRunning: boolean = false;
7 7
8 dispose; 8 public dispose?: IReactionDisposer;
9 9
10 constructor(reaction) { 10 constructor(reaction: any) {
11 this.reaction = reaction; 11 this.reaction = reaction;
12 } 12 }
13 13
14 start() { 14 start(): void {
15 if (!this.isRunning) { 15 if (!this.isRunning) {
16 this.dispose = autorun(this.reaction); 16 this.dispose = autorun(this.reaction);
17 this.isRunning = true; 17 this.isRunning = true;
18 } 18 }
19 } 19 }
20 20
21 stop() { 21 stop(): void {
22 if (this.isRunning) { 22 if (this.isRunning) {
23 this.dispose?.(); 23 this.dispose?.();
24 this.isRunning = false; 24 this.isRunning = false;
diff --git a/src/stores/lib/Store.js b/src/stores/lib/Store.js
index a867c3a46..739a47729 100644
--- a/src/stores/lib/Store.js
+++ b/src/stores/lib/Store.js
@@ -2,12 +2,16 @@ import { computed, observable } from 'mobx';
2import Reaction from './Reaction'; 2import Reaction from './Reaction';
3 3
4export default class Store { 4export default class Store {
5 stores = {}; 5 /** @type Stores */
6 stores;
6 7
7 api = {}; 8 /** @type ApiInterface */
9 api;
8 10
9 actions = {}; 11 /** @type Actions */
12 actions;
10 13
14 /** @type Reaction[] */
11 _reactions = []; 15 _reactions = [];
12 16
13 // status implementation 17 // status implementation
@@ -28,8 +32,9 @@ export default class Store {
28 } 32 }
29 33
30 registerReactions(reactions) { 34 registerReactions(reactions) {
31 for (const reaction of reactions) 35 for (const reaction of reactions) {
32 this._reactions.push(new Reaction(reaction)); 36 this._reactions.push(new Reaction(reaction));
37 }
33 } 38 }
34 39
35 setup() {} 40 setup() {}
diff --git a/src/stores/lib/TypedStore.ts b/src/stores/lib/TypedStore.ts
new file mode 100644
index 000000000..5d8bf3bbd
--- /dev/null
+++ b/src/stores/lib/TypedStore.ts
@@ -0,0 +1,46 @@
1import { computed, IReactionPublic, observable } from 'mobx';
2import { Actions } from 'src/actions/lib/actions';
3import { ApiInterface } from 'src/api';
4import { Stores } from 'src/stores.types';
5import Reaction from './Reaction';
6
7export default abstract class TypedStore {
8 _reactions: Reaction[] = [];
9
10 @observable _status: any = null;
11
12 @computed get actionStatus() {
13 return this._status || [];
14 }
15
16 set actionStatus(status) {
17 this._status = status;
18 }
19
20 constructor(
21 public stores: Stores,
22 public api: ApiInterface,
23 public actions: Actions,
24 ) {}
25
26 registerReactions(reactions: { (r: IReactionPublic): void }[]): void {
27 for (const reaction of reactions) {
28 this._reactions.push(new Reaction(reaction));
29 }
30 }
31
32 public abstract setup(): void;
33
34 initialize(): void {
35 this.setup();
36 for (const reaction of this._reactions) reaction.start();
37 }
38
39 teardown(): void {
40 for (const reaction of this._reactions) reaction.stop();
41 }
42
43 resetStatus(): void {
44 this._status = null;
45 }
46}