Improved doc and typing, improved API connection handling

This commit is contained in:
Arnaud Vergnet 2020-07-01 13:14:17 +02:00
parent 3f14f7bb96
commit b813aa0b83
6 changed files with 138 additions and 76 deletions

View file

@ -1,3 +1,5 @@
// @flow
import i18n from "i18n-js"; import i18n from "i18n-js";
/** /**

View file

@ -1,14 +1,13 @@
// @flow // @flow
import type {Machine} from "../screens/Proxiwash/ProxiwashScreen";
/** /**
* Singleton class used to manage themes * Singleton class used to manage april fools
*/ */
export default class AprilFoolsManager { export default class AprilFoolsManager {
static instance: AprilFoolsManager | null = null; static instance: AprilFoolsManager | null = null;
aprilFoolsEnabled: boolean;
static fakeMachineNumber = [ static fakeMachineNumber = [
"", "",
"cos(ln(1))", "cos(ln(1))",
@ -24,6 +23,7 @@ export default class AprilFoolsManager {
"1×10¹+1×10⁰", "1×10¹+1×10⁰",
"Re(√192e^(iπ/6))", "Re(√192e^(iπ/6))",
]; ];
aprilFoolsEnabled: boolean;
constructor() { constructor() {
let today = new Date(); let today = new Date();
@ -40,7 +40,13 @@ export default class AprilFoolsManager {
AprilFoolsManager.instance; AprilFoolsManager.instance;
} }
static getFakeMenuItem(menu: Object) { /**
* Adds fake menu entries
*
* @param menu
* @returns {Object}
*/
static getFakeMenuItem(menu: Array<{dishes: Array<{name: string}>}>) {
menu[1]["dishes"].splice(4, 0, {name: "Coq au vin"}); menu[1]["dishes"].splice(4, 0, {name: "Coq au vin"});
menu[1]["dishes"].splice(2, 0, {name: "Bat'Soupe"}); menu[1]["dishes"].splice(2, 0, {name: "Bat'Soupe"});
menu[1]["dishes"].splice(1, 0, {name: "Pave de loup"}); menu[1]["dishes"].splice(1, 0, {name: "Pave de loup"});
@ -49,16 +55,26 @@ export default class AprilFoolsManager {
return menu; return menu;
} }
static getNewProxiwashDryerOrderedList(dryers: Array<Object>) { /**
if (dryers !== undefined) { * Changes proxiwash dryers order
*
* @param dryers
*/
static getNewProxiwashDryerOrderedList(dryers: Array<Machine> | null) {
if (dryers != null) {
let second = dryers[1]; let second = dryers[1];
dryers.splice(1, 1); dryers.splice(1, 1);
dryers.push(second); dryers.push(second);
} }
} }
static getNewProxiwashWasherOrderedList(washers: Array<Object>) { /**
if (washers !== undefined) { * Changes proxiwash washers order
*
* @param washers
*/
static getNewProxiwashWasherOrderedList(washers: Array<Machine> | null) {
if (washers != null) {
let first = washers[0]; let first = washers[0];
let second = washers[1]; let second = washers[1];
let fifth = washers[4]; let fifth = washers[4];
@ -67,14 +83,25 @@ export default class AprilFoolsManager {
washers.splice(4, 1, ninth); washers.splice(4, 1, ninth);
washers.splice(1, 1, first); washers.splice(1, 1, first);
washers.splice(0, 1, fifth); washers.splice(0, 1, fifth);
// washers.push(fifth);
} }
} }
/**
* Gets the new display number for the given machine number
*
* @param number
* @returns {string}
*/
static getProxiwashMachineDisplayNumber(number: number) { static getProxiwashMachineDisplayNumber(number: number) {
return AprilFoolsManager.fakeMachineNumber[number]; return AprilFoolsManager.fakeMachineNumber[number];
} }
/**
* Gets the new and ugly april fools theme
*
* @param currentTheme
* @returns {{colors: {textDisabled: string, agendaDayTextColor: string, surface: string, background: string, dividerBackground: string, accent: string, agendaBackgroundColor: string, tabIcon: string, card: string, primary: string}}}
*/
static getAprilFoolsTheme(currentTheme: Object) { static getAprilFoolsTheme(currentTheme: Object) {
return { return {
...currentTheme, ...currentTheme,

View file

@ -23,15 +23,13 @@ export default class ConnectionManager {
#email: string; #email: string;
#token: string | null; #token: string | null;
listeners: Array<Function>;
constructor() { constructor() {
this.#token = null; this.#token = null;
this.listeners = [];
} }
/** /**
* Get this class instance or create one if none is found * Gets this class instance or create one if none is found
*
* @returns {ConnectionManager} * @returns {ConnectionManager}
*/ */
static getInstance(): ConnectionManager { static getInstance(): ConnectionManager {
@ -40,21 +38,20 @@ export default class ConnectionManager {
ConnectionManager.instance; ConnectionManager.instance;
} }
getToken() { /**
* Gets the current token
*
* @returns {string | null}
*/
getToken(): string | null {
return this.#token; return this.#token;
} }
onLoginStateChange(newState: boolean) { /**
for (let i = 0; i < this.listeners.length; i++) { * Tries to recover login token from the secure keychain
if (this.listeners[i] !== undefined) *
this.listeners[i](newState); * @returns {Promise<R>}
} */
}
addLoginStateListener(listener: Function) {
this.listeners.push(listener);
}
async recoverLogin() { async recoverLogin() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (this.getToken() !== null) if (this.getToken() !== null)
@ -64,7 +61,6 @@ export default class ConnectionManager {
.then((data) => { .then((data) => {
if (data) { if (data) {
this.#token = data.password; this.#token = data.password;
this.onLoginStateChange(true);
resolve(this.#token); resolve(this.#token);
} else } else
reject(false); reject(false);
@ -76,17 +72,28 @@ export default class ConnectionManager {
}); });
} }
/**
* Check if the user has a valid token
*
* @returns {boolean}
*/
isLoggedIn() { isLoggedIn() {
return this.getToken() !== null; return this.getToken() !== null;
} }
/**
* Saves the login token in the secure keychain
*
* @param email
* @param token
* @returns {Promise<R>}
*/
async saveLogin(email: string, token: string) { async saveLogin(email: string, token: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Keychain.setInternetCredentials(SERVER_NAME, 'token', token) Keychain.setInternetCredentials(SERVER_NAME, 'token', token)
.then(() => { .then(() => {
this.#token = token; this.#token = token;
this.#email = email; this.#email = email;
this.onLoginStateChange(true);
resolve(true); resolve(true);
}) })
.catch(() => { .catch(() => {
@ -95,12 +102,16 @@ export default class ConnectionManager {
}); });
} }
/**
* Deletes the login token from the keychain
*
* @returns {Promise<R>}
*/
async disconnect() { async disconnect() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Keychain.resetInternetCredentials(SERVER_NAME) Keychain.resetInternetCredentials(SERVER_NAME)
.then(() => { .then(() => {
this.#token = null; this.#token = null;
this.onLoginStateChange(false);
resolve(true); resolve(true);
}) })
.catch(() => { .catch(() => {
@ -109,6 +120,16 @@ export default class ConnectionManager {
}); });
} }
/**
* Sends the given login and password to the api.
* If the combination is valid, the login token is received and saved in the secure keychain.
* If not, the promise is rejected with the corresponding error code.
*
* @param email
* @param password
* @returns {Promise<R>}
*/
async connect(email: string, password: string) { async connect(email: string, password: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const data = { const data = {
@ -117,6 +138,7 @@ export default class ConnectionManager {
}; };
apiRequest(AUTH_PATH, 'POST', data) apiRequest(AUTH_PATH, 'POST', data)
.then((response) => { .then((response) => {
if (this.isConnectionResponseValid(response)) {
this.saveLogin(email, response.token) this.saveLogin(email, response.token)
.then(() => { .then(() => {
resolve(true); resolve(true);
@ -124,12 +146,20 @@ export default class ConnectionManager {
.catch(() => { .catch(() => {
reject(ERROR_TYPE.TOKEN_SAVE); reject(ERROR_TYPE.TOKEN_SAVE);
}); });
} else
reject(ERROR_TYPE.SERVER_ERROR);
}) })
.catch((error) => reject(error)); .catch((error) => reject(error));
}); });
} }
isConnectionResponseValid(response: Object) { /**
* Checks if the API connection response is correctly formatted
*
* @param response
* @returns {boolean}
*/
isConnectionResponseValid(response: { [key: string]: any }) {
let valid = isResponseValid(response); let valid = isResponseValid(response);
if (valid && response.error === ERROR_TYPE.SUCCESS) if (valid && response.error === ERROR_TYPE.SUCCESS)
@ -140,6 +170,13 @@ export default class ConnectionManager {
return valid; return valid;
} }
/**
* Sends an authenticated request with the login token to the API
*
* @param path
* @param params
* @returns {Promise<R>}
*/
async authenticatedRequest(path: string, params: Object) { async authenticatedRequest(path: string, params: Object) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (this.getToken() !== null) { if (this.getToken() !== null) {

View file

@ -45,6 +45,10 @@ export default class DateManager {
DateManager.instance; DateManager.instance;
} }
static isWeekend(date: Date) {
return date.getDay() === 6 || date.getDay() === 0;
}
/** /**
* Gets a translated string representing the given date. * Gets a translated string representing the given date.
* *
@ -58,8 +62,4 @@ export default class DateManager {
return this.daysOfWeek[date.getDay()] + " " + date.getDate() + " " + this.monthsOfYear[date.getMonth()] + " " + date.getFullYear(); return this.daysOfWeek[date.getDay()] + " " + date.getDate() + " " + this.monthsOfYear[date.getMonth()] + " " + date.getFullYear();
} }
static isWeekend(date: Date) {
return date.getDay() === 6 || date.getDay() === 0;
}
} }

View file

@ -19,8 +19,4 @@ export default class LocaleManager {
i18n.translations = {fr, en}; i18n.translations = {fr, en};
i18n.locale = RNLocalize.findBestAvailableLanguage(["en", "fr"]).languageTag; i18n.locale = RNLocalize.findBestAvailableLanguage(["en", "fr"]).languageTag;
} }
static getCurrentLocale() {
return RNLocalize.findBestAvailableLanguage(["en", "fr"]).languageTag;
}
} }