From c404dee96d14770b46e29a76e81a602ac6e869ce Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Thu, 19 Apr 2018 21:10:01 +0200 Subject: [PATCH] Typescriptify stores --- ui/src/stores/AppStore.js | 33 ---------------- ui/src/stores/AppStore.ts | 38 +++++++++++++++++++ ui/src/stores/ClientStore.js | 34 ----------------- ui/src/stores/ClientStore.ts | 35 +++++++++++++++++ .../stores/{GlobalStore.js => GlobalStore.ts} | 35 ++++++++--------- .../{MessageStore.js => MessageStore.ts} | 34 +++++++++-------- .../{Notifications.js => Notifications.ts} | 14 ++++--- .../{SnackBarStore.js => SnackBarStore.ts} | 15 +++++--- ui/src/stores/{UserStore.js => UserStore.ts} | 19 +++++----- ui/src/stores/dispatcher.js | 3 -- ui/src/stores/dispatcher.ts | 8 ++++ 11 files changed, 142 insertions(+), 126 deletions(-) delete mode 100644 ui/src/stores/AppStore.js create mode 100644 ui/src/stores/AppStore.ts delete mode 100644 ui/src/stores/ClientStore.js create mode 100644 ui/src/stores/ClientStore.ts rename ui/src/stores/{GlobalStore.js => GlobalStore.ts} (68%) rename ui/src/stores/{MessageStore.js => MessageStore.ts} (79%) rename ui/src/stores/{Notifications.js => Notifications.ts} (70%) rename ui/src/stores/{SnackBarStore.js => SnackBarStore.ts} (54%) rename ui/src/stores/{UserStore.js => UserStore.ts} (50%) delete mode 100644 ui/src/stores/dispatcher.js create mode 100644 ui/src/stores/dispatcher.ts diff --git a/ui/src/stores/AppStore.js b/ui/src/stores/AppStore.js deleted file mode 100644 index 6f34263..0000000 --- a/ui/src/stores/AppStore.js +++ /dev/null @@ -1,33 +0,0 @@ -import {EventEmitter} from 'events'; -import dispatcher from './dispatcher'; - -class AppStore extends EventEmitter { - constructor() { - super(); - this.apps = []; - } - - get() { - return this.apps; - } - - getById(id) { - return this.apps.find((app) => app.id === id); - } - - getName(id) { - const app = this.getById(id); - return id === -1 ? 'All Messages' : app !== undefined ? app.name : 'unknown'; - } - - handle(data) { - if (data.type === 'UPDATE_APPS') { - this.apps = data.payload; - this.emit('change'); - } - } -} - -const store = new AppStore(); -dispatcher.register(store.handle.bind(store)); -export default store; diff --git a/ui/src/stores/AppStore.ts b/ui/src/stores/AppStore.ts new file mode 100644 index 0000000..f713da8 --- /dev/null +++ b/ui/src/stores/AppStore.ts @@ -0,0 +1,38 @@ +import {EventEmitter} from 'events'; +import dispatcher, {IEvent} from './dispatcher'; + +class AppStore extends EventEmitter { + private apps: IApplication[] = []; + + public get(): IApplication[] { + return this.apps; + } + + public getById(id: number): IApplication { + const app = this.getByIdOrUndefined(id); + if (!app) { + throw new Error('app is required to exist') + } + return app; + } + + public getName(id: number): string { + const app = this.getByIdOrUndefined(id); + return id === -1 ? 'All Messages' : app !== undefined ? app.name : 'unknown'; + } + + public handle(data: IEvent): void { + if (data.type === 'UPDATE_APPS') { + this.apps = data.payload; + this.emit('change'); + } + } + + private getByIdOrUndefined(id: number): IApplication | undefined { + return this.apps.find((a) => a.id === id); + } +} + +const store = new AppStore(); +dispatcher.register(store.handle.bind(store)); +export default store; diff --git a/ui/src/stores/ClientStore.js b/ui/src/stores/ClientStore.js deleted file mode 100644 index 0396455..0000000 --- a/ui/src/stores/ClientStore.js +++ /dev/null @@ -1,34 +0,0 @@ -import {EventEmitter} from 'events'; -import dispatcher from './dispatcher'; - -class ClientStore extends EventEmitter { - constructor() { - super(); - this.clients = []; - } - - get() { - return this.clients; - } - - getById(id) { - return this.clients.find((client) => client.id === id); - } - - getIdByToken(token) { - const client = this.clients.find((client) => client.token === token); - return client !== undefined ? client.id : ''; - } - - handle(data) { - if (data.type === 'UPDATE_CLIENTS') { - this.clients = data.payload; - this.emit('change'); - } - } -} - - -const store = new ClientStore(); -dispatcher.register(store.handle.bind(store)); -export default store; diff --git a/ui/src/stores/ClientStore.ts b/ui/src/stores/ClientStore.ts new file mode 100644 index 0000000..4106bb4 --- /dev/null +++ b/ui/src/stores/ClientStore.ts @@ -0,0 +1,35 @@ +import {EventEmitter} from 'events'; +import {default as dispatcher, IEvent} from './dispatcher'; + +class ClientStore extends EventEmitter { + private clients: IClient[] = []; + + public get(): IClient[] { + return this.clients; + } + + public getById(id: number): IClient { + const client = this.clients.find((c) => c.id === id); + if (!client) { + throw new Error('client is required to exist') + } + return client; + } + + public getIdByToken(token: string): number { + const client = this.clients.find((c) => c.token === token); + return client !== undefined ? client.id : -1; + } + + public handle(data: IEvent): void { + if (data.type === 'UPDATE_CLIENTS') { + this.clients = data.payload; + this.emit('change'); + } + } +} + + +const store = new ClientStore(); +dispatcher.register(store.handle.bind(store)); +export default store; diff --git a/ui/src/stores/GlobalStore.js b/ui/src/stores/GlobalStore.ts similarity index 68% rename from ui/src/stores/GlobalStore.js rename to ui/src/stores/GlobalStore.ts index 95e0f4d..ae1f9fe 100644 --- a/ui/src/stores/GlobalStore.js +++ b/ui/src/stores/GlobalStore.ts @@ -1,40 +1,31 @@ import {EventEmitter} from 'events'; -import dispatcher from './dispatcher'; +import dispatcher, {IEvent} from './dispatcher'; class GlobalStore extends EventEmitter { - constructor() { - super(); - this.currentUser = null; - this.isAuthenticating = true; - } + private currentUser: IUser | null = null; + private isAuthenticating = true; - authenticating() { + public authenticating(): boolean { return this.isAuthenticating; } - get() { - return this.currentUser || {name: 'unknown', admin: false}; + public get(): IUser { + return this.currentUser || {name: 'unknown', admin: false, id: -1}; } - isAdmin() { + public isAdmin(): boolean { return this.get().admin; } - getName() { + public getName(): string { return this.get().name; } - isLoggedIn() { + public isLoggedIn(): boolean { return this.currentUser != null; } - set(user) { - this.isAuthenticating = false; - this.currentUser = user; - this.emit('change'); - } - - handle(data) { + public handle(data: IEvent): void { if (data.type === 'NO_AUTHENTICATION') { this.set(null); } else if (data.type === 'AUTHENTICATED') { @@ -44,6 +35,12 @@ class GlobalStore extends EventEmitter { this.emit('change'); } } + + private set(user: IUser | null): void { + this.isAuthenticating = false; + this.currentUser = user; + this.emit('change'); + } } const store = new GlobalStore(); diff --git a/ui/src/stores/MessageStore.js b/ui/src/stores/MessageStore.ts similarity index 79% rename from ui/src/stores/MessageStore.js rename to ui/src/stores/MessageStore.ts index 7a25046..02ee1b9 100644 --- a/ui/src/stores/MessageStore.js +++ b/ui/src/stores/MessageStore.ts @@ -1,23 +1,25 @@ import {EventEmitter} from 'events'; -import dispatcher from './dispatcher'; -import AppStore from './AppStore'; import * as MessageAction from '../actions/MessageAction'; +import AppStore from './AppStore'; +import dispatcher, {IEvent} from './dispatcher'; class MessageStore extends EventEmitter { + + private appToMessages: { [appId: number]: IAppMessages } = {}; + private reset: false | number = false; + private resetOnAll: false | number = false; + private loading = false; + constructor() { super(); - this.appToMessages = {}; - this.reset = false; - this.resetOnAll = false; - this.loading = false; AppStore.on('change', () => { this.updateApps(); this.emit('change'); }); } - shouldReset(appId) { - let reset = appId === -1 ? this.resetOnAll : this.reset; + public shouldReset(appId: number): false | number { + const reset = appId === -1 ? this.resetOnAll : this.reset; if (reset !== false) { this.reset = false; this.resetOnAll = false; @@ -25,7 +27,7 @@ class MessageStore extends EventEmitter { return reset; } - loadNext(id) { + public loadNext(id: number): void { if (this.loading || !this.get(id).hasMore) { return; } @@ -33,7 +35,7 @@ class MessageStore extends EventEmitter { MessageAction.fetchMessagesApp(id, this.get(id).nextSince).catch(() => this.loading = false); } - get(id) { + public get(id: number): IAppMessages { if (this.exists(id)) { return this.appToMessages[id]; } else { @@ -41,11 +43,11 @@ class MessageStore extends EventEmitter { } } - exists(id) { + public exists(id: number): boolean { return this.appToMessages[id] !== undefined; } - handle(data) { + public handle(data: IEvent): void { const {payload} = data; if (data.type === 'UPDATE_MESSAGES') { if (this.exists(payload.id)) { @@ -83,7 +85,7 @@ class MessageStore extends EventEmitter { } } - removeFromList(messages, messageToDelete) { + private removeFromList(messages: IAppMessages, messageToDelete: IMessage): false | number { if (messages) { const index = messages.messages.findIndex((message) => message.id === messageToDelete.id); if (index !== -1) { @@ -94,11 +96,11 @@ class MessageStore extends EventEmitter { return false; } - updateApps = () => { - const appToUrl = {}; + private updateApps = (): void => { + const appToUrl: { [appId: number]: string } = {}; AppStore.get().forEach((app) => appToUrl[app.id] = app.image); Object.keys(this.appToMessages).forEach((key) => { - const appMessages = this.appToMessages[key]; + const appMessages: IAppMessages = this.appToMessages[key]; appMessages.messages.forEach((message) => message.image = appToUrl[message.appid]); }); }; diff --git a/ui/src/stores/Notifications.js b/ui/src/stores/Notifications.ts similarity index 70% rename from ui/src/stores/Notifications.js rename to ui/src/stores/Notifications.ts index f4e628e..88955e1 100644 --- a/ui/src/stores/Notifications.js +++ b/ui/src/stores/Notifications.ts @@ -1,5 +1,5 @@ -import dispatcher from './dispatcher'; import Notify from 'notifyjs'; +import dispatcher, {IEvent} from './dispatcher'; export function requestPermission() { if (Notify.needsPermission && Notify.isSupported()) { @@ -8,22 +8,24 @@ export function requestPermission() { } } -function closeAndFocus(event) { +function closeAndFocus(event: Event) { if (window.parent) { window.parent.focus(); } window.focus(); window.location.href = '/'; - event.target.close(); + const target = event.target as Notification; + target.close(); } -function closeAfterTimeout(event) { +function closeAfterTimeout(event: Event) { setTimeout(() => { - event.target.close(); + const target = event.target as Notification; + target.close(); }, 5000); } -dispatcher.register((data) => { +dispatcher.register((data: IEvent): void => { if (data.type === 'ONE_MESSAGE') { const msg = data.payload; diff --git a/ui/src/stores/SnackBarStore.js b/ui/src/stores/SnackBarStore.ts similarity index 54% rename from ui/src/stores/SnackBarStore.js rename to ui/src/stores/SnackBarStore.ts index 2ae2e29..4000d6f 100644 --- a/ui/src/stores/SnackBarStore.js +++ b/ui/src/stores/SnackBarStore.ts @@ -1,18 +1,21 @@ import {EventEmitter} from 'events'; -import dispatcher from './dispatcher'; +import dispatcher, {IEvent} from './dispatcher'; class SnackBarStore extends EventEmitter { - messages = []; + public messages: string[] = []; - next() { - return this.messages.shift(); + public next(): string { + if (!this.hasNext()) { + throw new Error("no such element") + } + return this.messages.shift() as string; } - hasNext() { + public hasNext(): boolean { return this.messages.length !== 0; } - handle(data) { + public handle(data: IEvent): void { if (data.type === 'SNACK') { this.messages.push(data.payload); this.emit('change'); diff --git a/ui/src/stores/UserStore.js b/ui/src/stores/UserStore.ts similarity index 50% rename from ui/src/stores/UserStore.js rename to ui/src/stores/UserStore.ts index b8573be..99df644 100644 --- a/ui/src/stores/UserStore.js +++ b/ui/src/stores/UserStore.ts @@ -1,21 +1,22 @@ import {EventEmitter} from 'events'; -import dispatcher from './dispatcher'; +import dispatcher, {IEvent} from './dispatcher'; class UserStore extends EventEmitter { - constructor() { - super(); - this.users = []; - } + private users: IUser[] = []; - get() { + public get(): IUser[] { return this.users; } - getById(id) { - return this.users.find((app) => app.id === id); + public getById(id: number): IUser { + const user = this.users.find((u) => u.id === id); + if (!user) { + throw new Error('user must exist'); + } + return user; } - handle(data) { + public handle(data: IEvent): void { if (data.type === 'UPDATE_USERS') { this.users = data.payload; this.emit('change'); diff --git a/ui/src/stores/dispatcher.js b/ui/src/stores/dispatcher.js deleted file mode 100644 index 5d1ad2b..0000000 --- a/ui/src/stores/dispatcher.js +++ /dev/null @@ -1,3 +0,0 @@ -import {Dispatcher} from 'flux'; - -export default new Dispatcher(); diff --git a/ui/src/stores/dispatcher.ts b/ui/src/stores/dispatcher.ts new file mode 100644 index 0000000..da16c2f --- /dev/null +++ b/ui/src/stores/dispatcher.ts @@ -0,0 +1,8 @@ +import {Dispatcher} from 'flux'; + +export interface IEvent { + type: string + payload?: any +} + +export default new Dispatcher();