diff --git a/src/components/Header.tsx b/src/components/Header.tsx index abc054a621..f08a6ff205 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -6,6 +6,7 @@ import languages from "../i18n/languages"; import opencastLogo from "../img/opencast-white.svg?url"; import { setSpecificServiceFilter } from "../slices/tableFilterSlice"; import { getErrorCount, getHealthStatus } from "../selectors/healthSelectors"; +import { getRegistration } from "../selectors/registrationSelectors"; import { getOrgProperties, getUserInformation, @@ -19,6 +20,9 @@ import HotKeyCheatSheet from "./shared/HotKeyCheatSheet"; import { useHotkeys } from "react-hotkeys-hook"; import { useAppDispatch, useAppSelector } from "../store"; import { HealthStatus, fetchHealthStatus } from "../slices/healthSlice"; +import { + fetchRegistration, +} from "../slices/registrationSlice"; import { UserInfoState } from "../slices/userInfoSlice"; import { Tooltip } from "./shared/Tooltip"; import { HiOutlineTranslate } from "react-icons/hi"; @@ -51,6 +55,7 @@ const Header = () => { const healthStatus = useAppSelector(state => getHealthStatus(state)); const errorCounter = useAppSelector(state => getErrorCount(state)); const user = useAppSelector(state => getUserInformation(state)); + const registration = useAppSelector(state => getRegistration(state)); const orgProperties = useAppSelector(state => getOrgProperties(state)); const displayTerms = (orgProperties["org.opencastproject.admin.display_terms"] || "false").toLowerCase() === "true"; @@ -58,6 +63,10 @@ const Header = () => { await dispatch(fetchHealthStatus()); }; + useEffect(() => { + dispatch(fetchRegistration()); + }, [dispatch]); + const hideMenuHelp = () => { setMenuHelp(false); }; @@ -134,18 +143,18 @@ const Header = () => { }, []); useEffect(() => { - if (!user) { return; } - - const isAdmin = user.isAdmin || user.isOrgAdmin; - const isLocalhost = window.location.hostname === "localhost"; - const lastDismissed = localStorage.getItem("adopterModalDismissed"); - const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000; - const dismissedLongEnough = !lastDismissed || Date.now() - parseInt(lastDismissed) > THIRTY_DAYS; - - if (isAdmin && !isLocalhost && dismissedLongEnough) { - showRegistrationModal(); - } - }, [user]); + if (!user) { return; } + + const isAdmin = user.isAdmin || user.isOrgAdmin; + const isLocalhost = window.location.hostname === "localhost"; + const lastDismissed = localStorage.getItem("adopterModalDismissed"); + const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000; + const dismissedLongEnough = !lastDismissed || Date.now() - parseInt(lastDismissed) > THIRTY_DAYS; + + if (isAdmin && !isLocalhost && dismissedLongEnough && registration == null) { + showRegistrationModal(); + } + }, [user, registration]); return ( <>
diff --git a/src/selectors/registrationSelectors.ts b/src/selectors/registrationSelectors.ts new file mode 100644 index 0000000000..f6d9354405 --- /dev/null +++ b/src/selectors/registrationSelectors.ts @@ -0,0 +1,7 @@ +import { RootState } from "../store"; + +/** + * This file contains selectors regarding information about the registration status + */ +// Are we registered at all +export const getRegistration = (state: RootState) => state.registration.registration; diff --git a/src/slices/registrationSlice.ts b/src/slices/registrationSlice.ts new file mode 100644 index 0000000000..faee20d637 --- /dev/null +++ b/src/slices/registrationSlice.ts @@ -0,0 +1,52 @@ +import { PayloadAction, createSlice } from "@reduxjs/toolkit"; +import axios from "axios"; +import { createAppAsyncThunk } from "../createAsyncThunkWithTypes"; + +export type Registration = { + agreedToPolicy: boolean, + registered: boolean, + termsVersionAgreed: string, +} + +export type RegistrationState = { + registration: boolean | null, + error: boolean +}; + +// Initial state of health status in redux store +const initialState: RegistrationState = { + registration: null, + error: false, +}; + +// This is the registration itself +export const fetchRegistration = createAppAsyncThunk("registration/fetchRegistration", async () => { + const res = await axios.get("/admin-ng/adopter/registration"); + return res.data; +}); + +const registrationSlice = createSlice({ + name: "registration", + initialState, + reducers: { + setError(state, action: PayloadAction<{ + error: RegistrationState["error"], + }>) { + state.error = action.payload.error; + }, + }, + // These are used for thunks + extraReducers: builder => { + builder + .addCase(fetchRegistration.fulfilled, (state, _action: PayloadAction< + Registration + >) => { + state.registration = true; + }); + }, +}); + +export const { setError } = registrationSlice.actions; + +// Export the slice reducer as the default export +export default registrationSlice.reducer; diff --git a/src/store.ts b/src/store.ts index 76a3f65d9f..0558b56bb1 100644 --- a/src/store.ts +++ b/src/store.ts @@ -15,6 +15,7 @@ import groups from "./slices/groupSlice"; import acls from "./slices/aclSlice"; import themes from "./slices/themeSlice"; import health from "./slices/healthSlice"; +import registration from "./slices/registrationSlice"; import notifications from "./slices/notificationSlice"; import workflows from "./slices/workflowSlice"; import eventDetails from "./slices/eventDetailsSlice"; @@ -64,6 +65,7 @@ const reducers = combineReducers({ acls: persistReducer(aclsPersistConfig, acls), themes: persistReducer(themesPersistConfig, themes), health, + registration, notifications, workflows, eventDetails,