diff --git a/apps/desktop/layer/renderer/src/components/ui/modal/stacked/hooks.tsx b/apps/desktop/layer/renderer/src/components/ui/modal/stacked/hooks.tsx index 1d5ca0ded..82a0ed257 100644 --- a/apps/desktop/layer/renderer/src/components/ui/modal/stacked/hooks.tsx +++ b/apps/desktop/layer/renderer/src/components/ui/modal/stacked/hooks.tsx @@ -126,10 +126,45 @@ export const useDialog = (): DialogInstance => { const { present } = useModalStack() const { t } = useTranslation() return { + /** + * Show a confirmation dialog with different visual variants + * @param options.variant - Visual style variant: + * - "ask" (default): Standard confirmation dialog + * - "warning": Warning dialog with yellow icon and yellow confirm button + * - "danger": Danger dialog with red icon and red confirm button + */ ask: useEventCallback((options) => { + const variant = options.variant || "ask" + + // Variant-specific configuration + const variantConfig = { + ask: { + icon: null, + confirmVariant: "primary" as const, + confirmClassName: "", + }, + warning: { + icon: , + confirmVariant: "primary" as const, + confirmClassName: "bg-yellow-600 hover:bg-yellow-700", + }, + danger: { + icon: , + confirmVariant: "primary" as const, + confirmClassName: "bg-red-600 hover:bg-red-700", + }, + } + + const config = variantConfig[variant] + return new Promise((resolve) => { present({ - title: options.title, + title: ( + + {config.icon} + {options.title} + + ), content: ({ dismiss }) => ( {options.message} @@ -146,6 +181,8 @@ export const useDialog = (): DialogInstance => { {options.cancelText ?? t("words.cancel", { ns: "common" })} { options.onConfirm?.() resolve(true) diff --git a/apps/desktop/layer/renderer/src/components/ui/modal/stacked/types.tsx b/apps/desktop/layer/renderer/src/components/ui/modal/stacked/types.tsx index 269e5c32a..5a814b5e9 100644 --- a/apps/desktop/layer/renderer/src/components/ui/modal/stacked/types.tsx +++ b/apps/desktop/layer/renderer/src/components/ui/modal/stacked/types.tsx @@ -37,6 +37,7 @@ export interface DialogInstance { ask: (options: { title: string message: string + variant?: "ask" | "warning" | "danger" onConfirm?: () => void onCancel?: () => void confirmText?: string diff --git a/apps/desktop/layer/renderer/src/modules/settings/control.tsx b/apps/desktop/layer/renderer/src/modules/settings/control.tsx index 3fa329ad8..dc866ee86 100644 --- a/apps/desktop/layer/renderer/src/modules/settings/control.tsx +++ b/apps/desktop/layer/renderer/src/modules/settings/control.tsx @@ -34,10 +34,13 @@ export const SettingSwitch: Component<{ onCheckedChange: (checked: boolean) => void }> = ({ checked, label, onCheckedChange, className }) => { const id = useId() + const handleCheckedChange = (checked: boolean) => { + onCheckedChange(checked) + } return ( {titleCase(label)} - + ) } diff --git a/apps/desktop/layer/renderer/src/modules/settings/helper/setting-builder.tsx b/apps/desktop/layer/renderer/src/modules/settings/helper/setting-builder.tsx index ba950413f..85e648449 100644 --- a/apps/desktop/layer/renderer/src/modules/settings/helper/setting-builder.tsx +++ b/apps/desktop/layer/renderer/src/modules/settings/helper/setting-builder.tsx @@ -15,6 +15,7 @@ export type SettingItem = { label: string description?: string onChange: (value: T[K]) => void + onChangeGuard?: (value: T[K]) => "handled" | void type?: "password" vertical?: boolean @@ -92,7 +93,15 @@ export const createSettingBuilder = assertSetting.onChange(checked as T[keyof T])} + onCheckedChange={(checked) => { + if (assertSetting.onChangeGuard) { + const handled = assertSetting.onChangeGuard(checked as T[keyof T]) + if (handled === "handled") { + return + } + assertSetting.onChange(checked as T[keyof T]) + } + }} label={assertSetting.label} /> ) diff --git a/apps/desktop/layer/renderer/src/modules/settings/tabs/general.tsx b/apps/desktop/layer/renderer/src/modules/settings/tabs/general.tsx index c4b7b3a11..950f9f680 100644 --- a/apps/desktop/layer/renderer/src/modules/settings/tabs/general.tsx +++ b/apps/desktop/layer/renderer/src/modules/settings/tabs/general.tsx @@ -20,6 +20,7 @@ import { useGeneralSettingSelector, useGeneralSettingValue, } from "~/atoms/settings/general" +import { useDialog } from "~/components/ui/modal/stacked/hooks" import { useProxyValue, useSetProxy } from "~/hooks/biz/useProxySetting" import { useMinimizeToTrayValue, useSetMinimizeToTray } from "~/hooks/biz/useTraySetting" import { fallbackLanguage } from "~/i18n" @@ -65,6 +66,7 @@ export const SettingGeneral = () => { const isMobile = useMobile() + const { ask } = useDialog() const reRenderKey = useGeneralSettingKey("enhancedSettings") return ( @@ -167,9 +169,25 @@ export const SettingGeneral = () => { IN_ELECTRON && NettingSetting, { type: "title", value: t("general.advanced") }, + defineSettingItem("enhancedSettings", { label: t("general.enhanced.label"), description: t("general.enhanced.description"), + onChangeGuard(value) { + if (value) { + ask({ + variant: "danger", + title: t("general.enhanced.enable.modal.title"), + message: t("general.enhanced.enable.modal.description"), + confirmText: t("general.enhanced.enable.modal.confirm"), + cancelText: t("general.enhanced.enable.modal.cancel"), + onConfirm: () => { + setGeneralSetting("enhancedSettings", value) + }, + }) + return "handled" + } + }, }), ]} /> diff --git a/locales/settings/en.json b/locales/settings/en.json index 8ed8153a0..551cc047e 100644 --- a/locales/settings/en.json +++ b/locales/settings/en.json @@ -167,16 +167,20 @@ "general.app": "App", "general.auto_expand_long_social_media.description": "Automatically expand social media entries containing long text.", "general.auto_expand_long_social_media.label": "Expand long social media", - "general.auto_group.description": "Automatically group feeds by site domain.", - "general.auto_group.label": "Auto Group", + "general.auto_group.description": "Group feeds from the same website domain together.", + "general.auto_group.label": "Auto Group feeds by site", "general.cache": "Cache", "general.content": "Content", "general.data": "Data", "general.data_file.label": "Data File", "general.data_persist.description": "Persist data locally to enable offline access and local search.", "general.data_persist.label": "Persist data for offline usage", - "general.enhanced.description": "Enabling the enhanced settings offers more customization options, but it may also introduce unforeseen issues.", + "general.enhanced.description": "Enabling the enhanced settings offers more customization options, but may also introduce unforeseen issues. !!! Keep this as the gateway to truly experimental/complex things if needed later, otherwise it might be removable if other settings cover specifics.", "general.enhanced.disabled.tip": "Enhanced settings are disabled, you can enable them in the General settings - Advanced.", + "general.enhanced.enable.modal.cancel": "Cancel", + "general.enhanced.enable.modal.confirm": "Enable", + "general.enhanced.enable.modal.description": "Enabling the enhanced settings offers more customization options, but may also introduce unforeseen issues. Do not enable this unless you know what you are doing.", + "general.enhanced.enable.modal.title": "Enable Enhanced Settings", "general.enhanced.enabled.tip": "Enhanced settings are enabled, you can disable them in the General settings - Advanced.", "general.enhanced.label": "Enhanced Settings", "general.export.button": "Export", @@ -204,11 +208,11 @@ "general.log_file.description": "Reveal the log file in the system.", "general.log_file.label": "Log File", "general.mark_as_read.hover.description": "Automatically mark entries as read when hovered.", - "general.mark_as_read.hover.label": "Mark as read when hovering", - "general.mark_as_read.render.description": "Automatically mark single-level entries (e.g. social media posts, pictures, video views) as read when they enter the view.", - "general.mark_as_read.render.label": "Mark as read when in the view", + "general.mark_as_read.hover.label": "When hovering over article", + "general.mark_as_read.render.description": "Mark items like social posts or images as read immediately.", + "general.mark_as_read.render.label": "Single items when they enter view", "general.mark_as_read.scroll.description": "Automatically mark entries as read when scrolled out of the view.", - "general.mark_as_read.scroll.label": "Mark as read when scrolling", + "general.mark_as_read.scroll.label": "When scrolling past article", "general.mark_as_read.title": "Mark as read", "general.minimize_to_tray.description": "Minimize to system tray when closing window", "general.minimize_to_tray.label": "Minimize to tray", @@ -228,7 +232,7 @@ "general.send_anonymous_data.label": "Send anonymous data", "general.show_quick_timeline.description": "Show the quick timeline at the top of the feed list.", "general.show_quick_timeline.label": "Show feed list timeline", - "general.show_unread_on_launch.description": "Only show unread entries when you open the app.", + "general.show_unread_on_launch.description": "Automatically filter to unread content when the app starts", "general.show_unread_on_launch.label": "Unread only on launch", "general.sidebar_title": "General", "general.startup_screen.subscription": "Subscription", diff --git a/locales/settings/ja.json b/locales/settings/ja.json index d6484bfaa..98e887c18 100644 --- a/locales/settings/ja.json +++ b/locales/settings/ja.json @@ -201,11 +201,11 @@ "general.log_file.description": "システムにログファイルがあると表示します。", "general.log_file.label": "ログファイル", "general.mark_as_read.hover.description": "ホバー時にエントリを自動的に既読にします。", - "general.mark_as_read.hover.label": "ホバーで既読にする", - "general.mark_as_read.render.description": "表示されると、単一レベルのエントリ(例:ソーシャルメディアの投稿、画像、動画)が自動的に既読になります。", - "general.mark_as_read.render.label": "表示中に既読にする", + "general.mark_as_read.hover.label": "記事にホバーしたとき", + "general.mark_as_read.render.description": "ソーシャルメディアの投稿や画像などの単一項目を即座に既読にマークします。", + "general.mark_as_read.render.label": "単一項目がビューに入ったとき", "general.mark_as_read.scroll.description": "表示からスクロールアウトしたときにエントリを自動的に既読にします。", - "general.mark_as_read.scroll.label": "スクロール時に既読にする", + "general.mark_as_read.scroll.label": "記事をスクロールして通り過ぎたとき", "general.mark_as_read.title": "既読にする", "general.minimize_to_tray.description": "ウィンドウを閉じるとシステムトレイに最小化します", "general.minimize_to_tray.label": "トレイに最小化", diff --git a/locales/settings/zh-CN.json b/locales/settings/zh-CN.json index 46b256b87..9ac0c0903 100644 --- a/locales/settings/zh-CN.json +++ b/locales/settings/zh-CN.json @@ -201,11 +201,11 @@ "general.log_file.description": "在系统中显示日志文件。", "general.log_file.label": "日志文件", "general.mark_as_read.hover.description": "当鼠标悬停在条目时自动将其标记为已读。", - "general.mark_as_read.hover.label": "悬停时标记为已读", - "general.mark_as_read.render.description": "当单级条目(例如社交媒体贴文、图片、视频观看)进入可视区域时自动将其标记为已读。", - "general.mark_as_read.render.label": "在可视区域中时标记为已读", + "general.mark_as_read.hover.label": "悬停在文章上时", + "general.mark_as_read.render.description": "将社交媒体贴文或图片等单项内容立即标记为已读。", + "general.mark_as_read.render.label": "单项内容进入视图时", "general.mark_as_read.scroll.description": "当条目滚动出可视区域时自动将其标记为已读。", - "general.mark_as_read.scroll.label": "滚动时标记为已读", + "general.mark_as_read.scroll.label": "滚动浏览文章时", "general.mark_as_read.title": "标记已读", "general.minimize_to_tray.description": "关闭窗口时最小化到系统托盘。", "general.minimize_to_tray.label": "最小化到托盘", diff --git a/locales/settings/zh-TW.json b/locales/settings/zh-TW.json index 39724429c..f9d23dcda 100644 --- a/locales/settings/zh-TW.json +++ b/locales/settings/zh-TW.json @@ -201,11 +201,11 @@ "general.log_file.description": "在系統中顯示記錄檔案。", "general.log_file.label": "記錄檔案", "general.mark_as_read.hover.description": "游標懸停時自動將條目標記為已讀。", - "general.mark_as_read.hover.label": "滑過時標記為已讀", - "general.mark_as_read.render.description": "當單層級條目(例如社群媒體貼文、圖片、影片瀏覽)進入視圖時自動標記為已讀。", - "general.mark_as_read.render.label": "顯示時標記為已讀", + "general.mark_as_read.hover.label": "滑鼠懸停在文章上時", + "general.mark_as_read.render.description": "將社群媒體貼文或圖片等單項內容立即標記為已讀。", + "general.mark_as_read.render.label": "單項內容進入視圖時", "general.mark_as_read.scroll.description": "當條目捲動離開視圖時自動標記為已讀。", - "general.mark_as_read.scroll.label": "捲動時標記為已讀", + "general.mark_as_read.scroll.label": "捲動瀏覽文章時", "general.mark_as_read.title": "標記為已讀", "general.minimize_to_tray.description": "關閉視窗時最小化到工作列通知區域", "general.minimize_to_tray.label": "最小化到通知區域",