From 72ff384187ecf5d3247139481f83c140a1264b76 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Tue, 12 Aug 2025 20:12:06 +0300 Subject: [PATCH] refactor(call_to_action): clean up --- .../src/widgets/dialogs/call_to_action.tsx | 8 ++-- .../dialogs/call_to_action_definitions.ts | 41 ++++++++++++++++++- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/apps/client/src/widgets/dialogs/call_to_action.tsx b/apps/client/src/widgets/dialogs/call_to_action.tsx index 60873e2b0..2bdb97866 100644 --- a/apps/client/src/widgets/dialogs/call_to_action.tsx +++ b/apps/client/src/widgets/dialogs/call_to_action.tsx @@ -5,13 +5,13 @@ import ReactBasicWidget from "../react/ReactBasicWidget"; import { CallToAction, dismissCallToAction, getCallToActions } from "./call_to_action_definitions"; function CallToActionDialogComponent({ activeCallToActions }: { activeCallToActions: CallToAction[] }) { - const [ activeIndex, setActiveIndex ] = useState(0); - const [ shown, setShown ] = useState(true); - const activeItem = activeCallToActions[activeIndex]; - if (!activeCallToActions.length) { return <>; } + + const [ activeIndex, setActiveIndex ] = useState(0); + const [ shown, setShown ] = useState(true); + const activeItem = activeCallToActions[activeIndex]; function goToNext() { if (activeIndex + 1 < activeCallToActions.length) { diff --git a/apps/client/src/widgets/dialogs/call_to_action_definitions.ts b/apps/client/src/widgets/dialogs/call_to_action_definitions.ts index 84892ef33..93454cf35 100644 --- a/apps/client/src/widgets/dialogs/call_to_action_definitions.ts +++ b/apps/client/src/widgets/dialogs/call_to_action_definitions.ts @@ -2,13 +2,40 @@ import utils from "../../services/utils"; import options from "../../services/options"; import { t } from "../../services/i18n"; +/** + * A "call-to-action" is an interactive message for the user, generally to present new features. + */ export interface CallToAction { + /** + * A unique identifier to allow the call-to-action to be dismissed by the user and then never shown again. + */ id: string; + /** + * The title of the call-to-action, displayed as a heading in the message. + */ title: string; + /** + * The message body of the call-to-action. + */ message: string; + /** + * Function that determines whether the call-to-action can be displayed to the user. The check can be based on options or + * the platform of the user. If the user already dismissed this call-to-action, the value of this function doesn't matter. + * + * @returns whether to allow this call-to-action or to skip it, based on the user's environment. + */ enabled: () => boolean; + /** + * A list of buttons to display in the footer of the modal. + */ buttons: { + /** + * The text displayed on the button. + */ text: string; + /** + * The listener that will be called when the button is pressed. Async methods are supported and will be awaited before proceeding to the next step. + */ onClick: () => (void | Promise); }[]; } @@ -38,7 +65,7 @@ const CALL_TO_ACTIONS: CallToAction[] = [ id: "background_effects", title: t("call_to_action.background_effects_title"), message: t("call_to_action.background_effects_message"), - enabled: () => isNextTheme() && !options.is("backgroundEffects"), + enabled: () => utils.isElectron() && window.glob.platform === "win32" && isNextTheme() && !options.is("backgroundEffects"), buttons: [ { text: t("call_to_action.background_effects_button"), @@ -51,6 +78,12 @@ const CALL_TO_ACTIONS: CallToAction[] = [ } ]; +/** + * Obtains the list of available call-to-actions, meaning those that are enabled based on the user's environment but also with + * without the call-to-actions already dismissed by the user. + * + * @returns a list iof call to actions to display to the user. + */ export function getCallToActions() { const seenCallToActions = new Set(getSeenCallToActions()); @@ -58,6 +91,12 @@ export function getCallToActions() { !seenCallToActions.has(callToAction.id) && callToAction.enabled()); } +/** + * Marks the call-to-action as dismissed by the user, meaning that {@link getCallToActions()} will no longer list this particular action. + * + * @param id the corresponding ID of the call to action. + * @returns a promise with the option saving. Generally it's best to wait for the promise to resolve before refreshing the page. + */ export async function dismissCallToAction(id: string) { const seenCallToActions = getSeenCallToActions(); if (seenCallToActions.find(seenId => seenId === id)) {