Skip to content

Commit

Permalink
Merge pull request #18965 from GordonSmith/HPCC-32406-USER_RESET
Browse files Browse the repository at this point in the history
HPCC-32406 Add ECL Watch "reset settings" option
  • Loading branch information
GordonSmith authored Aug 8, 2024
2 parents a1db710 + e7ad5b2 commit 459df03
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 10 deletions.
9 changes: 8 additions & 1 deletion esp/src/src-react/components/Frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ import { useUserSession } from "../hooks/user";
const logger = scopedLogger("../components/Frame.tsx");
const envLogger = scopedLogger("environment");

const USER_COOKIE_CONSENT = "user_cookie_consent";

export function resetCookieConsent() {
const store = userKeyValStore();
return store.delete(USER_COOKIE_CONSENT);
}

interface FrameProps {
}

Expand Down Expand Up @@ -111,7 +118,7 @@ export const Frame: React.FunctionComponent<FrameProps> = () => {
/>}
/>
<CookieConsent showCookieConsent={showCookieConsent} onApply={(n: boolean) => {
userKeyValStore().set("user_cookie_consent", n ? "1" : "0");
userKeyValStore().set(USER_COOKIE_CONSENT, n ? "1" : "0");
}} />
</ThemeProvider >
</FluentProvider >;
Expand Down
87 changes: 87 additions & 0 deletions esp/src/src-react/components/Reset.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import * as React from "react";
import { PrimaryButton, DefaultButton, mergeStyleSets, Checkbox, Stack } from "@fluentui/react";
import { resetCookies, resetModernMode } from "src/Session";
import nlsHPCC from "src/nlsHPCC";
import { pushUrl, replaceUrl } from "../util/history";
import { resetMetricsViews } from "../hooks/metrics";
import { resetHistory } from "../util/history";
import { MessageBox } from "../layouts/MessageBox";
import { resetTheme, useUserTheme } from "../hooks/theme";
import { resetFavorites } from "../hooks/favorite";
import { resetCookieConsent } from "./Frame";
import { resetWorkunitOptions } from "./Workunits";

export interface ResetDialogProps {
}

