Skip to content

Commit 7ad1e30

Browse files
authored
fix: window titlebar maximize state sync, fixed #1982 (#1989)
Signed-off-by: Innei <tukon479@gmail.com>
1 parent 810516a commit 7ad1e30

File tree

9 files changed

+93
-28
lines changed

9 files changed

+93
-28
lines changed

apps/main/src/tipc/app.ts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url"
66

77
import { getRendererHandlers } from "@egoist/tipc/main"
88
import { callWindowExpose } from "@follow/shared/bridge"
9-
import { app, BrowserWindow, clipboard, dialog, screen, shell } from "electron"
9+
import { app, BrowserWindow, clipboard, dialog, shell } from "electron"
1010

1111
import { registerMenuAndContextMenu } from "~/init"
1212
import { clearAllData, getCacheSize } from "~/lib/cleaner"
@@ -15,7 +15,7 @@ import { registerAppTray } from "~/lib/tray"
1515
import { logger, revealLogFile } from "~/logger"
1616
import { cleanupOldRender, loadDynamicRenderEntry } from "~/updater/hot-updater"
1717

18-
import { isDev, isWindows11 } from "../env"
18+
import { isDev } from "../env"
1919
import { downloadFile } from "../lib/download"
2020
import { i18n } from "../lib/i18n"
2121
import { cleanBetterAuthSessionCookie, cleanUser } from "../lib/user"
@@ -144,25 +144,7 @@ export const appRoute = {
144144
}
145145
}
146146
}),
147-
getWindowIsMaximized: t.procedure.input<void>().action(async ({ context }) => {
148-
const window: BrowserWindow | null = (context.sender as Sender).getOwnerBrowserWindow()
149147

150-
if (isWindows11 && window) {
151-
const size = screen.getDisplayMatching(window.getBounds()).workAreaSize
152-
153-
const windowSize = window.getSize()
154-
const windowPosition = window.getPosition()
155-
const isMaximized =
156-
windowSize[0] === size.width &&
157-
windowSize[1] === size.height &&
158-
windowPosition[0] === 0 &&
159-
windowPosition[1] === 0
160-
161-
return !!isMaximized
162-
}
163-
164-
return window?.isMaximized()
165-
}),
166148
quitAndInstall: t.procedure.action(async () => {
167149
quitAndInstall()
168150
}),

apps/main/src/window.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url"
33

44
import { is } from "@electron-toolkit/utils"
55
import { APP_PROTOCOL } from "@follow/shared"
6-
import { callWindowExpose } from "@follow/shared/bridge"
6+
import { callWindowExpose, WindowState } from "@follow/shared/bridge"
77
import type { BrowserWindowConstructorOptions } from "electron"
88
import { app, BrowserWindow, screen, shell } from "electron"
99
import type { Event } from "electron/main"
@@ -197,6 +197,27 @@ export function createWindow(
197197
})
198198
}
199199

200+
// async render and main state
201+
window.on("maximize", async () => {
202+
const caller = callWindowExpose(window)
203+
await caller.setWindowState(WindowState.MAXIMIZED)
204+
})
205+
206+
window.on("unmaximize", async () => {
207+
const caller = callWindowExpose(window)
208+
await caller.setWindowState(WindowState.NORMAL)
209+
})
210+
211+
window.on("minimize", async () => {
212+
const caller = callWindowExpose(window)
213+
await caller.setWindowState(WindowState.MINIMIZED)
214+
})
215+
216+
window.on("restore", async () => {
217+
const caller = callWindowExpose(window)
218+
await caller.setWindowState(WindowState.NORMAL)
219+
})
220+
200221
return window
201222
}
202223
export const windowStateStoreKey = "windowState"

apps/renderer/src/atoms/app.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
import { WindowState } from "@follow/shared/bridge"
12
import { atom } from "jotai"
23

34
import { createAtomHooks } from "~/lib/jotai"
45

56
export const [, , useAppIsReady, , appIsReady, setAppIsReady] = createAtomHooks(atom(false))
67

78
export const [, , useAppSearchOpen, , , setAppSearchOpen] = createAtomHooks(atom(false))
9+
10+
// For electron
11+
export const [, , useWindowState, , windowState, setWindowState] = createAtomHooks(
12+
atom<WindowState>(WindowState.NORMAL),
13+
)

apps/renderer/src/modules/app/Titlebar.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
import { useQuery } from "@tanstack/react-query"
1+
import { WindowState } from "@follow/shared/bridge"
22

