Skip to content

Persist link display settings to workspace #1932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
1 change: 1 addition & 0 deletions apps/web/lib/swr/use-workspace-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useWorkspace from "./use-workspace";

export const STORE_KEYS = {
conversionsOnboarding: "conversionsOnboarding",
linksDisplay: "linksDisplay",
};

export function useWorkspaceStore<T>(
Expand Down
11 changes: 9 additions & 2 deletions apps/web/ui/links/link-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function LinkDisplay() {
const [openPopover, setOpenPopover] = useState(false);
const { queryParams } = useRouterStuff();

useKeyboardShortcut("a", () => setShowArchived((o) => !o));
useKeyboardShortcut("a", () => setShowArchived(!showArchived));

return (
<Popover
Expand Down Expand Up @@ -170,7 +170,14 @@ export default function LinkDisplay() {
className="h-8 w-auto px-2"
variant="primary"
text="Set as default"
onClick={persist}
onClick={() => {
if (
window.confirm(
"Set this configuration as the default for everyone in this workspace?",
)
)
persist();
}}
/>
</div>
</motion.div>
Expand Down
169 changes: 74 additions & 95 deletions apps/web/ui/links/links-display-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { STORE_KEYS, useWorkspaceStore } from "@/lib/swr/use-workspace-store";
import { useLocalStorage } from "@dub/ui";
import { useSearchParams } from "next/navigation";
import {
Dispatch,
PropsWithChildren,
SetStateAction,
createContext,
useMemo,
useState,
} from "react";
import { PropsWithChildren, createContext, useMemo } from "react";

export const linkViewModes = ["cards", "rows"] as const;

Expand Down Expand Up @@ -65,6 +58,9 @@ export const linkDisplayProperties: {

export type LinkDisplayProperty = (typeof linkDisplayPropertyIds)[number];

const defaultViewMode = linkViewModes[0];
const defaultSortBy = sortOptions[0].slug;
const defaultShowArchived = false;
export const defaultDisplayProperties: LinkDisplayProperty[] = [
"icon",
"link",
Expand All @@ -75,37 +71,38 @@ export const defaultDisplayProperties: LinkDisplayProperty[] = [
"analytics",
];

type PersistedLinksDisplay = {
viewMode: LinksViewMode;
sortBy: LinksSortSlug;
showArchived: boolean;
displayProperties: LinkDisplayProperty[];
};

function useLinksDisplayOption<T>(
key: string,
parsePersisted: (value: T) => T,
defaultValue: T,
overrideValue?: T,
parseValue: (value: any) => T,
) {
const [valuePersisted, setValuePersisted] = useLocalStorage<T>(
const [value, setValue] = useLocalStorage<T>(
`links-display-${key}`,
defaultValue,
);
const [value, setValue] = useState(overrideValue ?? valuePersisted);

return {
value,
value: parseValue(value),
setValue,
valuePersisted,
setValuePersisted,
persist: () => setValuePersisted(value),
reset: () => setValue(parsePersisted(valuePersisted)),
};
}

export const LinksDisplayContext = createContext<{
viewMode: LinksViewMode;
setViewMode: Dispatch<SetStateAction<LinksViewMode>>;
setViewMode: (mode: LinksViewMode) => void;
displayProperties: LinkDisplayProperty[];
setDisplayProperties: Dispatch<SetStateAction<LinkDisplayProperty[]>>;
setDisplayProperties: (properties: LinkDisplayProperty[]) => void;
sortBy: LinksSortSlug;
setSort: Dispatch<SetStateAction<LinksSortSlug>>;
setSort: (sort: LinksSortSlug) => void;
showArchived: boolean;
setShowArchived: Dispatch<SetStateAction<boolean>>;
setShowArchived: (show: boolean) => void;
isDirty: boolean;
persist: () => void;
reset: () => void;
Expand Down Expand Up @@ -134,94 +131,72 @@ const parseDisplayProperties = (displayPropertiesRaw: string[]) =>
(p) => displayPropertiesRaw.findIndex((pr) => pr === p) !== -1,
);

const parseSort = (sort: string) =>
sortOptions.find(({ slug }) => slug === sort)?.slug ?? sortOptions[0].slug;
const parseSortBy = (sortBy: string) =>
sortOptions.find(({ slug }) => slug === sortBy)?.slug ?? sortOptions[0].slug;

const parseShowArchived = (showArchived: boolean) => showArchived === true;

const parseObject = (object: any): PersistedLinksDisplay | undefined =>
object
? {
viewMode: parseViewMode(object.viewMode),
sortBy: parseSortBy(object.sortBy),
showArchived: parseShowArchived(object.showArchived),
displayProperties: parseDisplayProperties(object.displayProperties),
}
: undefined;

export function LinksDisplayProvider({ children }: PropsWithChildren) {
const searchParams = useSearchParams();
const sortRaw = searchParams?.get("sortBy");
const showArchivedRaw = searchParams?.get("showArchived");
// Persisted values to workspace store
const [persistedRaw, setPersisted] = useWorkspaceStore<PersistedLinksDisplay>(
STORE_KEYS.linksDisplay,
);
const persisted = useMemo(() => parseObject(persistedRaw), [persistedRaw]);

// View mode
const {
value: viewMode,
setValue: setViewMode,
valuePersisted: viewModePersisted,
persist: persistViewMode,
reset: resetViewMode,
} = useLinksDisplayOption<string>(
"view-mode",
parseViewMode,
linkViewModes[0],
);
const { value: viewMode, setValue: setViewMode } =
useLinksDisplayOption<LinksViewMode>(
"view-mode",
defaultViewMode,
parseViewMode,
);

// Sort
const {
value: sortBy,
setValue: setSort,
valuePersisted: sortPersisted,
persist: persistSort,
reset: resetSort,
} = useLinksDisplayOption<string>(
"sortBy",
parseSort,
sortOptions[0].slug,
sortRaw ? parseSort(sortRaw) : undefined,
);
const { value: sortBy, setValue: setSort } =
useLinksDisplayOption<LinksSortSlug>("sortBy", defaultSortBy, parseSortBy);

// Show archived
const {
value: showArchived,
setValue: setShowArchived,
valuePersisted: showArchivedPersisted,
persist: persistShowArchived,
reset: resetShowArchived,
} = useLinksDisplayOption<boolean>(
"show-archived",
parseShowArchived,
false,
showArchivedRaw ? showArchivedRaw === "true" : undefined,
);
const { value: showArchived, setValue: setShowArchived } =
useLinksDisplayOption<boolean>(
"show-archived",
defaultShowArchived,
parseShowArchived,
);

// Display properties
const {
value: displayProperties,
setValue: setDisplayProperties,
valuePersisted: displayPropertiesPersisted,
persist: persistDisplayProperties,
reset: resetDisplayProperties,
} = useLinksDisplayOption<LinkDisplayProperty[]>(
"display-properties",
parseDisplayProperties,
defaultDisplayProperties,
);
const { value: displayProperties, setValue: setDisplayProperties } =
useLinksDisplayOption<LinkDisplayProperty[]>(
"display-properties",
defaultDisplayProperties,
parseDisplayProperties,
);

const isDirty = useMemo(() => {
if (viewMode !== parseViewMode(viewModePersisted)) return true;
if (sortBy !== parseSort(sortPersisted)) return true;
if (showArchived !== parseShowArchived(showArchivedPersisted)) return true;
if (viewMode !== (persisted?.viewMode ?? defaultViewMode)) return true;
if (sortBy !== (persisted?.sortBy ?? defaultSortBy)) return true;
if (showArchived !== (persisted?.showArchived ?? defaultShowArchived))
return true;
if (
displayProperties.slice().sort().join(",") !==
parseDisplayProperties(displayPropertiesPersisted)
(persisted?.displayProperties ?? defaultDisplayProperties)
.slice()
.sort()
.join(",")
)
return true;

return false;
}, [
viewModePersisted,
viewMode,
sortPersisted,
sortBy,
showArchivedPersisted,
showArchived,
displayPropertiesPersisted,
displayProperties,
]);
}, [persisted, viewMode, sortBy, showArchived, displayProperties]);

return (
<LinksDisplayContext.Provider
Expand All @@ -236,16 +211,20 @@ export function LinksDisplayProvider({ children }: PropsWithChildren) {
setShowArchived,
isDirty,
persist: () => {
persistViewMode();
persistDisplayProperties();
persistSort();
persistShowArchived();
setPersisted({
viewMode: viewMode,
sortBy: sortBy,
showArchived: showArchived,
displayProperties: displayProperties,
});
},
reset: () => {
resetViewMode();
resetDisplayProperties();
resetSort();
resetShowArchived();
setViewMode(persisted?.viewMode ?? defaultViewMode);
setDisplayProperties(
persisted?.displayProperties ?? defaultDisplayProperties,
);
setSort(persisted?.sortBy ?? defaultSortBy);
setShowArchived(persisted?.showArchived ?? defaultShowArchived);
},
}}
>
Expand Down
Loading