export const ResetDialog: React.FunctionComponent<ResetDialogProps> = ({
}) => {

const [show, setShow] = React.useState(true);
const [checkMetricOptions, setCheckMetricOptions] = React.useState(true);
const [checkWorkunitOptions, setCheckWorkunitOptions] = React.useState(true);
const [checkHistory, setCheckHistoryCheckbox] = React.useState(true);
const [checkFavorites, setCheckFavorites] = React.useState(true);
const [checkEclWatchVersion, setCheckEclWatchVersion] = React.useState(true);
const [checkTheme, setCheckTheme] = React.useState(true);
const [checkCookies, setCheckCookies] = React.useState(false);
const { theme } = useUserTheme();

const styles = React.useMemo(() => mergeStyleSets({
root: {
height: "100%",
backgroundColor: theme.semanticColors.bodyBackground
},
}), [theme.semanticColors.bodyBackground]);

return <div className={styles.root}>
<MessageBox show={show} setShow={setShow} title={`${nlsHPCC.ResetUserSettings}?`} footer={
<>
<PrimaryButton text={nlsHPCC.Reset} onClick={async () => {
if (checkMetricOptions) {
await resetMetricsViews();
}
if (checkWorkunitOptions) {
await resetWorkunitOptions();
}
if (checkHistory) {
await resetHistory();
}
if (checkFavorites) {
await resetFavorites();
}
if (checkHistory) {
await resetHistory();
}
if (checkEclWatchVersion) {
await resetModernMode();
}
if (checkTheme) {
await resetTheme();
}
if (checkCookies) {
await resetCookies();
await resetCookieConsent();
}
setShow(false);
replaceUrl("/");
window.location.reload();
}} />
<DefaultButton text={nlsHPCC.Cancel} onClick={() => {
setShow(false);
pushUrl("/");
}} />
</>
}>
<Stack tokens={{ childrenGap: 10 }}>
<Checkbox label={nlsHPCC.MetricOptions} checked={checkMetricOptions} onChange={(ev, checked) => setCheckMetricOptions(checked)} />
<Checkbox label={nlsHPCC.WorkunitOptions} checked={checkWorkunitOptions} onChange={(ev, checked) => setCheckWorkunitOptions(checked)} />
<Checkbox label={nlsHPCC.History} checked={checkHistory} onChange={(ev, checked) => setCheckHistoryCheckbox(checked)} />
<Checkbox label={nlsHPCC.Favorites} checked={checkFavorites} onChange={(ev, checked) => setCheckFavorites(checked)} />
<Checkbox label={nlsHPCC.ECLWatchVersion} checked={checkEclWatchVersion} onChange={(ev, checked) => setCheckEclWatchVersion(checked)} />
<Checkbox label={nlsHPCC.Theme} checked={checkTheme} onChange={(ev, checked) => setCheckTheme(checked)} />
<Checkbox label={nlsHPCC.Cookies} checked={checkCookies} onChange={(ev, checked) => setCheckCookies(checked)} />
</Stack>
</MessageBox>
</div>;
};
6 changes: 6 additions & 0 deletions esp/src/src-react/components/Title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ export const DevTitle: React.FunctionComponent<DevTitleProps> = ({
isChecked: true,
onClick: onTechPreviewClick
},
{ key: "divider_4", itemType: ContextualMenuItemType.Divider },
{
key: "reset",
href: "/esp/files/index.html#/reset",
text: nlsHPCC.ResetUserSettings
},
{ key: "about", text: nlsHPCC.About, onClick: () => setShowAbout(true) }
],
directionalHintFixed: true
Expand Down
10 changes: 9 additions & 1 deletion esp/src/src-react/components/Workunits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SizeMe } from "react-sizeme";
import { CreateWUQueryStore, defaultSort, emptyFilter, Get, WUQueryStore, formatQuery } from "src/ESPWorkunit";
import * as WsWorkunits from "src/WsWorkunits";
import { formatCost } from "src/Session";
import { userKeyValStore } from "src/KeyValStore";
import nlsHPCC from "src/nlsHPCC";
import { useConfirm } from "../hooks/confirm";
import { useMyAccount } from "../hooks/user";
Expand Down Expand Up @@ -45,6 +46,13 @@ const defaultUIState = {
hasNotCompleted: false
};

const WORKUNITS_SHOWTIMELINE = "workunits_showTimeline";

export function resetWorkunitOptions() {
const store = userKeyValStore();
return store?.delete(WORKUNITS_SHOWTIMELINE);
}

