From 68bfb27cb816f61caeaa7817ebfc526d5288211f Mon Sep 17 00:00:00 2001
From: Jan-Marius Vatle <48485965+janmarius@users.noreply.github.com>
Date: Mon, 18 Mar 2024 10:43:55 +0100
Subject: [PATCH] FIX-2270 Storing the currentUsername for server objects
between refreshing (#2307)
---
.../__testUtils__/testUtils.tsx | 37 +++++++------
.../components/ContentViews/ServerManager.tsx | 7 +++
.../components/TopRightCornerMenu.tsx | 7 +++
.../contexts/loggedInUsernamesContext.tsx | 53 +++++++++++++++++++
.../contexts/loggedInUsernamesReducer.tsx | 46 ++++++++++++++++
.../hooks/query/useGetServers.tsx | 27 ++++++++++
.../routes/AuthRoute.tsx | 7 +++
Src/WitsmlExplorer.Frontend/routes/Root.tsx | 37 +++++++------
8 files changed, 187 insertions(+), 34 deletions(-)
create mode 100644 Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesContext.tsx
create mode 100644 Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesReducer.tsx
diff --git a/Src/WitsmlExplorer.Frontend/__testUtils__/testUtils.tsx b/Src/WitsmlExplorer.Frontend/__testUtils__/testUtils.tsx
index e0aae7abf..03d3c3e8a 100644
--- a/Src/WitsmlExplorer.Frontend/__testUtils__/testUtils.tsx
+++ b/Src/WitsmlExplorer.Frontend/__testUtils__/testUtils.tsx
@@ -4,6 +4,7 @@ import { render } from "@testing-library/react";
import { ConnectedServerProvider } from "contexts/connectedServerContext";
import { CurveThresholdProvider } from "contexts/curveThresholdContext";
import { Filter, FilterContextProvider } from "contexts/filter";
+import { LoggedInUsernamesProvider } from "contexts/loggedInUsernamesContext";
import OperationContext from "contexts/operationContext";
import {
DateTimeFormat,
@@ -89,23 +90,25 @@ export function renderWithContexts(
-
-
-
-
-
-
- {children}
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
+
+
diff --git a/Src/WitsmlExplorer.Frontend/components/ContentViews/ServerManager.tsx b/Src/WitsmlExplorer.Frontend/components/ContentViews/ServerManager.tsx
index cf8166d10..46cf0d725 100644
--- a/Src/WitsmlExplorer.Frontend/components/ContentViews/ServerManager.tsx
+++ b/Src/WitsmlExplorer.Frontend/components/ContentViews/ServerManager.tsx
@@ -14,6 +14,8 @@ import UserCredentialsModal, {
} from "components/Modals/UserCredentialsModal";
import ProgressSpinner from "components/ProgressSpinner";
import { useConnectedServer } from "contexts/connectedServerContext";
+import { useLoggedInUsernames } from "contexts/loggedInUsernamesContext";
+import { LoggedInUsernamesActionType } from "contexts/loggedInUsernamesReducer";
import OperationContext from "contexts/operationContext";
import OperationType from "contexts/operationType";
import { useGetServers } from "hooks/query/useGetServers";
@@ -40,6 +42,7 @@ const ServerManager = (): React.ReactElement => {
const editDisabled = msalEnabled && !getUserAppRoles().includes(adminRole);
const navigate = useNavigate();
const { connectedServer, setConnectedServer } = useConnectedServer();
+ const { dispatchLoggedInUsernames } = useLoggedInUsernames();
const connectServer = async (server: Server) => {
const userCredentialsModalProps: UserCredentialsModalProps = {
@@ -48,6 +51,10 @@ const ServerManager = (): React.ReactElement => {
dispatchOperation({ type: OperationType.HideModal });
AuthorizationService.onAuthorized(server, username);
AuthorizationService.setSelectedServer(server);
+ dispatchLoggedInUsernames({
+ type: LoggedInUsernamesActionType.AddLoggedInUsername,
+ payload: { serverId: server.id, username }
+ });
setConnectedServer(server);
navigate(getWellsViewPath(server.url));
}
diff --git a/Src/WitsmlExplorer.Frontend/components/TopRightCornerMenu.tsx b/Src/WitsmlExplorer.Frontend/components/TopRightCornerMenu.tsx
index ca1fe5bad..b7f35d4d8 100644
--- a/Src/WitsmlExplorer.Frontend/components/TopRightCornerMenu.tsx
+++ b/Src/WitsmlExplorer.Frontend/components/TopRightCornerMenu.tsx
@@ -6,6 +6,8 @@ import UserCredentialsModal, {
} from "components/Modals/UserCredentialsModal";
import ServerManagerButton from "components/ServerManagerButton";
import { useConnectedServer } from "contexts/connectedServerContext";
+import { useLoggedInUsernames } from "contexts/loggedInUsernamesContext";
+import { LoggedInUsernamesActionType } from "contexts/loggedInUsernamesReducer";
import OperationContext from "contexts/operationContext";
import OperationType from "contexts/operationType";
import useDocumentDimensions from "hooks/useDocumentDimensions";
@@ -26,6 +28,7 @@ export default function TopRightCornerMenu() {
const showLabels = documentWidth > 1180;
const { connectedServer } = useConnectedServer();
const navigate = useNavigate();
+ const { dispatchLoggedInUsernames } = useLoggedInUsernames();
const openSettingsMenu = () => {
dispatchOperation({
@@ -40,6 +43,10 @@ export default function TopRightCornerMenu() {
onConnectionVerified: (username) => {
dispatchOperation({ type: OperationType.HideModal });
AuthorizationService.onAuthorized(connectedServer, username);
+ dispatchLoggedInUsernames({
+ type: LoggedInUsernamesActionType.AddLoggedInUsername,
+ payload: { serverId: connectedServer.id, username }
+ });
}
};
dispatchOperation({
diff --git a/Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesContext.tsx b/Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesContext.tsx
new file mode 100644
index 000000000..410d0b31f
--- /dev/null
+++ b/Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesContext.tsx
@@ -0,0 +1,53 @@
+import {
+ LoggedInUsernamesAction,
+ loggedInUsernamesReducer
+} from "contexts/loggedInUsernamesReducer";
+import { ReactNode, createContext, useContext, useReducer } from "react";
+
+export interface LoggedInUsername {
+ serverId: string;
+ username: string;
+}
+
+interface LoggedInUsernamesContextType {
+ loggedInUsernames: LoggedInUsername[];
+ dispatchLoggedInUsernames: (action: LoggedInUsernamesAction) => void;
+}
+
+const LoggedInUsernamesContext =
+ createContext(null);
+
+interface LoggedInUsernamesProviderProps {
+ children: ReactNode;
+}
+
+/**
+ * This context is utilized to store the currentUsername for server objects in between refreshes for caching purposes.
+ * @param param0
+ * @returns
+ */
+export function LoggedInUsernamesProvider({
+ children
+}: LoggedInUsernamesProviderProps) {
+ const [loggedInUsernames, dispatchLoggedInUsernames] = useReducer(
+ loggedInUsernamesReducer,
+ []
+ );
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useLoggedInUsernames() {
+ const context = useContext(LoggedInUsernamesContext);
+ if (!context)
+ throw new Error(
+ `useLoggedInUsernames() has to be used within `
+ );
+ return context;
+}
diff --git a/Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesReducer.tsx b/Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesReducer.tsx
new file mode 100644
index 000000000..10e3457c6
--- /dev/null
+++ b/Src/WitsmlExplorer.Frontend/contexts/loggedInUsernamesReducer.tsx
@@ -0,0 +1,46 @@
+import { LoggedInUsername } from "contexts/loggedInUsernamesContext";
+
+export enum LoggedInUsernamesActionType {
+ AddLoggedInUsername = "AddLoggedInUsername"
+}
+
+export interface LoggedInUsernamesAction {
+ type: any;
+ payload: any;
+}
+
+export interface AddLoggedInUsernameAction extends LoggedInUsernamesAction {
+ type: LoggedInUsernamesActionType.AddLoggedInUsername;
+ payload: LoggedInUsername;
+}
+
+export function loggedInUsernamesReducer(
+ state: LoggedInUsername[],
+ action: LoggedInUsernamesAction
+): LoggedInUsername[] {
+ switch (action.type) {
+ case LoggedInUsernamesActionType.AddLoggedInUsername:
+ return addLoggedInUsername(state, action);
+ default: {
+ throw new Error(`Unsupported action type ${action.type}`);
+ }
+ }
+}
+
+const addLoggedInUsername = (
+ state: LoggedInUsername[],
+ { payload }: LoggedInUsernamesAction
+): LoggedInUsername[] => {
+ const loggedInUsernames = structuredClone(state);
+ const newLoggedInUsername: LoggedInUsername = structuredClone(payload);
+ const index = loggedInUsernames.findIndex(
+ (loggedInUsername) =>
+ loggedInUsername.serverId === newLoggedInUsername.serverId
+ );
+ if (index === -1) {
+ loggedInUsernames.push(newLoggedInUsername);
+ } else if (index >= 0) {
+ loggedInUsernames.splice(index, 1, newLoggedInUsername);
+ }
+ return loggedInUsernames;
+};
diff --git a/Src/WitsmlExplorer.Frontend/hooks/query/useGetServers.tsx b/Src/WitsmlExplorer.Frontend/hooks/query/useGetServers.tsx
index 82c22d902..7a7342d14 100644
--- a/Src/WitsmlExplorer.Frontend/hooks/query/useGetServers.tsx
+++ b/Src/WitsmlExplorer.Frontend/hooks/query/useGetServers.tsx
@@ -1,4 +1,8 @@
import { QueryObserverResult, useQuery } from "@tanstack/react-query";
+import {
+ LoggedInUsername,
+ useLoggedInUsernames
+} from "contexts/loggedInUsernamesContext";
import { Server } from "../../models/server";
import ServerService from "../../services/serverService";
import { QUERY_KEY_SERVERS } from "./queryKeys";
@@ -26,5 +30,28 @@ type ServersQueryResult = Omit<
export const useGetServers = (options?: QueryOptions): ServersQueryResult => {
const { data, ...state } = useQuery(serversQuery(options));
+ const { loggedInUsernames } = useLoggedInUsernames();
+
+ // This step is necessary because the currentUsername is lost during server refresh.
+ data?.map((server) => setCurrentUsernames(server, loggedInUsernames));
+
return { servers: data, ...state };
};
+
+/**
+ * The following method is used to set the currentUsername on the server object.
+ * It's worth noting that changing objects using their reference is generally considered bad practice.
+ * However, since the AuthorizationService is already modifying the server object in this way, this method has been designed to do the same.
+ * @param server
+ * @param loggedInUsernames
+ */
+const setCurrentUsernames = (
+ server: Server,
+ loggedInUsernames: LoggedInUsername[]
+) => {
+ loggedInUsernames.map((loggedInUsername) => {
+ if (server.id === loggedInUsername.serverId) {
+ server.currentUsername = loggedInUsername.username;
+ }
+ });
+};
diff --git a/Src/WitsmlExplorer.Frontend/routes/AuthRoute.tsx b/Src/WitsmlExplorer.Frontend/routes/AuthRoute.tsx
index fbf3a65b4..9236b67a0 100644
--- a/Src/WitsmlExplorer.Frontend/routes/AuthRoute.tsx
+++ b/Src/WitsmlExplorer.Frontend/routes/AuthRoute.tsx
@@ -1,4 +1,6 @@
import { useIsAuthenticated } from "@azure/msal-react";
+import { useLoggedInUsernames } from "contexts/loggedInUsernamesContext";
+import { LoggedInUsernamesActionType } from "contexts/loggedInUsernamesReducer";
import { useContext, useEffect } from "react";
import { Outlet, useNavigate, useParams } from "react-router-dom";
import { ItemNotFound } from "routes/ItemNotFound";
@@ -23,6 +25,7 @@ export default function AuthRoute() {
const { serverUrl } = useParams();
const { connectedServer, setConnectedServer } = useConnectedServer();
const navigate = useNavigate();
+ const { dispatchLoggedInUsernames } = useLoggedInUsernames();
useEffect(() => {
const unsubscribe =
@@ -68,6 +71,10 @@ export default function AuthRoute() {
onConnectionVerified: (username) => {
dispatchOperation({ type: OperationType.HideModal });
AuthorizationService.onAuthorized(server, username);
+ dispatchLoggedInUsernames({
+ type: LoggedInUsernamesActionType.AddLoggedInUsername,
+ payload: { serverId: server.id, username }
+ });
if (initialLogin) {
AuthorizationService.setSelectedServer(server);
setConnectedServer(server);
diff --git a/Src/WitsmlExplorer.Frontend/routes/Root.tsx b/Src/WitsmlExplorer.Frontend/routes/Root.tsx
index 92f8e1c5f..006a32f8d 100644
--- a/Src/WitsmlExplorer.Frontend/routes/Root.tsx
+++ b/Src/WitsmlExplorer.Frontend/routes/Root.tsx
@@ -1,6 +1,7 @@
import { InteractionType } from "@azure/msal-browser";
import { MsalAuthenticationTemplate, MsalProvider } from "@azure/msal-react";
import { ThemeProvider } from "@material-ui/core";
+import { LoggedInUsernamesProvider } from "contexts/loggedInUsernamesContext";
import Head from "next/head";
import { SnackbarProvider } from "notistack";
import { useEffect } from "react";
@@ -124,23 +125,25 @@ export default function Root() {
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+