Skip to content

Commit

Permalink
refactor: use of process storage
Browse files Browse the repository at this point in the history
  • Loading branch information
hel-axelor authored and lme-axelor committed May 30, 2024
1 parent 2459198 commit 8b55887
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const ProccessHistoryCard = ({
const I18n = useTranslator();

const borderStyle = useMemo(() => {
return getStyles(ProcessHistory.getStatusColor(status, Colors).background)
return getStyles(ProcessHistory.getStatusColor(status, Colors)?.background)
?.border;
}, [Colors, status]);

Expand All @@ -72,6 +72,7 @@ const ProccessHistoryCard = ({
iconName: 'calendar-event',
indicatorText: I18n.t('User_ProcessHistory_StartedOn'),
displayText: formatDateTime(startedDate, I18n.t('Base_DateFormat')),
hideIf: startedDate == null,
},
{
iconName: 'calendar-check',
Expand Down
16 changes: 11 additions & 5 deletions packages/core/src/auth/screens/ProcessHistoryListScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import React, {useCallback, useEffect, useState} from 'react';
import {StyleSheet} from 'react-native';
import {
Expand All @@ -32,13 +33,14 @@ import {useTranslator} from '../../i18n';
import {ProcessHistory} from '../types';
import {isToday} from './process-history-list-screen-helper';

// TODO: add refresh method to the list
const ProcessHistoryListScreen = () => {
const I18n = useTranslator();
const Colors = useThemeColor();

const {allProcessList} = useLoaderListener();
const {processList} = useLoaderListener();

const [filteredList, setFilteredList] = useState(allProcessList);
const [filteredList, setFilteredList] = useState(processList);
const [today, setToday] = useState(true);
const [selectedStatus, setSelectedStatus] = useState([]);

Expand All @@ -48,7 +50,11 @@ const ProcessHistoryListScreen = () => {
return [];
} else {
if (today) {
return list?.filter(item => isToday(item?.startedDate));
return list?.filter(
item =>
isToday(item?.startedDate) ||
item?.status === ProcessStatus.PENDING,
);
} else {
return list;
}
Expand All @@ -65,8 +71,8 @@ const ProcessHistoryListScreen = () => {
);

useEffect(() => {
setFilteredList(filterOnStatus(filterOnDate(allProcessList)));
}, [filterOnDate, filterOnStatus, allProcessList]);
setFilteredList(filterOnStatus(filterOnDate(processList)));
}, [filterOnDate, filterOnStatus, processList]);

return (
<Screen removeSpaceOnTop={true}>
Expand Down
27 changes: 15 additions & 12 deletions packages/core/src/auth/storage/ProcessStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,35 @@

import {Storage, storage} from '../../storage/Storage';
import {ProcessItem} from '../../components';
import {deserialize, serialize} from './process-storage.helper';

const PROCESS_HISTORY_KEY = 'PROCESS_HISTORY';

class ProcessStorage {
private processList: ProcessItem[];
private refreshCallBack: (sessionList: ProcessItem[]) => void;
private refreshCallBack: (processList: ProcessItem[]) => void;

constructor(private localStorage: Storage) {
this.processList = [];
}

private updateState() {
if (this.refreshCallBack == null) {
return;
}

this.refreshCallBack(this.processList);
}

register(callBack) {
this.refreshCallBack = callBack;
}

getProcessList(): ProcessItem[] {
if (this.processList == null || this.processList.length === 0) {
this.processList = this.localStorage.getItem(PROCESS_HISTORY_KEY) || [];
this.processList = deserialize(
this.localStorage.getItem(PROCESS_HISTORY_KEY),
);
}
return this.processList;
}
Expand All @@ -45,7 +56,7 @@ class ProcessStorage {
return;
}

if (this.processList.find(_item => _item.key === process.key)) {
if (this.processList.find(_item => _item.key === process.key) != null) {
this.processList = this.processList.map(_item => {
if (_item.key === process.key) {
return {..._item, ...process};
Expand All @@ -57,7 +68,7 @@ class ProcessStorage {
this.processList.push(process);
}

this.localStorage.setItem(PROCESS_HISTORY_KEY, this.processList);
this.localStorage.setItem(PROCESS_HISTORY_KEY, serialize(this.processList));
this.updateState();
}

Expand All @@ -77,14 +88,6 @@ class ProcessStorage {
this.localStorage.setItem(PROCESS_HISTORY_KEY, processList);
this.updateState();
}

private updateState() {
if (this.refreshCallBack == null) {
return;
}

this.refreshCallBack(this.processList);
}
}

export const processStorage = new ProcessStorage(storage);
66 changes: 66 additions & 0 deletions packages/core/src/auth/storage/process-storage.helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Axelor Business Solutions
*
* Copyright (C) 2024 Axelor (<http://axelor.com>).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import {ProcessItem} from '../../components';

const serializeItem = (p: ProcessItem) => {
return JSON.stringify(p, (k, v) => {
if (typeof v === 'function') {
return v.toString();
}

return v;
});
};

const deserializeItem = (data: string) => {
return JSON.parse(data, (k, v) => {
if (typeof v === 'string') {
if (v.startsWith('function') || v.startsWith('() =>')) {
// eslint-disable-next-line no-eval
return eval(`(${v})`);
}
}
return v;
});
};

export const serialize = (list: ProcessItem[]): string => {
if (!Array.isArray(list) || list.length === 0) {
return '[]';
}

return JSON.stringify(list.map(p => serializeItem(p)));
};

export const deserialize = (data: string) => {
if (data == null || data.length === 0) {
return [];
}

try {
if (!Array.isArray(data)) {
return [];
}

return data.map(i => deserializeItem(i));
} catch (e) {
console.error('Deserialization error:', e);
return [];
}
};
2 changes: 2 additions & 0 deletions packages/core/src/auth/types/process-history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ class ProcessHistory {
Colors: ThemeColors,
): Color => {
switch (status) {
case ProcessStatus.PENDING:
return Colors.plannedColor;
case ProcessStatus.RUNNING:
return Colors.progressColor;
case ProcessStatus.COMPLETED:
Expand Down
62 changes: 27 additions & 35 deletions packages/core/src/components/templates/Loader/ProcessProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
Event,
} from './types';

//TODO: resolve toast press
class ProcessProvider {
private _events: Map<string, Event>;
private _processMap: Map<string, ProcessItem>;
Expand All @@ -40,7 +41,8 @@ class ProcessProvider {
this._processMap = new Map(this._processList.map(p => [p.key, p]));
this._numberRunningProcess = 0;

processStorage.register(this.setProcessList);
this.onCompleted = this.onCompleted.bind(this);
this.onFailed = this.onFailed.bind(this);
}

get numberRunningProcess() {
Expand All @@ -51,14 +53,6 @@ class ProcessProvider {
return this._processList.filter(p => p.completed && !p.resolved).length;
}

get allProcessList() {
return this._processList;
}

setProcessList(processList: ProcessItem[]) {
this._processList = processList;
}

on(key: string, e: EventType, c: callBack) {
const event = this._events.has(key) ? this._events.get(key) : ({} as Event);

Expand Down Expand Up @@ -87,7 +81,7 @@ class ProcessProvider {
failedDate: null,
notifyMe: false,
response: null,
status: null,
status: ProcessStatus.PENDING,
completed: false,
resolved: false,
};
Expand Down Expand Up @@ -118,8 +112,8 @@ class ProcessProvider {
throw new Error(`Process with key ${p.key} not found.`);
}

this.onStart(p);
this.executeProcess(p, I18n);
const _p = this.onStart(p);
this.executeProcess(_p, I18n);
}

private onFinish(
Expand All @@ -136,15 +130,15 @@ class ProcessProvider {
completed: true,
completedDate:
status === ProcessStatus.COMPLETED && new Date().toISOString(),
failedDate: status === ProcessStatus.FAILED && new Date().toISOString(),
failedDate:
status === ProcessStatus.FAILED ? new Date().toISOString() : null,
};

this._processMap.set(p.key, _p);
processStorage.saveProcess(_p);

this.decrementNumberOfRunningProcess();
this.resolveProcess(p.key);
this.resolveProcess(_p);

this._processMap.set(p.key, _p);
processStorage.saveProcess(_p);
this.emit(
p.key,
status === ProcessStatus.COMPLETED
Expand All @@ -157,8 +151,10 @@ class ProcessProvider {

private onCompleted(p: ProcessItem, I18n: TranslatorProps) {
const {notifyMe, response, disabled, onSuccess} = p;

if (!notifyMe) {
this.onFinishCallBack(p.key, onSuccess, response);
onSuccess(response);
this.resolveProcess(p);
} else {
showToastMessage({
type: 'success',
Expand All @@ -169,16 +165,17 @@ class ProcessProvider {
typeof response === 'string'
? response
: I18n.t('Base_Loader_ProccessSuccessMessage'),
onPress: () =>
!disabled && this.onFinishCallBack(p.key, onSuccess, response),
onPress: () => !disabled && onSuccess(response),
});
}
}

private onFailed(p: ProcessItem, I18n: TranslatorProps) {
const {notifyMe, response, disabled, onError} = p;

if (!notifyMe) {
this.onFinishCallBack(p.key, onError, response);
onError(response);
this.resolveProcess(p);
} else {
showToastMessage({
type: 'error',
Expand All @@ -189,8 +186,7 @@ class ProcessProvider {
typeof response === 'string'
? response
: I18n.t('Base_Loader_ProccessErrorMessage'),
onPress: () =>
!disabled && this.onFinishCallBack(p.key, onError, response),
onPress: () => !disabled && onError(response),
});
}
}
Expand All @@ -203,27 +199,21 @@ class ProcessProvider {
this._numberRunningProcess = Math.max(0, this._numberRunningProcess - 1);
}

private resolveProcess(key: string) {
if (!this._processMap.has(key)) {
throw new Error(`Process with key ${key} not found.`);
private resolveProcess(p: ProcessItem) {
if (!this._processMap.has(p?.key)) {
throw new Error(`Process with key ${p?.key} not found.`);
}

const p = this._processMap.get(key);
if (!p.completed) {
throw new Error(`Could not resolve uncompleted process ${key}`);
throw new Error(`Could not resolve uncompleted process ${p.key}`);
}

const _p = {...p, resolved: true};
this._processMap.set(key, _p);
this._processMap.set(p.key, _p);
processStorage.saveProcess(_p);
}

private onFinishCallBack(key: string, cb: callBack, response: any) {
this.resolveProcess(key);
cb(response);
}

private onStart(p: ProcessItem) {
private onStart(p: ProcessItem): ProcessItem {
const _p = {
...p,
loading: true,
Expand All @@ -238,6 +228,8 @@ class ProcessProvider {
this.on(p.key, EventType.COMPLETED, this.onCompleted);
this.on(p.key, EventType.FAILED, this.onFailed);
this.emit(p.key, EventType.STARTED);

return _p;
}

private async executeProcess(p: ProcessItem, I18n: TranslatorProps) {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/components/templates/Loader/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type Event = {
};

export enum ProcessStatus {
PENDING = 'PENDING',
RUNNING = 'RUNNING',
COMPLETED = 'COMPLETED',
FAILED = 'FAILED',
Expand Down
Loading

0 comments on commit 8b55887

Please sign in to comment.