651 calendar auto hide upon loads (#667)
* [#651] fix duplicated initialisatoin at login * [#651] prevent event overwrite when getting calendars list Co-authored-by: Camille Moussu <cmoussu@linagora.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import * as appHooks from "@/app/hooks";
|
import * as appHooks from "@/app/hooks";
|
||||||
import { AppDispatch } from "@/app/store";
|
import { AppDispatch, setupStore } from "@/app/store";
|
||||||
import HandleLogin from "@/features/User/HandleLogin";
|
import HandleLogin from "@/features/User/HandleLogin";
|
||||||
import * as oidcAuth from "@/features/User/oidcAuth";
|
import * as oidcAuth from "@/features/User/oidcAuth";
|
||||||
import { clientConfig } from "@/features/User/oidcAuth";
|
import { clientConfig } from "@/features/User/oidcAuth";
|
||||||
|
import { useInitializeApp } from "@/features/User/useInitializeApp";
|
||||||
import * as apiUtils from "@/utils/apiUtils";
|
import * as apiUtils from "@/utils/apiUtils";
|
||||||
import { screen, waitFor } from "@testing-library/react";
|
import { renderHook, screen, waitFor } from "@testing-library/react";
|
||||||
|
import { Provider } from "react-redux";
|
||||||
import { push } from "redux-first-history";
|
import { push } from "redux-first-history";
|
||||||
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
import { renderWithProviders } from "../../utils/Renderwithproviders";
|
||||||
|
|
||||||
@@ -17,6 +19,17 @@ describe("HandleLogin", () => {
|
|||||||
const dispatch = jest.fn() as AppDispatch;
|
const dispatch = jest.fn() as AppDispatch;
|
||||||
jest.spyOn(appHooks, "useAppDispatch").mockReturnValue(dispatch);
|
jest.spyOn(appHooks, "useAppDispatch").mockReturnValue(dispatch);
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
|
Object.defineProperty(window, "matchMedia", {
|
||||||
|
writable: true,
|
||||||
|
value: jest.fn().mockImplementation((query) => ({
|
||||||
|
matches: false,
|
||||||
|
media: query,
|
||||||
|
onchange: null,
|
||||||
|
addEventListener: jest.fn(),
|
||||||
|
removeEventListener: jest.fn(),
|
||||||
|
dispatchEvent: jest.fn(),
|
||||||
|
})),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("redirects and sets sessionStorage when no userData", async () => {
|
test("redirects and sets sessionStorage when no userData", async () => {
|
||||||
@@ -28,18 +41,23 @@ describe("HandleLogin", () => {
|
|||||||
|
|
||||||
jest.spyOn(oidcAuth, "Auth").mockResolvedValue(loginUrlMock);
|
jest.spyOn(oidcAuth, "Auth").mockResolvedValue(loginUrlMock);
|
||||||
|
|
||||||
renderWithProviders(<HandleLogin />, {
|
const { result } = renderHook(() => useInitializeApp(), {
|
||||||
user: {
|
wrapper: ({ children }) => (
|
||||||
userData: null,
|
<Provider
|
||||||
tokens: null,
|
store={setupStore({
|
||||||
loading: false,
|
user: {
|
||||||
error: null,
|
userData: null,
|
||||||
},
|
tokens: null,
|
||||||
calendars: {
|
loading: false,
|
||||||
list: {},
|
error: null,
|
||||||
pending: false,
|
coreConfig: { language: "en" },
|
||||||
error: null,
|
},
|
||||||
},
|
calendars: { list: {}, pending: false, error: null },
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Provider>
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitFor(
|
await waitFor(
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Loading } from "./components/Loading/Loading";
|
|||||||
import { AVAILABLE_LANGUAGES } from "./features/Settings/constants";
|
import { AVAILABLE_LANGUAGES } from "./features/Settings/constants";
|
||||||
import HandleLogin from "./features/User/HandleLogin";
|
import HandleLogin from "./features/User/HandleLogin";
|
||||||
import { CallbackResume } from "./features/User/LoginCallback";
|
import { CallbackResume } from "./features/User/LoginCallback";
|
||||||
|
import { useInitializeApp } from "./features/User/useInitializeApp";
|
||||||
import { ScreenTooSmall } from "./ScreenTooSmall";
|
import { ScreenTooSmall } from "./ScreenTooSmall";
|
||||||
import { WebSocketGate } from "./websocket/WebSocketGate";
|
import { WebSocketGate } from "./websocket/WebSocketGate";
|
||||||
|
|
||||||
@@ -68,6 +69,8 @@ function App() {
|
|||||||
() => window.matchMedia(SMALL_SCREEN_QUERY).matches
|
() => window.matchMedia(SMALL_SCREEN_QUERY).matches
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useInitializeApp();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mediaQuery = window.matchMedia(SMALL_SCREEN_QUERY);
|
const mediaQuery = window.matchMedia(SMALL_SCREEN_QUERY);
|
||||||
const onChange = (event: MediaQueryListEvent) =>
|
const onChange = (event: MediaQueryListEvent) =>
|
||||||
|
|||||||
@@ -126,10 +126,25 @@ const CalendarSlice = createSlice({
|
|||||||
}>
|
}>
|
||||||
) => {
|
) => {
|
||||||
state.pending = false;
|
state.pending = false;
|
||||||
state.list = action.payload.importedCalendars;
|
|
||||||
state.error = action.payload.errors.length
|
state.error = action.payload.errors.length
|
||||||
? action.payload.errors
|
? action.payload.errors
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
Object.entries(action.payload.importedCalendars).forEach(
|
||||||
|
([id, cal]) => {
|
||||||
|
state.list[id] = {
|
||||||
|
...cal,
|
||||||
|
events: state.list[id]?.events || {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Remove calendars that no longer exist
|
||||||
|
Object.keys(state.list).forEach((id) => {
|
||||||
|
if (!action.payload.importedCalendars[id]) {
|
||||||
|
delete state.list[id];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.addCase(
|
.addCase(
|
||||||
|
|||||||
@@ -1,60 +1,14 @@
|
|||||||
import { useAppDispatch, useAppSelector } from "@/app/hooks";
|
import { useAppDispatch, useAppSelector } from "@/app/hooks";
|
||||||
import { redirectTo } from "@/utils/apiUtils";
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { push } from "redux-first-history";
|
import { push } from "redux-first-history";
|
||||||
import { getCalendarsListAsync } from "../Calendars/services";
|
|
||||||
import { Auth } from "./oidcAuth";
|
|
||||||
import { getOpenPaasUserDataAsync, setTokens, setUserData } from "./userSlice";
|
|
||||||
import { setAppLoading } from "@/app/loadingSlice";
|
import { setAppLoading } from "@/app/loadingSlice";
|
||||||
|
|
||||||
export function HandleLogin() {
|
export function HandleLogin() {
|
||||||
const userData = useAppSelector((state) => state.user);
|
const userData = useAppSelector((state) => state.user);
|
||||||
const calendars = useAppSelector((state) => state.calendars);
|
const calendars = useAppSelector((state) => state.calendars);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const hasInitiatedRef = useRef(false);
|
|
||||||
const hasNavigatedRef = useRef(false);
|
const hasNavigatedRef = useRef(false);
|
||||||
|
|
||||||
// Initiate login or load saved data
|
|
||||||
useEffect(() => {
|
|
||||||
if (hasInitiatedRef.current) return;
|
|
||||||
if (userData.userData && !calendars.pending) return;
|
|
||||||
|
|
||||||
hasInitiatedRef.current = true;
|
|
||||||
|
|
||||||
const initiateLogin = async () => {
|
|
||||||
const savedToken = sessionStorage.getItem("tokenSet")
|
|
||||||
? JSON.parse(sessionStorage.getItem("tokenSet")!)
|
|
||||||
: null;
|
|
||||||
const savedUser = sessionStorage.getItem("userData")
|
|
||||||
? JSON.parse(sessionStorage.getItem("userData")!)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (savedToken && savedUser) {
|
|
||||||
dispatch(setAppLoading(true));
|
|
||||||
dispatch(setTokens(savedToken));
|
|
||||||
dispatch(setUserData(savedUser));
|
|
||||||
await dispatch(getOpenPaasUserDataAsync());
|
|
||||||
await dispatch(getCalendarsListAsync());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginurl = await Auth();
|
|
||||||
|
|
||||||
sessionStorage.setItem(
|
|
||||||
"redirectState",
|
|
||||||
JSON.stringify({
|
|
||||||
code_verifier: loginurl.code_verifier,
|
|
||||||
state: loginurl.state,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
redirectTo(loginurl.redirectTo);
|
|
||||||
};
|
|
||||||
|
|
||||||
initiateLogin();
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [userData.userData, calendars.list, dispatch]);
|
|
||||||
|
|
||||||
// Navigate to /calendar only when all data is ready
|
// Navigate to /calendar only when all data is ready
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasNavigatedRef.current) return;
|
if (hasNavigatedRef.current) return;
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { useAppDispatch, useAppSelector } from "@/app/hooks";
|
||||||
|
import { setAppLoading } from "@/app/loadingSlice";
|
||||||
|
import { getCalendarsListAsync } from "@/features/Calendars/services";
|
||||||
|
import { Auth } from "@/features/User/oidcAuth";
|
||||||
|
import {
|
||||||
|
getOpenPaasUserDataAsync,
|
||||||
|
setTokens,
|
||||||
|
setUserData,
|
||||||
|
} from "@/features/User/userSlice";
|
||||||
|
import { redirectTo } from "@/utils/apiUtils";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
export function useInitializeApp() {
|
||||||
|
const userData = useAppSelector((state) => state.user);
|
||||||
|
const calendars = useAppSelector((state) => state.calendars);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const hasInitiatedRef = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasInitiatedRef.current) return;
|
||||||
|
if (userData.userData && !calendars.pending) return;
|
||||||
|
if (window.location.pathname === "/callback") return;
|
||||||
|
hasInitiatedRef.current = true;
|
||||||
|
|
||||||
|
const initiateLogin = async () => {
|
||||||
|
const savedToken = sessionStorage.getItem("tokenSet")
|
||||||
|
? JSON.parse(sessionStorage.getItem("tokenSet")!)
|
||||||
|
: null;
|
||||||
|
const savedUser = sessionStorage.getItem("userData")
|
||||||
|
? JSON.parse(sessionStorage.getItem("userData")!)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (savedToken && savedUser) {
|
||||||
|
dispatch(setAppLoading(true));
|
||||||
|
dispatch(setTokens(savedToken));
|
||||||
|
dispatch(setUserData(savedUser));
|
||||||
|
try {
|
||||||
|
await dispatch(getOpenPaasUserDataAsync());
|
||||||
|
await dispatch(getCalendarsListAsync());
|
||||||
|
} finally {
|
||||||
|
dispatch(setAppLoading(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginurl = await Auth();
|
||||||
|
sessionStorage.setItem(
|
||||||
|
"redirectState",
|
||||||
|
JSON.stringify({
|
||||||
|
code_verifier: loginurl.code_verifier,
|
||||||
|
state: loginurl.state,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
redirectTo(loginurl.redirectTo);
|
||||||
|
};
|
||||||
|
|
||||||
|
initiateLogin();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [userData.userData]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user