From 2f9a56aa6ff9da57219330bb58c5a35035185bba Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 9 Apr 2023 17:23:58 -0400 Subject: [PATCH] Centralize default user and team preferences. (#5172 Passing the fallback at each callpoint was dumb --- app/models/Team.ts | 16 +++++++--------- app/scenes/Login/index.tsx | 5 ++++- app/scenes/Settings/Features.tsx | 6 +++--- app/scenes/Settings/Preferences.tsx | 24 ++++++++++++------------ app/scenes/Settings/Security.tsx | 2 +- server/models/Team.ts | 14 ++++++++------ server/models/User.ts | 12 +++++++----- server/policies/document.ts | 2 +- server/presenters/publicTeam.ts | 3 ++- shared/constants.ts | 21 +++++++++++++++++++++ shared/types.ts | 1 + 11 files changed, 67 insertions(+), 39 deletions(-) diff --git a/app/models/Team.ts b/app/models/Team.ts index 42cd31be8..1f8c09cbf 100644 --- a/app/models/Team.ts +++ b/app/models/Team.ts @@ -1,4 +1,5 @@ import { computed, observable } from "mobx"; +import { TeamPreferenceDefaults } from "@shared/constants"; import { TeamPreference, TeamPreferences } from "@shared/types"; import { stringToColor } from "@shared/utils/color"; import BaseModel from "./BaseModel"; @@ -88,22 +89,19 @@ class Team extends BaseModel { */ @computed get seamlessEditing(): boolean { - return !!this.getPreference(TeamPreference.SeamlessEdit, true); + return !!this.getPreference(TeamPreference.SeamlessEdit); } /** - * Get the value for a specific preference key, or return the fallback if - * none is set. + * Returns the value of the provided preference. * - * @param key The TeamPreference key to retrieve - * @param fallback An optional fallback value, defaults to false. - * @returns The value + * @param preference The team preference to retrieve + * @returns The preference value if set, else the default value */ getPreference( - key: T, - fallback = false + key: T ): TeamPreferences[T] | false { - return this.preferences?.[key] ?? fallback; + return this.preferences?.[key] ?? TeamPreferenceDefaults[key] ?? false; } /** diff --git a/app/scenes/Login/index.tsx b/app/scenes/Login/index.tsx index 7eea4ded6..30d678bb1 100644 --- a/app/scenes/Login/index.tsx +++ b/app/scenes/Login/index.tsx @@ -7,6 +7,7 @@ import { useLocation, Link, Redirect } from "react-router-dom"; import styled from "styled-components"; import { getCookie, setCookie } from "tiny-cookie"; import { s } from "@shared/styles"; +import { UserPreference } from "@shared/types"; import { parseDomain } from "@shared/utils/domains"; import { Config } from "~/stores/AuthStore"; import ButtonLarge from "~/components/ButtonLarge"; @@ -61,7 +62,9 @@ function Login({ children }: Props) { const [error, setError] = React.useState(null); const [emailLinkSentTo, setEmailLinkSentTo] = React.useState(""); const isCreate = location.pathname === "/create"; - const rememberLastPath = !!auth.user?.preferences?.rememberLastPath; + const rememberLastPath = !!auth.user?.getPreference( + UserPreference.RememberLastPath + ); const [lastVisitedPath] = useLastVisitedPath(); const handleReset = React.useCallback(() => { diff --git a/app/scenes/Settings/Features.tsx b/app/scenes/Settings/Features.tsx index 612a2a41a..2a76a74ac 100644 --- a/app/scenes/Settings/Features.tsx +++ b/app/scenes/Settings/Features.tsx @@ -53,7 +53,7 @@ function Features() { @@ -71,7 +71,7 @@ function Features() { @@ -86,7 +86,7 @@ function Features() { diff --git a/app/scenes/Settings/Preferences.tsx b/app/scenes/Settings/Preferences.tsx index 99a8e495f..4a9713866 100644 --- a/app/scenes/Settings/Preferences.tsx +++ b/app/scenes/Settings/Preferences.tsx @@ -87,29 +87,29 @@ function Preferences() { /> @@ -117,16 +117,16 @@ function Preferences() { {t("Behavior")} diff --git a/app/scenes/Settings/Security.tsx b/app/scenes/Settings/Security.tsx index 79e7e941f..3f30bfd7b 100644 --- a/app/scenes/Settings/Security.tsx +++ b/app/scenes/Settings/Security.tsx @@ -264,7 +264,7 @@ function Security() { > diff --git a/server/models/Team.ts b/server/models/Team.ts index bb7b3c575..8ebaa9e33 100644 --- a/server/models/Team.ts +++ b/server/models/Team.ts @@ -20,6 +20,7 @@ import { AllowNull, AfterUpdate, } from "sequelize-typescript"; +import { TeamPreferenceDefaults } from "@shared/constants"; import { CollectionPermission, TeamPreference, @@ -196,14 +197,15 @@ class Team extends ParanoidModel { }; /** - * Returns the passed preference value + * Returns the value of the given preference. * - * @param preference The user preference to retrieve - * @param fallback An optional fallback value, defaults to false. - * @returns The preference value if set, else undefined + * @param preference The team preference to retrieve + * @returns The preference value if set, else the default value */ - public getPreference = (preference: TeamPreference, fallback = false) => - this.preferences?.[preference] ?? fallback; + public getPreference = (preference: TeamPreference) => + this.preferences?.[preference] ?? + TeamPreferenceDefaults[preference] ?? + false; provisionFirstCollection = async (userId: string) => { await this.sequelize!.transaction(async (transaction) => { diff --git a/server/models/User.ts b/server/models/User.ts index d85ede94d..8bb19849b 100644 --- a/server/models/User.ts +++ b/server/models/User.ts @@ -22,6 +22,7 @@ import { AllowNull, AfterUpdate, } from "sequelize-typescript"; +import { UserPreferenceDefaults } from "@shared/constants"; import { languages } from "@shared/i18n"; import type { NotificationSettings } from "@shared/types"; import { @@ -354,14 +355,15 @@ class User extends ParanoidModel { }; /** - * Returns the passed preference value + * Returns the value of the givem preference * * @param preference The user preference to retrieve - * @param fallback An optional fallback value, defaults to false. - * @returns The preference value if set, else undefined + * @returns The preference value if set, else the default value. */ - public getPreference = (preference: UserPreference, fallback = false) => - this.preferences?.[preference] ?? fallback; + public getPreference = (preference: UserPreference) => + this.preferences?.[preference] ?? + UserPreferenceDefaults[preference] ?? + false; collectionIds = async (options = {}) => { const collectionStubs = await Collection.scope({ diff --git a/server/policies/document.ts b/server/policies/document.ts index 6e962d1e4..8653a240d 100644 --- a/server/policies/document.ts +++ b/server/policies/document.ts @@ -35,7 +35,7 @@ allow(User, "download", Document, (user, document) => { if ( user.isViewer && - !user.team.getPreference(TeamPreference.ViewersCanExport, true) + !user.team.getPreference(TeamPreference.ViewersCanExport) ) { return false; } diff --git a/server/presenters/publicTeam.ts b/server/presenters/publicTeam.ts index 9eeed2331..4a6508941 100644 --- a/server/presenters/publicTeam.ts +++ b/server/presenters/publicTeam.ts @@ -1,9 +1,10 @@ +import { TeamPreference } from "@shared/types"; import { Team } from "@server/models"; export default function presentPublicTeam(team: Team) { return { name: team.name, avatarUrl: team.avatarUrl, - customTheme: team.preferences?.customTheme, + customTheme: team.getPreference(TeamPreference.CustomTheme), }; } diff --git a/shared/constants.ts b/shared/constants.ts index cc5abc5b2..09572c72b 100644 --- a/shared/constants.ts +++ b/shared/constants.ts @@ -1,3 +1,24 @@ +import { + TeamPreference, + TeamPreferences, + UserPreference, + UserPreferences, +} from "./types"; + export const USER_PRESENCE_INTERVAL = 5000; export const MAX_AVATAR_DISPLAY = 6; + +export const TeamPreferenceDefaults: TeamPreferences = { + [TeamPreference.SeamlessEdit]: true, + [TeamPreference.ViewersCanExport]: true, + [TeamPreference.PublicBranding]: false, + [TeamPreference.Commenting]: false, + [TeamPreference.CustomTheme]: undefined, +}; + +export const UserPreferenceDefaults: UserPreferences = { + [UserPreference.RememberLastPath]: true, + [UserPreference.UseCursorPointer]: true, + [UserPreference.CodeBlockLineNumers]: true, +}; diff --git a/shared/types.ts b/shared/types.ts index a0b2b75b3..db2765456 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -107,6 +107,7 @@ export enum UserPreference { RememberLastPath = "rememberLastPath", /** If web-style hand pointer should be used on interactive elements. */ UseCursorPointer = "useCursorPointer", + /** Whether code blocks should show line numbers. */ CodeBlockLineNumers = "codeBlockLineNumbers", }