cetirizine

An experimental matrix client written in reactjs utilizing tailwind and storybook
git clone git://archive.git.mtrnord.blog/MTRNord/cetirizine.git
Log | Files | Refs | README | LICENSE

commit f952f892402cd14a9855f720fb28f7f6a72d44b2
parent db8aac076f6056cb69665d1d090adc629bc32892
Author: MTRNord <mtrnord1@gmail.com>
Date:   Fri, 28 Apr 2023 23:38:46 +0200

feat: Add persistence

Diffstat:
Mpackage-lock.json | 35+++++++++++++++++++++++++++++++++++
Mpackage.json | 2++
Msrc/app/api/api.ts | 16+++++++++++++---
Msrc/app/store.ts | 53+++++++++++++++++++++++++++--------------------------
Msrc/components/login/login.tsx | 1-
Msrc/main.tsx | 8++++++--
6 files changed, 83 insertions(+), 32 deletions(-)

diff --git a/package-lock.json b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@lexical/react": "^0.10.0", + "@piotr-cz/redux-persist-idb-storage": "^1.1.3", "@reduxjs/toolkit": "^1.9.5", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -26,6 +27,7 @@ "react-intersection-observer": "^9.4.3", "react-redux": "^8.0.5", "react-router-dom": "^6.10.0", + "reduxjs-toolkit-persist": "^7.2.1", "typescript": "^5.0.2", "web-vitals": "^2.1.4" }, @@ -3082,6 +3084,17 @@ "node": ">= 8" } }, + "node_modules/@piotr-cz/redux-persist-idb-storage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@piotr-cz/redux-persist-idb-storage/-/redux-persist-idb-storage-1.1.3.tgz", + "integrity": "sha512-aAx40LFsPmg93SHKHUUHtSM50iEFp4uZJrvrkG0oT4yOLyjUAPEqLIcPrsYDuWJpyJrYt8T7SNieinQxp+boEQ==", + "dependencies": { + "idb": "^3.0.0" + }, + "peerDependencies": { + "redux-persist": ">=3.5.0" + } + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.5", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz", @@ -8979,6 +8992,11 @@ "postcss": "^8.1.0" } }, + "node_modules/idb": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/idb/-/idb-3.0.2.tgz", + "integrity": "sha512-+FLa/0sTXqyux0o6C+i2lOR0VoS60LU/jzUo5xjfY6+7sEEgy4Gz1O7yFBXvjd7N0NyIGWIRg8DcQSLEG+VSPw==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -12545,6 +12563,15 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/redux-persist": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "peer": true, + "peerDependencies": { + "redux": ">4.0.0" + } + }, "node_modules/redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", @@ -12553,6 +12580,14 @@ "redux": "^4" } }, + "node_modules/reduxjs-toolkit-persist": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/reduxjs-toolkit-persist/-/reduxjs-toolkit-persist-7.2.1.tgz", + "integrity": "sha512-1gu9Qid0qXlsM4a2D8eHx/YOwa7I0lgAXqM0KYQoX2RA+KMezkq5wkJKEMLoUkpqVuWYHRdU7auS2/jFVsR0gQ==", + "peerDependencies": { + "@reduxjs/toolkit": "^1.6.1" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", diff --git a/package.json b/package.json @@ -5,6 +5,7 @@ "type": "module", "dependencies": { "@lexical/react": "^0.10.0", + "@piotr-cz/redux-persist-idb-storage": "^1.1.3", "@reduxjs/toolkit": "^1.9.5", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -22,6 +23,7 @@ "react-intersection-observer": "^9.4.3", "react-redux": "^8.0.5", "react-router-dom": "^6.10.0", + "reduxjs-toolkit-persist": "^7.2.1", "typescript": "^5.0.2", "web-vitals": "^2.1.4" }, diff --git a/src/app/api/api.ts b/src/app/api/api.ts @@ -1,6 +1,6 @@ -import { BaseQueryFn, FetchArgs, FetchBaseQueryError, createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import { BaseQueryFn, FetchArgs, FetchBaseQueryError, createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react' import { RootState } from '../store'; -import { ILoginFlows, ILoginParams, ILoginResponse, IWellKnown } from './apiTypes'; +import { ILoginFlows, ILoginParams, ILoginResponse, IRateLimitError, IWellKnown } from './apiTypes'; import { createAction, createReducer } from '@reduxjs/toolkit'; // async function login(baseUrl: string, userId: string, password: string): Promise<MatrixClient> { // //const client = await initMatrixClient(baseUrl, userId, undefined, undefined, password); @@ -50,7 +50,17 @@ const baseQuery: BaseQueryFn<string | FetchArgs, export const matrixApi = createApi({ reducerPath: 'matrixApi', - baseQuery: baseQuery, + baseQuery: retry( + async (args: string | FetchArgs, api, extraOptions) => { + const result = await baseQuery(args, api, extraOptions); + if (result.error?.status === 429) { + const retryAfterMs = (result.error.data as IRateLimitError).retry_after_ms; + if (retryAfterMs) { + await new Promise((resolve) => setTimeout(resolve, retryAfterMs)); + } + } + return result; + }, { maxRetries: 3 }), tagTypes: ['Login', 'Flows', "WellKnown"], endpoints: (builder) => ({ getWellKnown: builder.query<IWellKnown, undefined>({ diff --git a/src/app/store.ts b/src/app/store.ts @@ -1,21 +1,41 @@ import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"; import { matrixApi, auth } from "./api/api"; import { setupListeners } from "@reduxjs/toolkit/dist/query/react"; +import { FLUSH, PAUSE, PERSIST, PURGE, REGISTER, REHYDRATE, persistCombineReducers, persistStore } from "reduxjs-toolkit-persist"; +import createIdbStorage from '@piotr-cz/redux-persist-idb-storage' -export const store = configureStore({ - reducer: { +const persistConfig = { + key: 'root', + version: 1, + storage: createIdbStorage({ name: 'cetirizine', storeName: 'cetirizine' }), + serialize: false, + deserialize: false, +} + +const persistedReducer = persistCombineReducers( + persistConfig, + { auth: auth, [matrixApi.reducerPath]: matrixApi.reducer, - }, + } +); + +export const store = configureStore({ + reducer: persistedReducer, middleware: (getDefaultMiddleware) => [ - ...getDefaultMiddleware(), + ...getDefaultMiddleware({ + serializableCheck: { + ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER], + }, + }), matrixApi.middleware, ], - //preloadedState: await getInitialState() }); setupListeners(store.dispatch) +export const persistor = persistStore(store); + export type AppDispatch = typeof store.dispatch; export type RootState = ReturnType<typeof store.getState>; export type AppThunk<ReturnType = void> = ThunkAction< @@ -23,23 +43,4 @@ export type AppThunk<ReturnType = void> = ThunkAction< RootState, unknown, Action<string> ->; - -async function getInitialState() { - const localStorage = window.localStorage; - const accessToken = localStorage.getItem("accessToken"); - const baseUrl = localStorage.getItem("baseUrl"); - const userId = localStorage.getItem("userId"); - const deviceId = localStorage.getItem("deviceId"); - if (accessToken && baseUrl && userId) { - //const client = await initMatrixClient(baseUrl, userId, deviceId!, accessToken, undefined, true); - return { - login: { - //client, - loginPending: false - } - } - } else { - return undefined - } -} -\ No newline at end of file +>; +\ No newline at end of file diff --git a/src/components/login/login.tsx b/src/components/login/login.tsx @@ -5,7 +5,6 @@ import Button from '../button/button'; import Header from '../header/header'; import Input from '../input/basic/input'; import { Navigate } from 'react-router-dom'; -import { IRateLimitError } from '../../app/api/apiTypes'; export function Login() { const [login, { isLoading: loginPending, isSuccess, error: loginErrorRaw }] = useDoLoginMutation(); diff --git a/src/main.tsx b/src/main.tsx @@ -2,9 +2,10 @@ import React from 'react' import { createRoot } from 'react-dom/client'; import { Provider } from 'react-redux'; -import { store } from './app/store'; +import { persistor, store } from './app/store'; import App from './App'; import './index.scss'; +import { PersistGate } from 'reduxjs-toolkit-persist/integration/react'; const container = document.getElementById('root')!; const root = createRoot(container); @@ -12,7 +13,10 @@ const root = createRoot(container); root.render( <React.StrictMode> <Provider store={store}> - <App /> + <PersistGate loading={<span>Loading...</span>} persistor={persistor}> + <App /> + </PersistGate> + </Provider> </React.StrictMode> ); \ No newline at end of file