From caf43fc8d64c27a1aad4a5d6507313c513c1a447 Mon Sep 17 00:00:00 2001 From: Ricardo Cino Date: Wed, 22 Jun 2022 16:21:23 +0200 Subject: chore: featureStore and GlobalErrorStore JS => TS --- gulpfile.babel.js | 6 +- src/app.js | 65 ---------------------- src/app.jsx | 65 ++++++++++++++++++++++ src/stores.types.ts | 113 ++++++++++---------------------------- src/stores/FeaturesStore.js | 77 -------------------------- src/stores/FeaturesStore.ts | 76 +++++++++++++++++++++++++ src/stores/GlobalErrorStore.js | 87 ----------------------------- src/stores/GlobalErrorStore.ts | 122 +++++++++++++++++++++++++++++++++++++++++ src/stores/lib/TypedStore.ts | 6 +- 9 files changed, 297 insertions(+), 320 deletions(-) delete mode 100644 src/app.js create mode 100644 src/app.jsx delete mode 100644 src/stores/FeaturesStore.js create mode 100644 src/stores/FeaturesStore.ts delete mode 100644 src/stores/GlobalErrorStore.js create mode 100644 src/stores/GlobalErrorStore.ts diff --git a/gulpfile.babel.js b/gulpfile.babel.js index f7c016c2b..447542f56 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -68,9 +68,9 @@ const paths = { watch: 'src/styles/**/*.scss', }, javascripts: { - src: 'src/**/*.js', + src: ['src/**/*.js', 'src/**/*.jsx'], dest: 'build/', - watch: 'src/**/*.js', + watch: ['src/**/*.js', 'src/**/*.jsx'], }, typescripts: { src: ['src/**/*.ts', 'src/**/*.tsx'], @@ -187,7 +187,7 @@ export function styles() { export function processJavascripts() { return gulp - .src([paths.javascripts.src], { since: gulp.lastRun(processJavascripts) }) + .src(paths.javascripts.src, { since: gulp.lastRun(processJavascripts) }) .pipe( babel({ comments: false, diff --git a/src/app.js b/src/app.js deleted file mode 100644 index c92d044e6..000000000 --- a/src/app.js +++ /dev/null @@ -1,65 +0,0 @@ -import { webFrame } from 'electron'; - -import { render } from 'react-dom'; -import { Provider } from 'mobx-react'; -import { syncHistoryWithStore, RouterStore } from 'mobx-react-router'; -import { hashHistory } from 'react-router'; - -import ServerApi from './api/server/ServerApi'; -import LocalApi from './api/server/LocalApi'; -import storeFactory from './stores'; -import apiFactory from './api'; -import actions from './actions'; -import MenuFactory from './lib/Menu'; -import TouchBarFactory from './lib/TouchBar'; - -import I18N from './I18n'; -import Routes from './routes'; - -// Basic electron Setup -webFrame.setVisualZoomLevelLimits(1, 1); - -window.addEventListener('load', () => { - const serverApi = new ServerApi(); - const api = apiFactory(serverApi, new LocalApi()); - const router = new RouterStore(); - const stores = storeFactory(api, actions, router); - const history = syncHistoryWithStore(hashHistory, router); - const menu = new MenuFactory(stores, actions); - const touchBar = new TouchBarFactory(stores, actions); - - window['ferdium'] = { - stores, - actions, - api, - menu, - touchBar, - features: {}, - render() { - const preparedApp = ( - - - - - - ); - render(preparedApp, document.querySelector('#root')); - }, - }; - window['ferdium'].render(); -}); - -// Prevent back and forward mouse events for the app itself (not inside the recipe) -// TODO: send this request to the recipe.js -window.addEventListener('mouseup', e => { - if (e.button === 3 || e.button === 4) { - e.preventDefault(); - e.stopPropagation(); - } -}); - -// Prevent drag and drop into window from redirecting -window.addEventListener('dragover', event => event.preventDefault()); -window.addEventListener('drop', event => event.preventDefault()); -window.addEventListener('dragover', event => event.stopPropagation()); -window.addEventListener('drop', event => event.stopPropagation()); diff --git a/src/app.jsx b/src/app.jsx new file mode 100644 index 000000000..db0dee392 --- /dev/null +++ b/src/app.jsx @@ -0,0 +1,65 @@ +import { webFrame } from 'electron'; + +import { render } from 'react-dom'; +import { Provider } from 'mobx-react'; +import { syncHistoryWithStore, RouterStore } from 'mobx-react-router'; +import { hashHistory } from 'react-router'; + +import ServerApi from './api/server/ServerApi'; +import LocalApi from './api/server/LocalApi'; +import storeFactory from './stores'; +import apiFactory from './api'; +import actions from './actions'; +import MenuFactory from './lib/Menu'; +import TouchBarFactory from './lib/TouchBar'; + +import I18N from './I18n'; +import Routes from './routes'; + +// Basic electron Setup +webFrame.setVisualZoomLevelLimits(1, 1); + +window.addEventListener('load', () => { + const serverApi = new ServerApi(); + const api = apiFactory(serverApi, new LocalApi()); + const router = new RouterStore(); + const stores = storeFactory(api, actions, router); + const history = syncHistoryWithStore(hashHistory, router); + const menu = new MenuFactory(stores, actions); + const touchBar = new TouchBarFactory(stores, actions); + + window['ferdium'] = { + stores, + actions, + api, + menu, + touchBar, + features: {}, + render() { + const preparedApp = ( + + + + + + ); + render(preparedApp, document.querySelector('#root')); + }, + }; + window['ferdium'].render(); +}); + +// Prevent back and forward mouse events for the app itself (not inside the recipe) +// TODO: send this request to the recipe.js +window.addEventListener('mouseup', e => { + if (e.button === 3 || e.button === 4) { + e.preventDefault(); + e.stopPropagation(); + } +}); + +// Prevent drag and drop into window from redirecting +window.addEventListener('dragover', event => event.preventDefault()); +window.addEventListener('drop', event => event.preventDefault()); +window.addEventListener('dragover', event => event.stopPropagation()); +window.addEventListener('drop', event => event.stopPropagation()); diff --git a/src/stores.types.ts b/src/stores.types.ts index b0912c463..462d862d9 100644 --- a/src/stores.types.ts +++ b/src/stores.types.ts @@ -3,6 +3,7 @@ import Recipe from './models/Recipe'; import Service from './models/Service'; import User from './models/User'; import { CachedRequest } from './stores/lib/CachedRequest'; +import Reaction from './stores/lib/Reaction'; export interface FerdiumStores { app: AppStore; @@ -61,11 +62,21 @@ interface Api { user: UserStore; } -interface AppStore { +interface TypedStore { actions: Actions; + api: Api; + stores: Stores; + _reactions: Reaction[]; + _status: any; + actionStatus: () => void; + initialize: () => void; + tearDown: () => void; + resetStatus: () => void; +} + +interface AppStore extends TypedStore { accentColor: string; progressbarAccentColor: string; - api: Api; authRequestFailed: () => void; autoLaunchOnStart: () => void; automaticUpdates: boolean; @@ -84,7 +95,6 @@ interface AppStore { locale: () => void; reloadAfterResume: boolean; reloadAfterResumeTime: number; - stores: Stores; timeOfflineStart: () => void; timeSuspensionStart: () => void; updateStatus: () => void; @@ -95,91 +105,56 @@ interface AppStore { DOWNLOADED: 'DOWNLOADED'; FAILED: 'FAILED'; }; - _reactions: any[]; - _status: () => void; - actionStatus: () => void; cacheSize: () => void; debugInfo: () => void; } -interface CommunityRecipesStore { - actions: Actions; - stores: Stores; - _actions: any[]; - _reactions: any[]; +interface CommunityRecipesStore extends TypedStore { communityRecipes: () => void; } -interface FeaturesStore { - actions: Actions; - api: Api; +interface FeaturesStore extends TypedStore { + anonymousFeatures: () => void; defaultFeaturesRequest: () => void; features: () => void; featuresRequest: CachedRequest; - stores: Stores; - _reactions: any[]; - _status: () => void; + _monitorLoginStatus: () => void; _updateFeatures: () => void; - actionStatus: () => void; - anonymousFeatures: () => void; + _setupFeatures: () => void; } -interface GlobalErrorStore { - actions: Actions; - api: Api; +interface GlobalErrorStore extends TypedStore { error: () => void; messages: () => void; response: () => void; - stores: Stores; _handleRequests: () => void; - _reactions: []; - _status: () => void; - actionStatus: () => void; } -interface RecipePreviewsStore { - actions: Actions; +interface RecipePreviewsStore extends TypedStore { allRecipePreviewsRequest: () => void; - api: Api; searchRecipePreviewsRequest: () => void; - stores: Stores; - _reactions: []; - _status: () => void; - actionStatus: () => void; all: Recipe[]; dev: Recipe[]; searchResults: () => void; } -interface RecipeStore { - actions: Actions; +interface RecipeStore extends TypedStore { allRecipesRequest: () => void; - api: Api; getRecipeUpdatesRequest: () => void; installRecipeRequest: () => void; - stores: Stores; - _reactions: any[]; - _status: () => void; - actionStatus: () => void; isInstalled: (id: string) => boolean; active: () => void; all: Recipe[]; recipeIdForServices: () => void; } -interface RequestsStore { - actions: Actions; - api: Api; +interface RequestsStore extends TypedStore { localServerPort: () => void; retries: number; retryDelay: number; servicesRequest: () => void; showRequiredRequestsError: () => void; - stores: Stores; userInfoRequest: () => void; - _reactions: any[]; - _status: () => void; - actionStatus: () => void; areRequiredRequestsLoading: () => void; areRequiredRequestsSuccessful: () => void; } @@ -203,9 +178,7 @@ interface RouterStore { replace: () => void; } -export interface ServicesStore { - actions: Actions; - api: Api; +export interface ServicesStore extends TypedStore { clearCacheRequest: () => void; createServiceRequest: () => void; deleteServiceRequest: () => void; @@ -214,11 +187,7 @@ export interface ServicesStore { lastUsedServices: () => void; reorderServicesRequest: () => void; serviceMaintenanceTick: () => void; - stores: Stores; updateServiceRequest: () => void; - _reactions: any[]; - _status: () => void; - actionStatus: () => void; active: () => void; activeSettings: () => void; all: Service[]; @@ -235,17 +204,11 @@ interface ISettings { [key: string]: any; } -interface SettingsStore { - actions: Actions; - api: Api; +interface SettingsStore extends TypedStore { fileSystemSettingsTypes: any[]; loaded: boolean; - stores: Stores; updateAppSettingsRequest: () => void; _fileSystemSettingsCache: () => void; - _reactions: []; - _status: () => void; - actionStatus: () => void; all: ISettings; app: AppStore; migration: () => void; @@ -254,22 +217,17 @@ interface SettingsStore { stats: () => void; } -interface TodosStore { - actions: Actions; - api: Api; +interface TodosStore extends TypedStore { isFeatureActive: () => void; isInitialized: true; - stores: Stores; userAgentModel: () => void; webview: () => void; - _actions: any[]; _allReactions: any[]; _firstLaunchReaction: () => void; _goToService: () => void; _handleNewWindowEvent: () => void; _onTodosClientInitialized: () => void; _openDevTools: () => void; - _reactions: any[]; _reload: () => void; _routeCheckReaction: () => void; _updateSettings: () => void; @@ -292,14 +250,8 @@ interface TodosStore { _toggleTodosPanel: () => void; } -interface UIStore { - actions: Actions; - api: Api; +interface UIStore extends TypedStore { isOsDarkThemeActive: () => void; - stores: Stores; - _reactions: []; - _status: () => void; - actionStatus: () => void; showServicesUpdatedInfoBar: boolean; isDarkThemeActive: () => void; isSplitModeActive: () => void; @@ -308,7 +260,7 @@ interface UIStore { theme: () => void; } -interface UserStore { +interface UserStore extends TypedStore { BASE_ROUTE: '/auth'; CHANGE_SERVER_ROUTE: '/auth/server'; IMPORT_ROUTE: '/auth/signup/import'; @@ -320,9 +272,6 @@ interface UserStore { SIGNUP_ROUTE: '/auth/signup'; WELCOME_ROUTE: '/auth/welcome'; accountType: () => void; - actionStatus: () => void; - actions: Actions; - api: Api; authToken: () => void; deleteAccountRequest: () => void; fetchUserInfoInterval: null; @@ -339,12 +288,9 @@ interface UserStore { logoutReasonTypes: { SERVER: 'SERVER' }; passwordRequest: () => void; signupRequest: () => void; - stores: Stores; updateUserInfoRequest: () => void; userData: () => void; - _reactions: any[]; _requireAuthenticatedUser: () => void; - _status: () => void; changeServerRoute: () => void; data: User; importRoute: () => void; @@ -360,7 +306,7 @@ interface UserStore { team: () => void; } -export interface WorkspacesStore { +export interface WorkspacesStore extends TypedStore { activeWorkspace: () => void; delete: ({ workspace }) => void; update: ({ workspace }) => void; @@ -373,17 +319,14 @@ export interface WorkspacesStore { isSwitchingWorkspace: () => void; isWorkspaceDrawerOpen: () => void; nextWorkspace: () => void; - stores: Stores; workspaces: Workspace[]; workspaceBeingEdited: () => void; - _actions: any[]; _activateLastUsedWorkspaceReaction: () => void; _allActions: any[]; _allReactions: any[]; _cleanupInvalidServiceReferences: () => void; _getWorkspaceById: () => void; _openDrawerWithSettingsReaction: () => void; - _reactions: any[]; _setActiveServiceOnWorkspaceSwitchReaction: () => void; _setWorkspaceBeingEditedReaction: () => void; _toggleKeepAllWorkspacesLoadedSetting: () => void; diff --git a/src/stores/FeaturesStore.js b/src/stores/FeaturesStore.js deleted file mode 100644 index e2c0c08ef..000000000 --- a/src/stores/FeaturesStore.js +++ /dev/null @@ -1,77 +0,0 @@ -import { computed, observable, runInAction } from 'mobx'; - -import Store from './lib/Store'; -import CachedRequest from './lib/CachedRequest'; - -import serviceProxy from '../features/serviceProxy'; -import basicAuth from '../features/basicAuth'; -import workspaces from '../features/workspaces'; -import quickSwitch from '../features/quickSwitch'; -import publishDebugInfo from '../features/publishDebugInfo'; -import communityRecipes from '../features/communityRecipes'; -import todos from '../features/todos'; -import appearance from '../features/appearance'; - -export default class FeaturesStore extends Store { - @observable defaultFeaturesRequest = new CachedRequest( - this.api.features, - 'default', - ); - - @observable featuresRequest = new CachedRequest( - this.api.features, - 'features', - ); - - @observable features = {}; - - async setup() { - this.registerReactions([ - this._updateFeatures, - this._monitorLoginStatus.bind(this), - ]); - - await this.featuresRequest._promise; - setTimeout(this._setupFeatures.bind(this), 1); - } - - @computed get anonymousFeatures() { - return this.defaultFeaturesRequest.execute().result || {}; - } - - _updateFeatures = () => { - const features = {}; - if (this.stores.user.isLoggedIn) { - let requestResult = {}; - try { - requestResult = this.featuresRequest.execute().result; - } catch (error) { - console.error(error); - } - Object.assign(features, requestResult); - } - runInAction('FeaturesStore::_updateFeatures', () => { - this.features = features; - }); - }; - - _monitorLoginStatus() { - if (this.stores.user.isLoggedIn) { - this.featuresRequest.invalidate({ immediately: true }); - } else { - this.defaultFeaturesRequest.execute(); - this.defaultFeaturesRequest.invalidate({ immediately: true }); - } - } - - _setupFeatures() { - serviceProxy(this.stores); - basicAuth(); - workspaces(this.stores, this.actions); - quickSwitch(); - publishDebugInfo(); - communityRecipes(this.stores, this.actions); - todos(this.stores, this.actions); - appearance(this.stores); - } -} diff --git a/src/stores/FeaturesStore.ts b/src/stores/FeaturesStore.ts new file mode 100644 index 000000000..b63c252df --- /dev/null +++ b/src/stores/FeaturesStore.ts @@ -0,0 +1,76 @@ +import { computed, observable, runInAction } from 'mobx'; + +import CachedRequest from './lib/CachedRequest'; +import serviceProxy from '../features/serviceProxy'; +import basicAuth from '../features/basicAuth'; +import workspaces from '../features/workspaces'; +import quickSwitch from '../features/quickSwitch'; +import publishDebugInfo from '../features/publishDebugInfo'; +import communityRecipes from '../features/communityRecipes'; +import todos from '../features/todos'; +import appearance from '../features/appearance'; +import TypedStore from './lib/TypedStore'; + +export default class FeaturesStore extends TypedStore { + @observable defaultFeaturesRequest = new CachedRequest( + this.api.features, + 'default', + ); + + @observable featuresRequest = new CachedRequest( + this.api.features, + 'features', + ); + + @observable features = {}; + + async setup(): Promise { + this.registerReactions([ + this._updateFeatures, + this._monitorLoginStatus.bind(this), + ]); + + await this.featuresRequest._promise; + setTimeout(this._setupFeatures.bind(this), 1); + } + + @computed get anonymousFeatures(): any { + return this.defaultFeaturesRequest.execute().result || {}; + } + + _updateFeatures = (): void => { + const features = {}; + if (this.stores.user.isLoggedIn) { + let requestResult = {}; + try { + requestResult = this.featuresRequest.execute().result; + } catch (error) { + console.error(error); + } + Object.assign(features, requestResult); + } + runInAction('FeaturesStore::_updateFeatures', () => { + this.features = features; + }); + }; + + _monitorLoginStatus(): void { + if (this.stores.user.isLoggedIn) { + this.featuresRequest.invalidate({ immediately: true }); + } else { + this.defaultFeaturesRequest.execute(); + this.defaultFeaturesRequest.invalidate({ immediately: true }); + } + } + + _setupFeatures(): void { + serviceProxy(this.stores); + basicAuth(); + workspaces(this.stores, this.actions); + quickSwitch(); + publishDebugInfo(); + communityRecipes(this.stores, this.actions); + todos(this.stores, this.actions); + appearance(this.stores); + } +} diff --git a/src/stores/GlobalErrorStore.js b/src/stores/GlobalErrorStore.js deleted file mode 100644 index 356a6f298..000000000 --- a/src/stores/GlobalErrorStore.js +++ /dev/null @@ -1,87 +0,0 @@ -import { observable, action } from 'mobx'; -import Store from './lib/Store'; -import Request from './lib/Request'; - -export default class GlobalErrorStore extends Store { - @observable error = null; - - @observable messages = []; - - @observable response = {}; - - constructor(...args) { - super(...args); - - window.addEventListener('error', (...errorArgs) => { - this._handleConsoleError.call(this, ['error', ...errorArgs]); - }); - - const origConsoleError = console.error; - window.console.error = (...errorArgs) => { - this._handleConsoleError.call(this, ['error', ...errorArgs]); - origConsoleError.apply(this, errorArgs); - }; - - const origConsoleLog = console.log; - window.console.log = (...logArgs) => { - this._handleConsoleError.call(this, ['log', ...logArgs]); - origConsoleLog.apply(this, logArgs); - }; - - const origConsoleInfo = console.info; - window.console.info = (...infoArgs) => { - this._handleConsoleError.call(this, ['info', ...infoArgs]); - origConsoleInfo.apply(this, infoArgs); - }; - - Request.registerHook(this._handleRequests); - } - - _handleConsoleError(type, error, url, line) { - if (typeof type === 'object' && type.length > 0) { - this.messages.push({ - type: type[0], - info: type, - }); - } else { - this.messages.push({ - type, - error, - url, - line, - }); - } - } - - _handleRequests = action(async request => { - if (request.isError) { - this.error = request.error; - - if (request.error.json) { - try { - this.response = await request.error.json(); - } catch { - this.response = {}; - } - if (this.error.status === 401) { - window['ferdium'].stores.app.authRequestFailed = true; - // this.actions.user.logout({ serverLogout: true }); - } - } - - this.messages.push({ - type: 'error', - request: { - result: request.result, - wasExecuted: request.wasExecuted, - method: request._method, - }, - error: this.error, - response: this.response, - server: window['ferdium'].stores.settings.app.server, - }); - } else { - window['ferdium'].stores.app.authRequestFailed = false; - } - }); -} diff --git a/src/stores/GlobalErrorStore.ts b/src/stores/GlobalErrorStore.ts new file mode 100644 index 000000000..cb364574b --- /dev/null +++ b/src/stores/GlobalErrorStore.ts @@ -0,0 +1,122 @@ +import { observable, action } from 'mobx'; +import { Actions } from 'src/actions/lib/actions'; +import { ApiInterface } from 'src/api'; +import { Stores } from 'src/stores.types'; +import Request from './lib/Request'; +import TypedStore from './lib/TypedStore'; + +interface Message { + type: 'error' | 'log' | 'info'; + error?: { + message?: string; + status?: number; + }; + request?: { + result: any; + wasExecuted: any; + method: any; + }; + response?: any; + server?: any; + info?: any; + url?: string; + line?: number; +} + +export default class GlobalErrorStore extends TypedStore { + @observable error: any | null = null; + + @observable messages: Message[] = []; + + @observable response: object = {}; + + // TODO: Get rid of the @ts-ignores in this function. + constructor(stores: Stores, api: ApiInterface, actions: Actions) { + super(stores, api, actions); + + window.addEventListener('error', (...errorArgs: any[]): void => { + // @ts-ignore ts-message: Expected 5 arguments, but got 2. + this._handleConsoleError.call(this, ['error', ...errorArgs]); + }); + + const origConsoleError = console.error; + window.console.error = (...errorArgs: any[]) => { + // @ts-ignore ts-message: Expected 5 arguments, but got 2. + this._handleConsoleError.call(this, ['error', ...errorArgs]); + origConsoleError.apply(this, errorArgs); + }; + + const origConsoleLog = console.log; + window.console.log = (...logArgs: any[]) => { + // @ts-ignore ts-message: Expected 5 arguments, but got 2. + this._handleConsoleError.call(this, ['log', ...logArgs]); + origConsoleLog.apply(this, logArgs); + }; + + const origConsoleInfo = console.info; + window.console.info = (...infoArgs: any[]) => { + // @ts-ignore ts-message: Expected 5 arguments, but got 2. + this._handleConsoleError.call(this, ['info', ...infoArgs]); + origConsoleInfo.apply(this, infoArgs); + }; + + Request.registerHook(this._handleRequests); + } + + async setup(): Promise { + // Not implemented + } + + _handleConsoleError(type: any, error: any, url: string, line: number) { + if (typeof type === 'object' && type.length > 0) { + this.messages.push({ + type: type[0], + info: type, + }); + } else { + this.messages.push({ + type, + error, + url, + line, + }); + } + } + + @action _handleRequests = async (request: { + isError: any; + error: { json: () => object | PromiseLike }; + result: any; + wasExecuted: any; + _method: any; + }): Promise => { + if (request.isError) { + this.error = request.error; + + if (request.error.json) { + try { + this.response = await request.error.json(); + } catch { + this.response = {}; + } + if (this.error?.status === 401) { + window['ferdium'].stores.app.authRequestFailed = true; + } + } + + this.messages.push({ + type: 'error', + request: { + result: request.result, + wasExecuted: request.wasExecuted, + method: request._method, + }, + error: this.error, + response: this.response, + server: window['ferdium'].stores.settings.app.server, + }); + } else { + window['ferdium'].stores.app.authRequestFailed = false; + } + }; +} diff --git a/src/stores/lib/TypedStore.ts b/src/stores/lib/TypedStore.ts index 5d8bf3bbd..7f9d2d60f 100644 --- a/src/stores/lib/TypedStore.ts +++ b/src/stores/lib/TypedStore.ts @@ -18,9 +18,9 @@ export default abstract class TypedStore { } constructor( - public stores: Stores, - public api: ApiInterface, - public actions: Actions, + public readonly stores: Stores, + public readonly api: ApiInterface, + public readonly actions: Actions, ) {} registerReactions(reactions: { (r: IReactionPublic): void }[]): void { -- cgit v1.2.3-70-g09d2