= rawUseSelector;
+
+export const resetPersistedData = async () => persistor.purge();
diff --git a/src/core/user.js b/src/core/user.js
deleted file mode 100644
index 898819b937..0000000000
--- a/src/core/user.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * A simple collection of functionality in regards to the current user.
- *
- * @class User
- */
-import { hasToken } from "./token";
-import { store, persistor } from "./store";
-import { updateStatus, attemptAuthentication } from "./user.slice";
-
-const selectStatus = (state) => state.user.status;
-
-class User {
- /**
- * Used to keep track if we started attempting in this page view.
- *
- * @static
- */
- static #attemptingThisRequest = false;
-
- /**
- * Returns whether a user is authenticated of not.
- *
- * @static
- * @returns {boolean}
- * @memberof User
- */
- static isAuthenticated() {
- store.dispatch(
- updateStatus({
- hasToken: hasToken("user"),
- doFail: !User.#attemptingThisRequest
- })
- );
- return selectStatus(store.getState()) === "authenticated";
- }
-
- /**
- * Redirect to login.
- *
- * @param {string} loginUrl the URL to redirect to.
- */
- static authenticate(loginUrl) {
- // Switch state to attempting and flush state to session storage
- // before redirecting.
- store.dispatch(attemptAuthentication()).then(() => persistor.flush());
- User.#attemptingThisRequest = true;
- window.location.href = loginUrl;
- }
-
- /**
- * Whether authentication failed.
- *
- * Will return true if we just tried authenticating and it failed.
- *
- * @returns {boolean}
- */
- static authenticationFailed() {
- // isAuthenticated() will ensure state is up to date.
- return (
- !this.isAuthenticated() && selectStatus(store.getState()) === "failed"
- );
- }
-}
-
-export default User;
diff --git a/src/core/user.slice.js b/src/core/user.slice.js
deleted file mode 100644
index f6649d3524..0000000000
--- a/src/core/user.slice.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
-
-// This thunk doesn't actually do anything but resolve straight away,
-// but this allows us to chain a `.then()` on the action on the caller
-// side.
-export const attemptAuthentication = createAsyncThunk(
- "user/attemptAuthentication",
- () => Promise.resolve()
-);
-
-const userSlice = createSlice({
- name: "user",
- initialState: { status: "unauthenticated" },
- reducers: {
- updateStatus(state, action) {
- if (state.status === "unauthenticated" || state.status === "attempting") {
- if (action.payload.hasToken) {
- state.status = "authenticated";
- } else if (action.payload.doFail && state.status === "attempting") {
- state.status = "failed";
- }
- }
- },
- setStatusAuthenticated(state) {
- state.status = "authenticated";
- },
- setStatusUnauthenticated(state) {
- state.status = "unauthenticated";
- }
- },
- extraReducers: {
- [attemptAuthentication.pending]: (state) => {
- state.status = "attempting";
- }
- }
-});
-
-export const {
- updateStatus,
- setStatusAuthenticated,
- setStatusUnauthenticated
-} = userSlice.actions;
-
-export default userSlice.reducer;
diff --git a/src/core/utils/helpers/usePatronData.ts b/src/core/utils/helpers/usePatronData.ts
index c1ff3fd30f..994bc53dd4 100644
--- a/src/core/utils/helpers/usePatronData.ts
+++ b/src/core/utils/helpers/usePatronData.ts
@@ -1,4 +1,6 @@
import { useGetPatronInformationByPatronIdV2 } from "../../fbs/fbs";
+import BlockedTypes from "../types/BlockedTypes";
+import { Patron } from "../types/entities";
import { isAnonymous } from "./user";
export const usePatronData = () =>
@@ -6,4 +8,14 @@ export const usePatronData = () =>
enabled: !isAnonymous()
});
+export const getBlockedStatus = (patron?: Patron) => {
+ // TODO: Investigate if we need to consider multiple
+ // block statuses and not only the first.
+ if (patron?.blockStatus && patron?.blockStatus?.length > 0) {
+ return patron.blockStatus[0].blockedReason as BlockedTypes;
+ }
+ // We cannot resolve the block status so we return unknown
+ return BlockedTypes.unknown;
+};
+
export default {};
diff --git a/src/core/utils/types/BlockedTypes.ts b/src/core/utils/types/BlockedTypes.ts
index a05cf5165c..33e60b0fa5 100644
--- a/src/core/utils/types/BlockedTypes.ts
+++ b/src/core/utils/types/BlockedTypes.ts
@@ -1,11 +1,24 @@
enum BlockedTypes {
- extendedExclusion = "F", // Extended exclusion
- deceased = "D", // Deceased
- fee = "E", // Patron has fee
- selfcreated = "W", // Self created at website
- stolen = "O", // library card stolen
- exclusion = "U", // Exclusion
- automatonBlocked = "S" // Blocked by self service automaton
+ // Extended suspension
+ extendedSuspension = "F",
+ // Deceased
+ deceased = "D",
+ // Patron has fee
+ fee = "E",
+
+ // The user has not been assigned a correct patron category.
+ // This should be pretty rare.
+ // In Danish this is also called "Selvoprettet på web"
+ missingPatronCategory = "W",
+
+ // library card stolen
+ accountStolen = "O",
+ // Suspension
+ suspension = "U",
+ // Blocked by self service
+ blockedFromSelfservice = "S",
+ // A fallback for unknown types
+ unknown = "unknown"
}
export default BlockedTypes;
diff --git a/src/core/utils/withIsPatronBlockedHoc.tsx b/src/core/utils/withIsPatronBlockedHoc.tsx
index d402d5910f..d306f642bb 100644
--- a/src/core/utils/withIsPatronBlockedHoc.tsx
+++ b/src/core/utils/withIsPatronBlockedHoc.tsx
@@ -1,14 +1,12 @@
import React, { useEffect, useState, ComponentType, FC } from "react";
-import { useDispatch } from "react-redux";
-import BlockedModal from "../../components/blocked-patron/blocked-modal/BlockedModal";
+import BlockedModal, {
+ getBlockedModalId
+} from "../../components/blocked-patron/blocked-modal/BlockedModal";
import { AuthenticatedPatronV6 } from "../fbs/model";
import { useModalButtonHandler } from "./modal";
-import { setHasBeenVisible } from "../blockedModal.slice";
-import { RootState, useSelector } from "../store";
import BlockedTypes from "./types/BlockedTypes";
-import { redirectTo } from "./helpers/url";
-import { getModalIds } from "./helpers/modal-helpers";
-import { usePatronData } from "./helpers/usePatronData";
+import { getBlockedStatus, usePatronData } from "./helpers/usePatronData";
+import { useBlockedModalHasBeenVisible } from "../../components/blocked-patron/helper";
export interface PatronProps {
patron: AuthenticatedPatronV6 | null | undefined;
@@ -24,63 +22,54 @@ type InputProps = {
const withIsPatronBlockedHoc =
(Component: ComponentType
): FC
=>
({ redirectOnBlocked, ...props }) => {
- const dispatch = useDispatch();
+ // Used to check whether the modal has been opened by another component,
+ // the modal should really only be showed once.
+ const hasBeenVisible = useBlockedModalHasBeenVisible();
const { open } = useModalButtonHandler();
- const { blockedModal } = getModalIds();
+ const blockedModalId = getBlockedModalId();
const [blockedFromViewingContentArray] = useState([
- BlockedTypes.deceased,
- BlockedTypes.automatonBlocked,
- BlockedTypes.extendedExclusion,
- BlockedTypes.stolen
+ BlockedTypes.extendedSuspension
]);
- const [blockedStatus, setBlockedStatus] = useState();
const [blockedFromViewingContent, setBlockedFromViewingContent] = useState<
boolean | null
>(null);
const { data: patronData } = usePatronData();
-
- // Used to check whether the modal has been opened by another component,
- // the modal should really only be showed once.
- const {
- data: { hasBeenVisible }
- } = useSelector((state: RootState) => state.blockedModal);
+ const blockedStatus = getBlockedStatus(patronData?.patron);
useEffect(() => {
- if (!patronData) {
+ // If we do not know the blocked status, we do not have to do anything.
+ if (!patronData?.patron) {
return;
}
- if (
- patronData?.patron?.blockStatus &&
- patronData?.patron?.blockStatus?.length > 0
- ) {
- setBlockedStatus(patronData.patron.blockStatus[0].blockedReason);
- // As above comment, only opens modal if it has not already been visible.
- if (!hasBeenVisible && typeof blockedModal === "string") {
- open(blockedModal);
- dispatch(setHasBeenVisible({ hasBeenVisible: true }));
- }
- } else {
- setBlockedFromViewingContent(false);
- }
- }, [blockedModal, dispatch, hasBeenVisible, open, patronData]);
- useEffect(() => {
- if (!blockedStatus) {
- return;
+ // Open modal if it has not been shown yet and we have a blocked status.
+ if (blockedStatus !== BlockedTypes.unknown && !hasBeenVisible) {
+ open(blockedModalId);
}
- if (blockedFromViewingContentArray.includes(blockedStatus)) {
- setBlockedFromViewingContent(true);
- redirectTo(new URL(redirectOnBlocked));
- } else {
- setBlockedFromViewingContent(false);
+
+ // Make sure to hide the content behind the modal on certain blocked statuses.
+ if (blockedFromViewingContent === null) {
+ setBlockedFromViewingContent(
+ blockedFromViewingContentArray.includes(blockedStatus)
+ );
}
- }, [blockedFromViewingContentArray, blockedStatus, redirectOnBlocked]);
+ }, [
+ blockedFromViewingContent,
+ blockedFromViewingContentArray,
+ blockedModalId,
+ blockedStatus,
+ hasBeenVisible,
+ open,
+ patronData?.patron
+ ]);
return (
<>
-
+ {blockedStatus !== BlockedTypes.unknown && (
+
+ )}
{!blockedFromViewingContent && (