Skip to content

Commit

Permalink
refactor: zen modal implemention and fix macos traffic light overlay
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <tukon479@gmail.com>
  • Loading branch information
Innei committed Nov 5, 2024
1 parent eaa5699 commit 51a4bd2
Show file tree
Hide file tree
Showing 22 changed files with 251 additions and 106 deletions.
13 changes: 13 additions & 0 deletions apps/main/src/menu.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { callWindowExpose } from "@follow/shared/bridge"
import { dispatchEventOnWindow } from "@follow/shared/event"
import { name } from "@pkg"
import type { BrowserWindow, MenuItem, MenuItemConstructorOptions } from "electron"
Expand Down Expand Up @@ -130,6 +131,18 @@ export const registerAppMenu = () => {
{ role: "zoomIn", label: t("menu.zoomIn") },
{ role: "zoomOut", label: t("menu.zoomOut") },
{ type: "separator" },
{
type: "normal",
label: t("menu.zenMode"),
accelerator: "Ctrl+Shift+Z",
click: () => {
const mainWindow = getMainWindow()
if (!mainWindow) return

const caller = callWindowExpose(mainWindow)
caller.zenMode()
},
},
{ role: "togglefullscreen", label: t("menu.toggleFullScreen") },
],
},
Expand Down
37 changes: 29 additions & 8 deletions apps/renderer/src/atoms/settings/ui.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createSettingAtom } from "@follow/atoms/helper/setting.js"
import type { UISettings } from "@follow/shared/interface/settings"
import { atom, useAtomValue } from "jotai"

import { internal_feedColumnShowAtom } from "../sidebar"
import { jotaiStore } from "@follow/utils/jotai"
import { atom, useAtomValue, useSetAtom } from "jotai"
import { useEventCallback } from "usehooks-ts"

