commit f952f892402cd14a9855f720fb28f7f6a72d44b2
parent db8aac076f6056cb69665d1d090adc629bc32892
Author: MTRNord <mtrnord1@gmail.com>
Date: Fri, 28 Apr 2023 23:38:46 +0200
feat: Add persistence
Diffstat:
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