fix: Back/forward controls in desktop app (#12046)

* fix: Back/forward controls in desktop app

* PR feedback
This commit is contained in:
Tom Moor
2026-04-14 18:43:26 -04:00
committed by GitHub
parent 46f1f99ce6
commit d3f1884fa7
4 changed files with 42 additions and 34 deletions
+1 -1
View File
@@ -57,7 +57,6 @@ function AppSidebar() {
return (
<Sidebar hidden={!ui.readyToShow} ref={handleSidebarRef}>
<HistoryNavigation />
{dndArea && (
<DndProvider backend={HTML5Backend} options={html5Options}>
<DragPlaceholder />
@@ -133,6 +132,7 @@ function AppSidebar() {
</Scrollable>
</DndProvider>
)}
<HistoryNavigation />
</Sidebar>
);
}
+1 -1
View File
@@ -44,7 +44,6 @@ function SettingsSidebar() {
return (
<Sidebar>
<HistoryNavigation />
<SidebarButton
title={t("Return to App")}
image={<StyledBackIcon />}
@@ -96,6 +95,7 @@ function SettingsSidebar() {
)}
</Scrollable>
</Flex>
<HistoryNavigation />
</Sidebar>
);
}
@@ -3,54 +3,50 @@ import * as React from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import { s } from "@shared/styles";
import { isMac } from "@shared/utils/browser";
import Flex from "~/components/Flex";
import NudeButton from "~/components/NudeButton";
import Tooltip from "~/components/Tooltip";
import useKeyDown from "~/hooks/useKeyDown";
import Desktop from "~/utils/Desktop";
function HistoryNavigation(props: React.ComponentProps<typeof Flex>) {
const { t } = useTranslation();
const [back, setBack] = React.useState(false);
const [forward, setForward] = React.useState(false);
const [canGoBack, setCanGoBack] = React.useState(false);
const [canGoForward, setCanGoForward] = React.useState(false);
const [supported, setSupported] = React.useState(false);
useKeyDown(
(event) =>
isMac
? event.metaKey && event.key === "["
: event.altKey && event.key === "ArrowLeft",
() => {
setBack(true);
setTimeout(() => setBack(false), 100);
React.useEffect(() => {
if (!(Desktop.bridge && "onNavigationStateChanged" in Desktop.bridge)) {
return;
}
);
setSupported(true);
return Desktop.bridge.onNavigationStateChanged((state) => {
setCanGoBack(state.canGoBack);
setCanGoForward(state.canGoForward);
});
}, []);
useKeyDown(
(event) =>
isMac
? event.metaKey && event.key === "]"
: event.altKey && event.key === "ArrowRight",
() => {
setForward(true);
setTimeout(() => setForward(false), 100);
}
);
if (!Desktop.isMacApp()) {
if (!Desktop.isMacApp() || !supported) {
return null;
}
return (
<Navigation gap={4} {...props}>
<Tooltip content={t("Go back")}>
<NudeButton onClick={() => Desktop.bridge?.goBack()}>
<Back $active={back} />
<NudeButton
aria-label={t("Go back")}
disabled={!canGoBack}
onClick={() => Desktop.bridge?.goBack()}
>
<Back $enabled={canGoBack} />
</NudeButton>
</Tooltip>
<Tooltip content={t("Go forward")}>
<NudeButton onClick={() => Desktop.bridge?.goForward()}>
<Forward $active={forward} />
<NudeButton
aria-label={t("Go forward")}
disabled={!canGoForward}
onClick={() => Desktop.bridge?.goForward()}
>
<Forward $enabled={canGoForward} />
</NudeButton>
</Tooltip>
</Navigation>
@@ -61,16 +57,20 @@ const Navigation = styled(Flex)`
position: absolute;
right: 12px;
top: 14px;
button {
cursor: default;
}
`;
const Forward = styled(ArrowIcon)<{ $active: boolean }>`
const Forward = styled(ArrowIcon)<{ $enabled: boolean }>`
color: ${s("textTertiary")};
opacity: ${(props) => (props.$active ? 1 : 0.5)};
opacity: ${(props) => (props.$enabled ? 0.5 : 0.15)};
transition: color 100ms ease-in-out;
&:active,
&:hover {
opacity: 1;
opacity: ${(props) => (props.$enabled ? 1 : 0.15)};
}
`;
+8
View File
@@ -101,6 +101,14 @@ declare global {
*/
goForward: () => void;
/**
* Registers a callback to be called when the navigation state changes (e.g. after
* navigating to a new page). Receives whether back/forward navigation is possible.
*/
onNavigationStateChanged: (
callback: (state: { canGoBack: boolean; canGoForward: boolean }) => void
) => () => void;
/**
* Registers a callback to be called when the application wants to open the find in page dialog.
*/