3+
import { useWindowState } from "~/atoms/app"
34
import { useUISettingKey } from "~/atoms/settings/ui"
45
import { ElECTRON_CUSTOM_TITLEBAR_HEIGHT } from "~/constants"
56
import { tipcClient } from "~/lib/client"
67

78
export const Titlebar = () => {
8-
const { data: isMaximized, refetch } = useQuery({
9-
queryFn: () => tipcClient?.getWindowIsMaximized(),
10-
queryKey: ["windowIsMaximized"],
11-
})
9+
const isMaximized = useWindowState() === WindowState.MAXIMIZED
1210

1311
const feedColWidth = useUISettingKey("feedColWidth")
1412

@@ -35,7 +33,6 @@ export const Titlebar = () => {
3533
className="no-drag-region pointer-events-auto flex h-full w-[50px] items-center justify-center duration-200 hover:bg-theme-item-active"
3634
onClick={async () => {
3735
await tipcClient?.windowAction({ action: "maximum" })
38-
refetch()
3936
}}
4037
>
4138
{isMaximized ? (

apps/renderer/src/providers/extension-expose-provider.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useEffect, useLayoutEffect } from "react"
44
import { useTranslation } from "react-i18next"
55
import { toast } from "sonner"
66

7+
import { setWindowState } from "~/atoms/app"
78
import { getGeneralSettings } from "~/atoms/settings/general"
89
import { getUISettings, useToggleZenMode } from "~/atoms/settings/ui"
910
import { setUpdaterStatus } from "~/atoms/updater"
@@ -85,5 +86,15 @@ export const ExtensionExposeProvider = () => {
8586
dialog,
8687
})
8788
}, [dialog])
89+
90+
useBindElectronBridge()
8891
return null
8992
}
93+
94+
const useBindElectronBridge = () => {
95+
useEffect(() => {
96+
registerGlobalContext({
97+
setWindowState,
98+
})
99+
}, [])
100+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { MemoedDangerousHTMLStyle } from "@follow/components/common/MemoedDangerousHTMLStyle.jsx"
2+
import { RootPortal } from "@follow/components/ui/portal/index.js"
3+
import type { FC, PropsWithChildren } from "react"
4+
import { createContext, useCallback, useContext, useState } from "react"
5+
6+
const Provider = createContext<(id: string, styles: string) => () => void>(() => () => {})
7+
export const PortalInjectStylesProvider: FC<PropsWithChildren> = ({ children }) => {
8+
const [styles, setStyles] = useState({} as Record<string, string>)
9+
10+
const injectStyles = useCallback((id: string, styles: string) => {
11+
const dispose = () => {
12+
setStyles((prev) => {
13+
const { [id]: _, ...rest } = prev
14+
return rest
15+
})
16+
}
17+
if (styles[id]) return dispose
18+
setStyles((prev) => ({ ...prev, [id]: styles }))
19+
20+
return dispose
21+
}, [])
22+
return (
23+
<Provider.Provider value={injectStyles}>
24+
<RootPortal to={document.head}>
25+
{Object.entries(styles).map(([id, style]) => (
26+
<MemoedDangerousHTMLStyle key={id} id={`inject-${id}`}>
27+
{style}
28+
</MemoedDangerousHTMLStyle>
29+
))}
30+
</RootPortal>
31+
{children}
32+
</Provider.Provider>
33+
)
34+
}
35+
36+
export const useInjectStyles = () => {
37+
return useContext(Provider)
38+
}

apps/renderer/src/providers/root-providers.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
4747
<SettingSync />
4848
<FollowCommandManager />
4949
{import.meta.env.DEV && <Devtools />}
50+
5051
{children}
5152

5253
<Suspense>

apps/server/client/providers/root-providers.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
2020
<StableRouterProvider />
2121
<ModalStackContainer>
2222
<OpenInAppDetector />
23-
{/* <MobileDetector /> */}
23+
2424
<UserProvider />
2525
<Toaster />
2626
{children}

packages/shared/src/bridge.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ declare const dialog: {
1717
cancelText?: string
1818
}) => Promise<boolean>
1919
}
20+
21+
export enum WindowState {
22+
MINIMIZED = "minimized",
23+
MAXIMIZED = "maximized",
24+
NORMAL = "normal",
25+
}
2026
interface RenderGlobalContext {
2127
/// Access Settings
2228
showSetting: (path?: string) => void
@@ -46,6 +52,9 @@ interface RenderGlobalContext {
4652
/// Utils
4753
toast: typeof toast
4854

55+
/// Electron State
56+
setWindowState: (state: WindowState) => void
57+
4958
readyToUpdate: () => void
5059
dialog: typeof dialog
5160
// URL

0 commit comments

Comments
 (0)