From 9ada92dfc13c437c90d6affad5e2f612224db3c6 Mon Sep 17 00:00:00 2001 From: Andra Constantin Date: Wed, 11 Dec 2024 13:46:15 -0500 Subject: [PATCH] track consent without state - functionality --- Backend/Models/User.cs | 7 ++++ Backend/Repositories/UserRepository.cs | 1 + src/api/models/user.ts | 6 +++ .../AnalyticsConsent/AnalyticsConsent.tsx | 20 ++++++++++ src/components/App/AppLoggedIn.tsx | 37 +++++++++++++++++++ src/components/App/index.tsx | 4 +- src/components/UserSettings/UserSettings.tsx | 37 ++++++++++++------- src/types/user.ts | 1 + 8 files changed, 98 insertions(+), 15 deletions(-) create mode 100644 src/components/AnalyticsConsent/AnalyticsConsent.tsx diff --git a/Backend/Models/User.cs b/Backend/Models/User.cs index b18a928732..10c12604cf 100644 --- a/Backend/Models/User.cs +++ b/Backend/Models/User.cs @@ -69,6 +69,9 @@ public class User [BsonElement("otelConsent")] public bool OtelConsent { get; set; } + [BsonElement("answeredConsent")] + public bool AnsweredConsent { get; set; } + [BsonElement("uiLang")] public string UILang { get; set; } @@ -101,6 +104,7 @@ public User() Password = ""; Username = ""; OtelConsent = false; + AnsweredConsent = false; UILang = ""; GlossSuggestion = AutocompleteSetting.On; Token = ""; @@ -124,6 +128,7 @@ public User Clone() Password = Password, Username = Username, OtelConsent = OtelConsent, + AnsweredConsent = AnsweredConsent, UILang = UILang, GlossSuggestion = GlossSuggestion, Token = Token, @@ -147,6 +152,7 @@ public bool ContentEquals(User other) other.Password.Equals(Password, StringComparison.Ordinal) && other.Username.Equals(Username, StringComparison.Ordinal) && other.OtelConsent == OtelConsent && + other.AnsweredConsent == AnsweredConsent && other.UILang.Equals(UILang, StringComparison.Ordinal) && other.GlossSuggestion.Equals(GlossSuggestion) && other.Token.Equals(Token, StringComparison.Ordinal) && @@ -185,6 +191,7 @@ public override int GetHashCode() hash.Add(Password); hash.Add(Username); hash.Add(OtelConsent); + hash.Add(AnsweredConsent); hash.Add(UILang); hash.Add(GlossSuggestion); hash.Add(Token); diff --git a/Backend/Repositories/UserRepository.cs b/Backend/Repositories/UserRepository.cs index d48f311116..35e3d8e44f 100644 --- a/Backend/Repositories/UserRepository.cs +++ b/Backend/Repositories/UserRepository.cs @@ -197,6 +197,7 @@ public async Task Update(string userId, User user, bool updateIs .Set(x => x.Agreement, user.Agreement) .Set(x => x.Username, user.Username) .Set(x => x.OtelConsent, user.OtelConsent) + .Set(x => x.AnsweredConsent, user.AnsweredConsent) .Set(x => x.UILang, user.UILang) .Set(x => x.GlossSuggestion, user.GlossSuggestion); diff --git a/src/api/models/user.ts b/src/api/models/user.ts index 7fd261072e..7b3cc9a52d 100644 --- a/src/api/models/user.ts +++ b/src/api/models/user.ts @@ -98,6 +98,12 @@ export interface User { * @memberof User */ otelConsent?: boolean; + /** + * + * @type {boolean} + * @memberof User + */ + answeredConsent?: boolean; /** * * @type {string} diff --git a/src/components/AnalyticsConsent/AnalyticsConsent.tsx b/src/components/AnalyticsConsent/AnalyticsConsent.tsx new file mode 100644 index 0000000000..36f780be6d --- /dev/null +++ b/src/components/AnalyticsConsent/AnalyticsConsent.tsx @@ -0,0 +1,20 @@ +import { ReactElement } from "react"; + +interface ConsentProps { + onOtelChange: (consent: boolean) => void; +} + +export function AnalyticsConsent(props: ConsentProps): ReactElement { + const acceptAnalytics = (): void => { + props.onOtelChange(true); + }; + const denyAnalytics = (): void => { + props.onOtelChange(false); + }; + return ( +
+ + +
+ ); +} diff --git a/src/components/App/AppLoggedIn.tsx b/src/components/App/AppLoggedIn.tsx index 5fb0c49417..2131411372 100644 --- a/src/components/App/AppLoggedIn.tsx +++ b/src/components/App/AppLoggedIn.tsx @@ -17,6 +17,9 @@ import { Path } from "types/path"; import FontContext, { ProjectFonts } from "utilities/fontContext"; import { getProjCss } from "utilities/fontCssUtilities"; import { routerPath } from "utilities/pathUtilities"; +import { AnalyticsConsent } from "components/AnalyticsConsent/AnalyticsConsent"; +import { updateUser } from "backend"; +import { getCurrentUser } from "backend/localStorage"; const BaseGoalScreen = loadable( () => import("goals/DefaultGoal/BaseGoalScreen") @@ -30,6 +33,8 @@ const UserSettings = loadable( () => import("components/UserSettings/UserSettings") ); +// export function UserConsent(): ReactElement; + export default function AppWithBar(): ReactElement { const proj = useAppSelector( (state: StoreState) => state.currentProjectState.project, @@ -48,6 +53,29 @@ export default function AppWithBar(): ReactElement { const [styleOverrides, setStyleOverrides] = useState(); + const [answeredConsent, setAnsweredConsent] = useState( + getCurrentUser()?.answeredConsent + ); + + async function handleConsent(otelConsent: boolean): Promise { + await updateUser({ + ...getCurrentUser()!, + otelConsent, + answeredConsent: true, + }); + setAnsweredConsent(true); + } + + // useEffect(() => { + // setAnsweredConsent(true); + // }, [answeredConsent]); + + // const handleConsent = (otelConsent: boolean): void => { + // const user = getCurrentUser(); + // console.log(user); + // console.log(otelConsent); + // }; + useEffect(() => { updateLangFromUser(); }, []); @@ -80,9 +108,18 @@ export default function AppWithBar(): ReactElement { +
HI THERE
+ {/*
HI
*/} + {/*
HI
*/} +
HEY
+ {answeredConsent ? null : ( +
+ +
+ )} } /> } /> diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 7d906a381c..5b9cf17752 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -3,7 +3,7 @@ import { RouterProvider } from "react-router-dom"; import AnnouncementBanner from "components/AnnouncementBanner"; import UpperRightToastContainer from "components/Toast/UpperRightToastContainer"; -import CookieConsent from "cookies/CookieConsent"; +// import CookieConsent from "cookies/CookieConsent"; import router from "router/browserRouter"; /** @@ -13,7 +13,7 @@ export default function App(): ReactElement { return (
}> - + {/* */} diff --git a/src/components/UserSettings/UserSettings.tsx b/src/components/UserSettings/UserSettings.tsx index bfde8a5935..0a2eb50b61 100644 --- a/src/components/UserSettings/UserSettings.tsx +++ b/src/components/UserSettings/UserSettings.tsx @@ -11,18 +11,18 @@ import { Typography, } from "@mui/material"; import { enqueueSnackbar } from "notistack"; -import { FormEvent, Fragment, ReactElement, useEffect, useState } from "react"; +import { FormEvent, Fragment, ReactElement, useState } from "react"; import { useTranslation } from "react-i18next"; -import { show } from "vanilla-cookieconsent"; import { AutocompleteSetting, User } from "api/models"; import { isEmailTaken, updateUser } from "backend"; import { getAvatar, getCurrentUser } from "backend/localStorage"; +import { AnalyticsConsent } from "components/AnalyticsConsent/AnalyticsConsent"; import { asyncLoadSemanticDomains } from "components/Project/ProjectActions"; import ClickableAvatar from "components/UserSettings/ClickableAvatar"; import { updateLangFromUser } from "i18n"; -import { useAppDispatch, useAppSelector } from "rootRedux/hooks"; -import { StoreState } from "rootRedux/types"; +import { useAppDispatch } from "rootRedux/hooks"; +// import { StoreState } from "rootRedux/types"; import theme from "types/theme"; import { uiWritingSystems } from "types/writingSystem"; @@ -58,14 +58,14 @@ export function UserSettings(props: { }): ReactElement { const dispatch = useAppDispatch(); - const analyticsConsent = useAppSelector( - (state: StoreState) => state.analyticsState.consent - ); + // const analyticsConsent = useAppSelector( + // (state: StoreState) => state.analyticsState.consent + // ); const [name, setName] = useState(props.user.name); const [phone, setPhone] = useState(props.user.phone); const [email, setEmail] = useState(props.user.email); - const [otelConsent, setOtelConsent] = useState(analyticsConsent); + const [otelConsent, setOtelConsent] = useState(props.user.otelConsent); const [uiLang, setUiLang] = useState(props.user.uiLang ?? ""); const [glossSuggestion, setGlossSuggestion] = useState( props.user.glossSuggestion @@ -81,9 +81,13 @@ export function UserSettings(props: { return unchanged || !(await isEmailTaken(unicodeEmail)); } - useEffect(() => { - setOtelConsent(analyticsConsent); - }, [analyticsConsent]); + const [showOptions, setShowOptions] = useState(false); + const show = (): void => setShowOptions(true); + + const handleConsentChange = (consentVal: boolean): void => { + setOtelConsent(consentVal); + setShowOptions(false); + }; const disabled = name === props.user.name && @@ -293,7 +297,7 @@ export function UserSettings(props: { {t( - analyticsConsent + otelConsent ? "userSettings.analyticsConsent.consentYes" : "userSettings.analyticsConsent.consentNo" )} @@ -301,11 +305,18 @@ export function UserSettings(props: { + {showOptions ? ( +
+ +
+ ) : null}
diff --git a/src/types/user.ts b/src/types/user.ts index 29d6d0177c..55ad01aeb1 100644 --- a/src/types/user.ts +++ b/src/types/user.ts @@ -15,6 +15,7 @@ export function newUser(name = "", username = "", password = ""): User { glossSuggestion: AutocompleteSetting.On, token: "", isAdmin: false, + answeredConsent: false, }; }