diff --git a/app/components/Notifications/NotificationIcon.tsx b/app/components/Notifications/NotificationIcon.tsx
new file mode 100644
index 000000000..22ae3ce3e
--- /dev/null
+++ b/app/components/Notifications/NotificationIcon.tsx
@@ -0,0 +1,32 @@
+import { observer } from "mobx-react";
+import { SubscribeIcon } from "outline-icons";
+import * as React from "react";
+import styled, { useTheme } from "styled-components";
+import { s } from "@shared/styles";
+import useStores from "~/hooks/useStores";
+import Relative from "../Sidebar/components/Relative";
+
+const NotificationIcon = () => {
+ const { notifications } = useStores();
+ const theme = useTheme();
+ const count = notifications.approximateUnreadCount;
+
+ return (
+
+
+ {count > 0 && }
+
+ );
+};
+
+const Badge = styled.div`
+ position: absolute;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: ${s("accent")};
+ top: 0;
+ right: 0;
+`;
+
+export default observer(NotificationIcon);
diff --git a/app/components/Notifications/Notifications.tsx b/app/components/Notifications/Notifications.tsx
index 3ba408f03..e234b0376 100644
--- a/app/components/Notifications/Notifications.tsx
+++ b/app/components/Notifications/Notifications.tsx
@@ -10,6 +10,7 @@ import { markNotificationsAsRead } from "~/actions/definitions/notifications";
import useActionContext from "~/hooks/useActionContext";
import useStores from "~/hooks/useStores";
import { hover } from "~/styles";
+import Desktop from "~/utils/Desktop";
import Empty from "../Empty";
import Flex from "../Flex";
import NudeButton from "../NudeButton";
@@ -36,6 +37,15 @@ function Notifications(
const { t } = useTranslation();
const isEmpty = notifications.orderedData.length === 0;
+ // Update the notification count in the dock icon, if possible.
+ React.useEffect(() => {
+ // Account for old versions of the desktop app that don't have the
+ // setNotificationCount method on the bridge.
+ if (Desktop.bridge && "setNotificationCount" in Desktop.bridge) {
+ Desktop.bridge.setNotificationCount(notifications.approximateUnreadCount);
+ }
+ }, [notifications.approximateUnreadCount]);
+
return (
diff --git a/app/components/Notifications/NotificationsButton.tsx b/app/components/Notifications/NotificationsPopover.tsx
similarity index 92%
rename from app/components/Notifications/NotificationsButton.tsx
rename to app/components/Notifications/NotificationsPopover.tsx
index 0c3df2cb8..b1efd1078 100644
--- a/app/components/Notifications/NotificationsButton.tsx
+++ b/app/components/Notifications/NotificationsPopover.tsx
@@ -7,7 +7,7 @@ import { depths } from "@shared/styles";
import Popover from "~/components/Popover";
import Notifications from "./Notifications";
-const NotificationsButton: React.FC = ({ children }) => {
+const NotificationsPopover: React.FC = ({ children }) => {
const { t } = useTranslation();
const scrollableRef = React.useRef(null);
@@ -46,4 +46,4 @@ const StyledPopover = styled(Popover)`
z-index: ${depths.menu};
`;
-export default observer(NotificationsButton);
+export default observer(NotificationsPopover);
diff --git a/app/components/Sidebar/Sidebar.tsx b/app/components/Sidebar/Sidebar.tsx
index 30ee71d58..aa72341b7 100644
--- a/app/components/Sidebar/Sidebar.tsx
+++ b/app/components/Sidebar/Sidebar.tsx
@@ -1,5 +1,4 @@
import { observer } from "mobx-react";
-import { SubscribeIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Portal } from "react-portal";
@@ -16,9 +15,9 @@ import { draggableOnDesktop, fadeOnDesktopBackgrounded } from "~/styles";
import { fadeIn } from "~/styles/animations";
import Desktop from "~/utils/Desktop";
import Avatar from "../Avatar";
-import NotificationsButton from "../Notifications/NotificationsButton";
+import NotificationIcon from "../Notifications/NotificationIcon";
+import NotificationsPopover from "../Notifications/NotificationsPopover";
import HeaderButton, { HeaderButtonProps } from "./components/HeaderButton";
-import Relative from "./components/Relative";
import ResizeBorder from "./components/ResizeBorder";
import Toggle, { ToggleButton, Positioner } from "./components/Toggle";
@@ -188,14 +187,11 @@ const Sidebar = React.forwardRef(
/>
}
>
-
+
{(rest: HeaderButtonProps) => (
- }
- />
+ } />
)}
-
+
)}
@@ -223,29 +219,6 @@ const Sidebar = React.forwardRef(
}
);
-const BadgedNotificationIcon = observer(() => {
- const { notifications } = useStores();
- const theme = useTheme();
- const count = notifications.approximateUnreadCount;
-
- return (
-
-
- {count > 0 && }
-
- );
-});
-
-const Badge = styled.div`
- position: absolute;
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: ${s("accent")};
- top: 0;
- right: 0;
-`;
-
const StyledAvatar = styled(Avatar)`
margin-left: 4px;
`;
diff --git a/app/typings/window.d.ts b/app/typings/window.d.ts
index 4fc66d294..f2b68bf46 100644
--- a/app/typings/window.d.ts
+++ b/app/typings/window.d.ts
@@ -56,6 +56,11 @@ declare global {
*/
setSpellCheckerLanguages: (languages: string[]) => Promise;
+ /**
+ * Set the badge on the app icon.
+ */
+ setNotificationCount: (count: number) => Promise;
+
/**
* Registers a callback to be called when the window is focused.
*/