Skip to content

Commit

Permalink
Merge branch 'develop' into dependabot/npm_and_yarn/ui/develop/graphq…
Browse files Browse the repository at this point in the history
…l-16.10.0
  • Loading branch information
nimdanitro authored Dec 17, 2024
2 parents a51448c + 8cbb9bf commit 4c89856
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 83 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### 🐛 Bug Fixes

- *(i18n)* Format times and dates according to locale ([#572](https://github.com/f-eld-ch/sitrep/issues/572)) - ([204bbae](https://github.com/f-eld-ch/sitrep/commit/204bbaea4b402306a5e5f52c9a30e9509adbbb9a))
- *(map)* Do not reload while editing text of feature ([#579](https://github.com/f-eld-ch/sitrep/issues/579)) - ([78f7f7f](https://github.com/f-eld-ch/sitrep/commit/78f7f7f0b4856ba64fdbd4a417fe0f82388e9748))
- *(map)* Fix searchbar icon - ([b7e1f98](https://github.com/f-eld-ch/sitrep/commit/b7e1f98dfaffae53f064d4630d19bd964c4d563d))

### ⚙️ Other
Expand Down
66 changes: 30 additions & 36 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { lazy, Suspense, useEffect, useState } from "react";
import { lazy, Suspense, useEffect } from "react";
import { Navigate, Route, HashRouter as Router, Routes } from "react-router-dom";

import "./App.scss";
Expand All @@ -16,55 +16,49 @@ import { List as RequestList } from "views/measures/requests";
import { List as TaskList } from "views/measures/tasks";
import { List as ResourcesList } from "views/resource";

import { useTranslation } from "react-i18next";
import { ApolloProvider } from "@apollo/client";
import { Spinner } from "components";
import { useTranslation } from "react-i18next";
import { UserState } from "types";
import { UserContext } from "utils";
import { UserProvider } from "utils";
import MessageSheet from "views/journal/MessageSheet";
import { Layout, LayoutMarginLess } from "views/Layout";
import { default as client } from "client";
import { Provider as FeatureFlagProvider } from "FeatureFlags";

import "./i18n";
import dayjs from "dayjs";
import LocalizedFormat from "dayjs/plugin/localizedFormat";
import de from "dayjs/locale/de";
import fr from "dayjs/locale/fr";
import it from "dayjs/locale/it";
import en from "dayjs/locale/en";
const Map = lazy(() => import("views/map"));

function App() {
const [userState, setUserState] = useState<UserState>({ isLoggedin: false, email: "", username: "" });
const { i18n } = useTranslation();

const setUserStateFromUserinfo = () => {
fetch("/oauth2/userinfo", { credentials: "include" })
.then((response) => {
if (!response.ok) {
throw new Error("unauthenticated");
}
return response.json();
})
.then((userInfo) => {
setUserState({
isLoggedin: true,
email: userInfo.email,
username: userInfo.user || userInfo.preferredUsername,
});
})
.catch(() => {
setUserState({ isLoggedin: false, email: "", username: "" });
});
};
dayjs.extend(LocalizedFormat);

useEffect(() => {
setUserStateFromUserinfo();
i18n.changeLanguage();

const interval = setInterval(() => {
setUserStateFromUserinfo();
}, 10000);

return () => clearInterval(interval);
}, [i18n]);
const locale = (lang: string) => {
switch (lang) {
case "de":
return de;
case "en":
return en;
case "fr":
return fr;
case "it":
return it;
default:
return en;
}
};
const lang = locale(i18n.language);
dayjs.locale(lang.toString());
}, [i18n.language]);

return (
<UserContext.Provider value={userState}>
<UserProvider>
<ApolloProvider client={client}>
<FeatureFlagProvider>
<Router>
Expand Down Expand Up @@ -189,7 +183,7 @@ function App() {
</Router>
</FeatureFlagProvider>
</ApolloProvider>
</UserContext.Provider>
</UserProvider>
);
}

Expand Down
7 changes: 1 addition & 6 deletions ui/src/FeatureFlags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const localFlagConfig = {

const Provider = (props: PropsWithChildren) => {
const { children } = props;
const userState = useContext(UserContext);
const { state: userState } = useContext(UserContext);

useEffect(() => {
const fliptProvider = new FliptWebProvider("sitrep-ui", { url: "https://flipt.sitrep.ch" });
Expand All @@ -38,11 +38,6 @@ const Provider = (props: PropsWithChildren) => {
email: userState.email,
};
OpenFeature.setContext(context);

return () => {
console.log("closing openfeature provider");
OpenFeature.close();
};
}, [userState]);

return <OpenFeatureProvider>{children}</OpenFeatureProvider>;
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ function VersionNavBar() {
}

function UserNavBar() {
const userState = useContext(UserContext);
const { state: userState } = useContext(UserContext);
const { t } = useTranslation();

if (!userState.isLoggedin) return <></>;
Expand Down
1 change: 0 additions & 1 deletion ui/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import { ReloadPrompt } from "utils";
import "./i18n";
import reportWebVitals from "./reportWebVitals";
const container = document.getElementById("root");

Expand Down
97 changes: 94 additions & 3 deletions ui/src/utils/UserContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,96 @@
import { createContext } from "react";
import { createContext, useReducer, useEffect, useCallback, useContext, ReactNode, Dispatch } from "react";
import { UserState } from "types";
const UserContext = createContext<UserState>({ isLoggedin: false, username: "", email: "" });

export { UserContext };
// Define the initial state
const initialState: UserState = { isLoggedin: false, username: "", email: "" };

// Define action types
type UserAction = { type: "LOGIN"; payload: { username: string; email: string } } | { type: "LOGOUT" };

// Define the reducer function
const userReducer = (state: UserState, action: UserAction): UserState => {
switch (action.type) {
case "LOGIN":
return {
isLoggedin: true,
username: action.payload.username,
email: action.payload.email,
};
case "LOGOUT":
return {
isLoggedin: false,
username: "",
email: "",
};
default:
return state;
}
};

// Create the UserContext with initial state and a dummy dispatch function
const UserContext = createContext<{
state: UserState;
dispatch: Dispatch<UserAction>;
}>({
state: initialState,
dispatch: () => null,
});

// Create the UserProvider component
const UserProvider = ({ children }: { children: ReactNode }) => {
const [state, dispatch] = useReducer(userReducer, initialState);

return (
<UserContext.Provider value={{ state, dispatch }}>
<UserInfoFetcher />
{children}
</UserContext.Provider>
);
};

const UserInfoFetcher = () => {
const { state: userState, dispatch } = useContext(UserContext);
const setUserStateFromUserinfo = useCallback(() => {
fetch("/oauth2/userinfo", { credentials: "include" })
.then((response) => {
if (!response.ok) {
throw new Error("unauthenticated");
}
return response.json();
})
.then((userInfo) => {
const newUserState = {
isLoggedin: true,
email: userInfo.email,
username: userInfo.user || userInfo.preferredUsername,
};

// Only update state if it has changed
if (
newUserState.isLoggedin !== userState.isLoggedin ||
newUserState.email !== userState.email ||
newUserState.username !== userState.username
) {
dispatch({ type: "LOGIN", payload: newUserState });
}
})
.catch(() => {
if (userState.isLoggedin) {
dispatch({ type: "LOGOUT" });
}
});
}, [userState, dispatch]);

useEffect(() => {
setUserStateFromUserinfo();
const interval = setInterval(() => {
setUserStateFromUserinfo();
}, 30000);

return () => clearInterval(interval);
}, [userState]);

return <></>;
};

export { UserContext, UserProvider };
2 changes: 1 addition & 1 deletion ui/src/utils/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { UserContext } from "./UserContext";
export { UserContext, UserProvider } from "./UserContext";

export { ReloadPrompt } from "./ReloadSWPrompt";
6 changes: 4 additions & 2 deletions ui/src/utils/useDate.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import dayjs from "dayjs";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";

export const useDate = () => {
const { i18n } = useTranslation();
const [now, setNow] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => {
Expand All @@ -12,8 +14,8 @@ export const useDate = () => {
};
}, []);

const date = dayjs(now).format("LL");
const time = dayjs(now).format("LT");
const date = dayjs(now).locale(i18n.language).format("LL");
const time = dayjs(now).locale(i18n.language).format("LT");

return {
now,
Expand Down
4 changes: 2 additions & 2 deletions ui/src/views/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface LayoutProps {
export const Layout = (props: LayoutProps) => {
const [searchParams] = useSearchParams();
const { i18n } = useTranslation();
const userState = useContext(UserContext);
const { state: userState } = useContext(UserContext);

const lang = searchParams.get("lang");
useEffect(() => {
Expand All @@ -40,7 +40,7 @@ export const Layout = (props: LayoutProps) => {
export const LayoutMarginLess = (props: LayoutProps) => {
const [searchParams] = useSearchParams();
const { i18n } = useTranslation();
const userState = useContext(UserContext);
const { state: userState } = useContext(UserContext);

const lang = searchParams.get("lang");
useEffect(() => {
Expand Down
12 changes: 3 additions & 9 deletions ui/src/views/journal/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import classNames from "classnames";
import dayjs from "dayjs";
import de from "dayjs/locale/de";
import LocalizedFormat from "dayjs/plugin/localizedFormat";
import relativeTime from "dayjs/plugin/relativeTime";

import { useBooleanFlagValue } from "@openfeature/react-sdk";
import { faArrowsToEye, faEdit, faPrint, faSquareCheck } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
Expand All @@ -27,10 +25,6 @@ export interface MessageProps {
setTriageMessage?: (message: MessageType | undefined) => void;
}

dayjs.locale(de);
dayjs.extend(LocalizedFormat);
dayjs.extend(relativeTime);

function Message({
id,
sender,
Expand All @@ -45,7 +39,7 @@ function Message({
setTriageMessage,
origMessage,
}: MessageProps) {
const { t } = useTranslation();
const { t, i18n } = useTranslation();
const { incidentId, journalId } = useParams();
const showTasks = useBooleanFlagValue("show-tasks", false);

Expand Down Expand Up @@ -102,7 +96,7 @@ function Message({
<div className="level-item has-text-centered is-flex-shrink-1">
<div className="mb-0">
<p className="heading is-size-7">{t("message.time")}</p>
<p className="subtitle is-size-7">{dayjs(timeDate).format("LLL")}</p>
<p className="subtitle is-size-7">{dayjs(timeDate).locale(i18n.language).format("LLL")}</p>
</div>
</div>
<div className="level-item has-text-centered is-flex-shrink-1">
Expand Down
8 changes: 3 additions & 5 deletions ui/src/views/journal/MessageSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { faSquare, faSquareCheck } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Spinner } from "components";
import dayjs from "dayjs";
import de from "dayjs/locale/de";
import LocalizedFormat from "dayjs/plugin/localizedFormat";
import relativeTime from "dayjs/plugin/relativeTime";

Expand All @@ -13,13 +12,12 @@ import { useParams } from "react-router-dom";
import { Medium, PriorityStatus, TriageMessageData, TriageMessageVars, TriageStatus } from "types";
import { GetMessageForTriage } from "./graphql";

dayjs.locale(de);
dayjs.extend(LocalizedFormat);
dayjs.extend(relativeTime);

function MessageSheet() {
const { messageId } = useParams();
const { t } = useTranslation();
const { t, i18n } = useTranslation();

const { loading, error, data } = useQuery<TriageMessageData, TriageMessageVars>(GetMessageForTriage, {
variables: { messageId: messageId },
Expand Down Expand Up @@ -60,9 +58,9 @@ function MessageSheet() {
</tr>
<tr>
<th>{t("message.time")}</th>
<td>{dayjs(data?.messagesByPk.createdAt).format("LLL")}</td>
<td>{dayjs(data?.messagesByPk.createdAt).locale(i18n.language).format("LLL")}</td>
<th>{t("message.createdAt")}</th>
<td>{dayjs(data?.messagesByPk.createdAt).format("LLL")}</td>
<td>{dayjs(data?.messagesByPk.createdAt).locale(i18n.language).format("LLL")}</td>
</tr>
<tr>
<th>{t("message.id")}</th>
Expand Down
Loading

0 comments on commit 4c89856

Please sign in to comment.