export const createDefaultSettings = (): UISettings => ({
// Sidebar
Expand Down Expand Up @@ -42,6 +42,8 @@ export const createDefaultSettings = (): UISettings => ({
wideMode: false,
})

const zenModeAtom = atom(false)

export const {
useSettingKey: useUISettingKey,
useSettingSelector: useUISettingSelector,
Expand All @@ -61,9 +63,28 @@ export const uiServerSyncWhiteListKeys: (keyof UISettings)[] = [
"customCSS",
]

const isZenModeAtom = atom((get) => {
const ui = get(__uiSettingAtom)
return ui.wideMode && !get(internal_feedColumnShowAtom)
})
export const useIsZenMode = () => useAtomValue(zenModeAtom)
export const getIsZenMode = () => jotaiStore.get(zenModeAtom)

export const useSetZenMode = () => {
const setZenMode = useSetAtom(zenModeAtom)
return useEventCallback((checked: boolean) => {
setZenMode(checked)
})
}

export const useToggleZenMode = () => {
const setZenMode = useSetZenMode()
const isZenMode = useIsZenMode()
return useEventCallback(() => {
const newIsZenMode = !isZenMode
document.documentElement.dataset.zenMode = newIsZenMode.toString()
setZenMode(newIsZenMode)
})
}

export const useIsZenMode = () => useAtomValue(isZenModeAtom)
export const useRealInWideMode = () => {
const wideMode = useUISettingKey("wideMode")
const isZenMode = useIsZenMode()
return wideMode || isZenMode
}
24 changes: 16 additions & 8 deletions apps/renderer/src/atoms/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { atom } from "jotai"
import { getRouteParams } from "~/hooks/biz/useRouteParams"
import { createAtomHooks } from "~/lib/jotai"

import { getIsZenMode, useIsZenMode } from "./settings/ui"

const defaultFeedView = FeedViewType.Articles
const viewAtom = atom<FeedViewType | -1>(-1)
const [
Expand Down Expand Up @@ -48,14 +50,20 @@ viewAtom.onMount = () => {
setSidebarActiveView(view)
}
}
export const [
internal_feedColumnShowAtom,
,
useFeedColumnShow,
,
getFeedColumnShow,
setFeedColumnShow,
] = createAtomHooks(atom(true))
const [, , internal_useFeedColumnShow, , internal_getFeedColumnShow, setFeedColumnShow] =
createAtomHooks(atom(true))

export const useFeedColumnShow = () => {
const isZenMode = useIsZenMode()
return internal_useFeedColumnShow() && !isZenMode
}

export const getFeedColumnShow = () => {
const isZenMode = getIsZenMode()
return internal_getFeedColumnShow() && !isZenMode
}

export { setFeedColumnShow }

export const [, , useFeedColumnTempShow, , getFeedColumnTempShow, setFeedColumnTempShow] =
createAtomHooks(atom(false))
4 changes: 2 additions & 2 deletions apps/renderer/src/components/ui/markdown/components/Toc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
} from "react"
import { useEventCallback } from "usehooks-ts"

import { useUISettingKey } from "~/atoms/settings/ui"
import { useRealInWideMode } from "~/atoms/settings/ui"
import {
useGetWrappedElementPosition,
useWrappedElementPosition,
Expand Down Expand Up @@ -58,7 +58,7 @@ export const Toc: Component<TocProps> = ({ className, onItemClick }) => {

const renderContentElementPosition = useWrappedElementPosition()
const renderContentElementSize = useWrappedElementSize()
const entryContentInWideMode = useUISettingKey("wideMode")
const entryContentInWideMode = useRealInWideMode()
const shouldShowTitle = useViewport((v) => {
if (!entryContentInWideMode) return false
const { w } = v
Expand Down
4 changes: 4 additions & 0 deletions apps/renderer/src/constants/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ export const shortcuts: Shortcuts = {
name: "keys.layout.toggleWideMode",
key: "Meta+[",
},
zenMode: {
name: "keys.layout.zenMode",
key: "Control+Shift+Z",
},
},
entries: {
refetch: {
Expand Down
16 changes: 14 additions & 2 deletions apps/renderer/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,20 @@ initializeApp().finally(() => {

const $container = document.querySelector("#root") as HTMLElement

if (IN_ELECTRON && getOS() === "Windows") {
document.body.style.cssText += `--fo-window-padding-top: ${ElECTRON_CUSTOM_TITLEBAR_HEIGHT}px;`
if (IN_ELECTRON) {
const os = getOS()

switch (os) {
case "Windows": {
document.body.style.cssText += `--fo-window-padding-top: ${ElECTRON_CUSTOM_TITLEBAR_HEIGHT}px;`
break
}
case "macOS": {
document.body.style.cssText += `--fo-macos-traffic-light-width: 100px; --fo-macos-traffic-light-height: 30px;`
break
}
}
document.documentElement.dataset.os = getOS()
}

ReactDOM.createRoot($container).render(
Expand Down
41 changes: 30 additions & 11 deletions apps/renderer/src/modules/entry-column/layouts/EntryListHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { MdiMeditation } from "@follow/components/icons/Meditation.jsx"
import { ActionButton } from "@follow/components/ui/button/index.js"
import { DividerVertical } from "@follow/components/ui/divider/index.js"
import { RotatingRefreshIcon } from "@follow/components/ui/loading/index.jsx"
Expand All @@ -12,7 +13,13 @@ import * as React from "react"
import { useTranslation } from "react-i18next"

import { setGeneralSetting, useGeneralSettingKey } from "~/atoms/settings/general"
import { setUISetting, useUISettingKey } from "~/atoms/settings/ui"
import {
setUISetting,
useIsZenMode,
useRealInWideMode,
useSetZenMode,
useUISettingKey,
} from "~/atoms/settings/ui"
import { useWhoami } from "~/atoms/user"
import { ImpressionView } from "~/components/common/ImpressionTracker"
import { FEED_COLLECTION_LIST, ROUTE_ENTRY_PENDING, ROUTE_FEED_IN_LIST } from "~/constants"
Expand Down Expand Up @@ -234,9 +241,11 @@ const SwitchToMasonryButton = () => {
}

const WideModeButton = () => {
const isWideMode = useUISettingKey("wideMode")
const isWideMode = useRealInWideMode()
const isZenMode = useIsZenMode()
const { t } = useTranslation()

const setIsZenMode = useSetZenMode()
return (
<ImpressionView
event="Switch to Wide Mode"
Expand All @@ -247,23 +256,33 @@ const WideModeButton = () => {
<ActionButton
shortcut={shortcuts.layout.toggleWideMode.key}
onClick={() => {
setUISetting("wideMode", !isWideMode)
// TODO: Remove this after useMeasure can get bounds in time
window.dispatchEvent(new Event("resize"))
if (isZenMode) {
setIsZenMode(false)
} else {
setUISetting("wideMode", !isWideMode)
// TODO: Remove this after useMeasure can get bounds in time
window.dispatchEvent(new Event("resize"))
}
window.analytics?.capture("Switch to Wide Mode", {
wideMode: !isWideMode ? 1 : 0,
click: 1,
})
}}
tooltip={
!isWideMode
? t("entry_list_header.switch_to_widemode")
: t("entry_list_header.switch_to_normalmode")
isZenMode
? t("zen.exit")
: !isWideMode
? t("entry_list_header.switch_to_widemode")
: t("entry_list_header.switch_to_normalmode")
}
>
<i
className={cn(isWideMode ? "i-mgc-align-justify-cute-re" : "i-mgc-align-left-cute-re")}
/>
{isZenMode ? (
<MdiMeditation />
) : (
<i
className={cn(isWideMode ? "i-mgc-align-justify-cute-re" : "i-mgc-align-left-cute-re")}
/>
)}
</ActionButton>
</ImpressionView>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { cn, isSafari } from "@follow/utils/utils"
import { useDebounceCallback } from "usehooks-ts"

import { AudioPlayer, useAudioPlayerAtomSelector } from "~/atoms/player"
import { useUISettingKeys } from "~/atoms/settings/ui"
import { useRealInWideMode, useUISettingKey } from "~/atoms/settings/ui"
import { RelativeTime } from "~/components/ui/datetime"
import { Media } from "~/components/ui/media"
import { FEED_COLLECTION_LIST } from "~/constants"
Expand Down Expand Up @@ -62,7 +62,8 @@ export function ListItem({
{ leading: false },
)

const [settingWideMode, thumbnailRatio] = useUISettingKeys(["wideMode", "thumbnailRatio"])
const settingWideMode = useRealInWideMode()
const thumbnailRatio = useUISettingKey("thumbnailRatio")
const rid = `list-item-${entryId}`

// NOTE: prevent 0 height element, react virtuoso will not stop render any more
Expand Down
2 changes: 1 addition & 1 deletion apps/renderer/src/modules/entry-content/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function EntryHeaderImpl({
{!hideRecentReader && (
<div
className={cn(
"absolute left-5 top-0 flex h-full items-center gap-2 text-[13px] leading-none text-zinc-500",
"absolute left-5 top-0 flex h-full items-center gap-2 text-[13px] leading-none text-zinc-500 zen-mode-macos:left-12",
"visible z-[11]",
views[view].wideMode && "static",
shouldShowMeta && "hidden",
Expand Down
35 changes: 24 additions & 11 deletions apps/renderer/src/modules/feed-column/header.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Logo } from "@follow/components/icons/logo.jsx"
import { MdiMeditation } from "@follow/components/icons/Meditation.js"
import { ActionButton } from "@follow/components/ui/button/index.js"
import { Popover, PopoverContent, PopoverTrigger } from "@follow/components/ui/popover/index.jsx"
import { stopPropagation } from "@follow/utils/dom"
Expand All @@ -13,6 +14,7 @@ import { toast } from "sonner"

import { setAppSearchOpen } from "~/atoms/app"
import { useGeneralSettingKey } from "~/atoms/settings/general"
import { useIsZenMode, useSetZenMode } from "~/atoms/settings/ui"
import { setFeedColumnShow, useFeedColumnShow, useSidebarActiveView } from "~/atoms/sidebar"
import { useNavigateEntry } from "~/hooks/biz/useNavigateEntry"
import { useI18n } from "~/hooks/common"
Expand Down Expand Up @@ -81,7 +83,8 @@ const LayoutActionButton = () => {
const [animation, setAnimation] = useState({
width: !feedColumnShow ? "auto" : 0,
})

const isZenMode = useIsZenMode()
const setIsZenMode = useSetZenMode()
useEffect(() => {
setAnimation({
width: !feedColumnShow ? "auto" : 0,
Expand All @@ -95,18 +98,28 @@ const LayoutActionButton = () => {
return (
<m.div initial={animation} animate={animation} className="overflow-hidden">
<ActionButton
tooltip={t("app.toggle_sidebar")}
tooltip={isZenMode ? t("zen.exit") : t("app.toggle_sidebar")}
icon={
<i
className={cn(
!feedColumnShow
? "i-mgc-layout-leftbar-open-cute-re "
: "i-mgc-layout-leftbar-close-cute-re",
"text-theme-vibrancyFg",
)}
/>
isZenMode ? (
<MdiMeditation />
) : (
<i
className={cn(
!feedColumnShow
? "i-mgc-layout-leftbar-open-cute-re "
: "i-mgc-layout-leftbar-close-cute-re",
"text-theme-vibrancyFg",
)}
/>
)
}
onClick={() => setFeedColumnShow(!feedColumnShow)}
onClick={() => {
if (isZenMode) {
setIsZenMode(false)
} else {
setFeedColumnShow(!feedColumnShow)
}
}}
/>
</m.div>
)
Expand Down
19 changes: 6 additions & 13 deletions apps/renderer/src/modules/settings/tabs/apperance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ import { bundledThemesInfo } from "shiki/themes"
import {
getUISettings,
setUISetting,
useIsZenMode,
useToggleZenMode,
useUISettingKey,
useUISettingSelector,
useUISettingValue,
} from "~/atoms/settings/ui"
import { setFeedColumnShow, useFeedColumnShow } from "~/atoms/sidebar"
import { useCurrentModal, useModalStack } from "~/components/ui/modal/stacked/hooks"
import { isElectronBuild } from "~/constants"
import { useSetTheme } from "~/hooks/common"
Expand Down Expand Up @@ -246,22 +247,14 @@ export const AppThemeSegment = () => {

const ZenMode = () => {
const { t } = useTranslation("settings")
const feedColumnShow = useFeedColumnShow()
const isWideMode = useUISettingKey("wideMode")
const isZenMode = useIsZenMode()
const toggleZenMode = useToggleZenMode()
return (
<SettingItemGroup>
<SettingSwitch
checked={!feedColumnShow && isWideMode}
checked={isZenMode}
className="mt-4"
onCheckedChange={(checked) => {
if (checked) {
setFeedColumnShow(false)
setUISetting("wideMode", true)
} else {
setFeedColumnShow(true)
setUISetting("wideMode", false)
}
}}
onCheckedChange={toggleZenMode}
label={t("appearance.zen_mode.label")}
/>
<SettingDescription>{t("appearance.zen_mode.description")}</SettingDescription>
Expand Down
Loading

0 comments on commit 51a4bd2

Please sign in to comment.