interface WorkunitsProps {
filter?: { [id: string]: any };
sort?: QuerySortItem;
Expand All @@ -64,7 +72,7 @@ export const Workunits: React.FunctionComponent<WorkunitsProps> = ({
const [showFilter, setShowFilter] = React.useState(false);
const { currentUser } = useMyAccount();
const [uiState, setUIState] = React.useState({ ...defaultUIState });
const [showTimeline, setShowTimeline] = useUserStore<boolean>("workunits_showTimeline", true);
const [showTimeline, setShowTimeline] = useUserStore<boolean>(WORKUNITS_SHOWTIMELINE, true);
const {
selection, setSelection,
pageNum, setPageNum,
Expand Down
5 changes: 5 additions & 0 deletions esp/src/src-react/hooks/favorite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import { hashHistory } from "../util/history";
const STORE_FAVORITES_ID = "favorites";
const STORE_CACHE_TIMEOUT = 10000;

export function resetFavorites() {
const store = userKeyValStore();
return store?.delete(STORE_FAVORITES_ID);
}

interface Payload {
// TODO: Will be used for labels and extra info...
}
Expand Down
12 changes: 9 additions & 3 deletions esp/src/src-react/hooks/metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import { useCounter } from "./util";

const logger = scopedLogger("src-react/hooks/metrics.ts");

const MetricOptionsVersion = 2;
const METRIC_OPTIONS_VERSION = 2;
const METRIC_OPTIONS_KEY = `MetricOptions-${METRIC_OPTIONS_VERSION}`;

export function resetMetricsViews() {
const store = userKeyValStore();
return store?.delete(METRIC_OPTIONS_KEY);
}

export interface MetricsOptions {
scopeTypes: string[];
Expand Down Expand Up @@ -58,15 +64,15 @@ export function useMetricsOptions(): [MetricsOptions, (opts: MetricsOptions) =>

const save = React.useCallback(() => {
if (checkLayout(options)) {
store?.set(`MetricOptions-${MetricOptionsVersion}`, JSON.stringify(options), true);
store?.set(METRIC_OPTIONS_KEY, JSON.stringify(options), true);
}
}, [store]);

const reset = React.useCallback((toDefaults: boolean = false) => {
if (toDefaults) {
setOptions({ ...defaults });
} else {
store?.get(`MetricOptions-${MetricOptionsVersion}`).then(opts => {
store?.get(METRIC_OPTIONS_KEY).then(opts => {
const options = JSON.parse(opts);
checkLayout(options);
setOptions({ ...defaults, ...options });
Expand Down
6 changes: 3 additions & 3 deletions esp/src/src-react/hooks/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function fromString<T>(value: string, defaultValue: T): T {
}
}

function useStore<T>(store: IKeyValStore, key: string, defaultValue: T, monitor: boolean = false): [value: T, setValue: (value: T) => void, reset: () => void] {
function useStore<T>(store: IKeyValStore, key: string, defaultValue: T, monitor: boolean = false): [value: T, setValue: (value: T) => Promise<void>, reset: () => Promise<void>] {

const [value, setValue] = React.useState<T>();

Expand Down Expand Up @@ -66,13 +66,13 @@ function useStore<T>(store: IKeyValStore, key: string, defaultValue: T, monitor:
}, [defaultValue, key, monitor, store]);

const extSetValue = React.useCallback((value: T) => {
store.set(key, toString<T>(value, defaultValue), monitor).then(() => {
return store.set(key, toString<T>(value, defaultValue), monitor).then(() => {
setValue(value);
});
}, [defaultValue, key, monitor, store]);

const reset = React.useCallback(() => {
store.delete(key, monitor).then(() => {
return store.delete(key, monitor).then(() => {
setValue(defaultValue);
});
}, [defaultValue, key, monitor, store]);
Expand Down
10 changes: 9 additions & 1 deletion esp/src/src-react/hooks/theme.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import { Theme } from "@fluentui/react";
import { Theme as ThemeV9 } from "@fluentui/react-components";
import { userKeyValStore } from "src/KeyValStore";
import { darkTheme, lightTheme, darkThemeV9, lightThemeV9 } from "../themes";
import { useUserStore } from "./store";

const THEME = "theme";

export function resetTheme() {
const store = userKeyValStore();
return store?.delete(THEME);
}

export function useUserTheme(): { theme: Theme, themeV9: ThemeV9, setTheme: (value: "light" | "dark") => void, isDark: boolean } {

const [theme, setTheme] = useUserStore("theme", "light", true);
const [theme, setTheme] = useUserStore(THEME, "light", true);

return {
theme: theme === "dark" ? darkTheme : lightTheme,
Expand Down
7 changes: 6 additions & 1 deletion esp/src/src-react/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,17 @@ export const routes: RoutesEx = [
path: "/login",
action: (ctx) => import("./components/forms/Login").then(_ => <_.Login />)
},
{
mainNav: [],
name: "reset",
path: "/reset",
action: (ctx) => import("./components/Reset").then(_ => <_.ResetDialog />)
},
// Main ---
{
mainNav: ["activities"],
path: "",
action: (context) => pushUrl("/activities")

},
{
mainNav: ["activities"],
Expand Down
5 changes: 5 additions & 0 deletions esp/src/src-react/util/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ const globalHistory = globalThis.history;

const STORE_HISTORY_ID = "history";

export function resetHistory() {
const store = userKeyValStore();
return store?.delete(STORE_HISTORY_ID);
}

class History<S extends object = object> {

location: HistoryLocation = {
Expand Down
7 changes: 7 additions & 0 deletions esp/src/src/KeyValStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ class CookieStorage implements IKeyValStore {
});
}

async deleteAll(broadcast?: boolean): Promise<void> {
const cookies = Utility.parseCookies();
for (const cookie in cookies) {
await this.delete(cookie, broadcast);
}
}

monitor(callback: (messages: ValueChangedMessage[]) => void): IObserverHandle {
return this._dispatch.attach(callback);
}
Expand Down
9 changes: 9 additions & 0 deletions esp/src/src/Session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ export async function fetchModernMode(): Promise<string> {
});
}

export async function resetModernMode() {
await sessionStore.delete(ModernMode);
await userStore.delete(ModernMode);
}

export async function resetCookies() {
await cookieStore.deleteAll(true);
}

const isV5DirectURL = () => !!parseSearch(window.location.search)?.["Widget"];
const isV9DirectURL = () => window.location.hash && window.location.hash.indexOf("#/stub/") !== 0;

Expand Down
11 changes: 11 additions & 0 deletions esp/src/src/nls/hpcc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export = {
Content: "Content",
Contents: "Contents",
ContentType: "Content Type",
Cookies: "Cookies",
CookiesNoticeLinkText: "Our Cookie Notice",
CookiesAcceptButtonText: "Allow Cookies",
Copy: "Copy",
Expand Down Expand Up @@ -195,6 +196,7 @@ export = {
Deactivate: "Deactivate",
Debug: "Debug",
DEF: "DEF",
Default: "Default",
Defaults: "Defaults",
Definition: "Definition",
DefinitionID: "Definition ID",
Expand Down Expand Up @@ -267,6 +269,7 @@ export = {
ECL: "ECL",
ECLWatchRequiresCookies: "ECL Watch requires cookies enabled to continue.",
ECLWatchSessionManagement: "ECL Watch session management",
ECLWatchVersion: "ECL Watch Version",
ECLWorkunit: "ECL Workunit",
EdgeLabel: "Edge Label",
Edges: "Edges",
Expand Down Expand Up @@ -313,6 +316,7 @@ export = {
ExcludeIndexes: "Exclude Indexes",
ExpireDays: "Expire in (days)",
ExpirationDate: "Expiration Date",
FactoryReset: "Factory Reset",
FailIfNoSourceFile: "Fail If No Source File",
Fatal: "Fatal",
Favorites: "Favorites",
Expand Down Expand Up @@ -548,6 +552,7 @@ export = {
Message: "Message",
Methods: "Methods",
Metrics: "Metrics",
MetricOptions: "Metric Options",
MetricsGraph: "Metrics/Graph",
MetricsSQL: "Metrics (SQL)",
Min: "Min",
Expand Down Expand Up @@ -796,6 +801,7 @@ export = {
RequiredForXML: "Required for spraying XML",
Reschedule: "Reschedule",
Reset: "Reset",
ResetUserSettings: "Reset User Settings",
ResetThisQuery: "Reset This Query?",
ResetViewToSelection: "Reset View to Selection",
Resource: "Resource",
Expand Down Expand Up @@ -826,7 +832,10 @@ export = {
SampleRequest: "Sample Request",
SampleResponse: "Sample Response",
Save: "Save",
SaveAs: "Save As",
Scope: "Scope",
ScopeColumns: "Scope Columns",
ScopeTypes: "Scope Types",
SearchResults: "Search Results",
Seconds: "Seconds",
SecondsRemaining: "Seconds Remaining",
Expand Down Expand Up @@ -943,6 +952,7 @@ export = {
TechPreview: "Tech Preview",
Terminators: "Terminators",
TestPages: "Test Pages",
Theme: "Theme",
TheReturnedResults: "The returned results",
ThorNetworkAddress: "Thor Network Address",
ThorMasterAddress: "Thor Master Address",
Expand Down Expand Up @@ -1123,6 +1133,7 @@ export = {
Workflows: "Workflows",
Workunit: "Workunit",
WorkunitNotFound: "Workunit not found",
WorkunitOptions: "Workunit Options",
Workunits: "Workunits",
WorkUnitScopeDefaultPermissions: "Workunit Scope Default Permissions",
Wrap: "Wrap",
Expand Down

0 comments on commit 459df03

Please sign in to comment.