diff --git a/app/actions/definitions/documents.tsx b/app/actions/definitions/documents.tsx
index c52e625d1..b045b57c5 100644
--- a/app/actions/definitions/documents.tsx
+++ b/app/actions/definitions/documents.tsx
@@ -26,6 +26,7 @@ import {
CommentIcon,
GlobeIcon,
CopyIcon,
+ EyeIcon,
} from "outline-icons";
import * as React from "react";
import { toast } from "sonner";
@@ -899,6 +900,37 @@ export const openDocumentInsights = createAction({
},
});
+export const toggleViewerInsights = createAction({
+ name: ({ t, stores, activeDocumentId }) => {
+ const document = activeDocumentId
+ ? stores.documents.get(activeDocumentId)
+ : undefined;
+ return document?.insightsEnabled
+ ? t("Disable viewer insights")
+ : t("Enable viewer insights");
+ },
+ analyticsName: "Toggle viewer insights",
+ section: DocumentSection,
+ icon: ,
+ visible: ({ activeDocumentId, stores }) => {
+ const can = stores.policies.abilities(activeDocumentId ?? "");
+ return can.updateInsights;
+ },
+ perform: async ({ activeDocumentId, stores }) => {
+ if (!activeDocumentId) {
+ return;
+ }
+ const document = stores.documents.get(activeDocumentId);
+ if (!document) {
+ return;
+ }
+
+ await document.save({
+ insightsEnabled: !document.insightsEnabled,
+ });
+ },
+});
+
export const rootDocumentActions = [
openDocument,
archiveDocument,
diff --git a/app/menus/InsightsMenu.tsx b/app/menus/InsightsMenu.tsx
new file mode 100644
index 000000000..b507e39a0
--- /dev/null
+++ b/app/menus/InsightsMenu.tsx
@@ -0,0 +1,48 @@
+import { t } from "i18next";
+import { MoreIcon } from "outline-icons";
+import React from "react";
+import { MenuButton, useMenuState } from "reakit/Menu";
+import styled from "styled-components";
+import { s } from "@shared/styles";
+import ContextMenu from "~/components/ContextMenu";
+import Template from "~/components/ContextMenu/Template";
+import NudeButton from "~/components/NudeButton";
+import { actionToMenuItem } from "~/actions";
+import { toggleViewerInsights } from "~/actions/definitions/documents";
+import useActionContext from "~/hooks/useActionContext";
+import { hover } from "~/styles";
+import { MenuItem } from "~/types";
+
+const InsightsMenu: React.FC = () => {
+ const menuRef = React.useRef(null);
+ const menu = useMenuState();
+ const context = useActionContext();
+ const items: MenuItem[] = [actionToMenuItem(toggleViewerInsights, context)];
+
+ return (
+ <>
+
+ {(props) => (
+
+ )}
+
+
+
+
+ >
+ );
+};
+
+const Button = styled(NudeButton)`
+ color: ${s("textSecondary")};
+
+ &:${hover},
+ &:active {
+ color: ${s("text")};
+ background: ${s("sidebarControlHoverBackground")};
+ }
+`;
+
+export default InsightsMenu;
diff --git a/app/scenes/Document/components/Insights.tsx b/app/scenes/Document/components/Insights.tsx
index 50bba4adc..156178030 100644
--- a/app/scenes/Document/components/Insights.tsx
+++ b/app/scenes/Document/components/Insights.tsx
@@ -13,21 +13,19 @@ import DocumentViews from "~/components/DocumentViews";
import Flex from "~/components/Flex";
import ListItem from "~/components/List/Item";
import PaginatedList from "~/components/PaginatedList";
-import Switch from "~/components/Switch";
import Text from "~/components/Text";
import Time from "~/components/Time";
-import useCurrentUser from "~/hooks/useCurrentUser";
import useKeyDown from "~/hooks/useKeyDown";
import usePolicy from "~/hooks/usePolicy";
import useStores from "~/hooks/useStores";
import useTextSelection from "~/hooks/useTextSelection";
+import InsightsMenu from "~/menus/InsightsMenu";
import { documentPath } from "~/utils/routeHelpers";
import Sidebar from "./SidebarLayout";
function Insights() {
const { views, documents } = useStores();
const { t } = useTranslation();
- const user = useCurrentUser();
const match = useRouteMatch<{ documentSlug: string }>();
const history = useHistory();
const selectedText = useTextSelection();
@@ -111,114 +109,99 @@ function Insights() {
- {document.insightsEnabled && (
- <>
-
- {t("Contributors")}
-
- {t(`Created`)}{" "}
- .
-
- {t(`Last updated`)}{" "}
- .
-
-
- {document.sourceMetadata?.createdByName && (
-
- }
- subtitle={t("Creator")}
- border={false}
- small
+
+
+ {t("Contributors")}
+
+ {t(`Created`)} .
+
+ {t(`Last updated`)}{" "}
+ .
+
+
+ {document.sourceMetadata?.createdByName && (
+
- )}
- (
- }
- subtitle={
- model.id === document.createdBy?.id
- ? document.sourceMetadata?.createdByName
- ? t("Imported")
- : t("Creator")
- : model.id === document.updatedBy?.id
- ? t("Last edited")
- : t("Previously edited")
- }
- border={false}
- small
- />
- )}
+ }
+ subtitle={t("Creator")}
+ border={false}
+ small
+ />
+ )}
+ (
+ }
+ subtitle={
+ model.id === document.createdBy?.id
+ ? document.sourceMetadata?.createdByName
+ ? t("Imported")
+ : t("Creator")
+ : model.id === document.updatedBy?.id
+ ? t("Last edited")
+ : t("Previously edited")
+ }
+ border={false}
+ small
/>
-
-
-
- {t("Views")}
-
- {documentViews.length <= 1
- ? t("No one else has viewed yet")
- : t(
- `Viewed {{ count }} times by {{ teamMembers }} people`,
- {
- count: documentViews.reduce(
- (memo, view) => memo + view.count,
- 0
- ),
- teamMembers: documentViews.length,
- }
- )}
- .
-
- {documentViews.length > 1 && (
-
-
-
)}
-
- >
+ />
+
+
+ {(document.insightsEnabled || can.updateInsights) && (
+
+
+
+ {t("Viewed by")}
+ {can.updateInsights && }
+
+
+ {document.insightsEnabled ? (
+ <>
+
+ {documentViews.length <= 1
+ ? t("No one else has viewed yet")
+ : t(
+ `Viewed {{ count }} times by {{ teamMembers }} people`,
+ {
+ count: documentViews.reduce(
+ (memo, view) => memo + view.count,
+ 0
+ ),
+ teamMembers: documentViews.length,
+ }
+ )}
+ .
+
+ {documentViews.length > 1 && (
+
+
+
+ )}
+ >
+ ) : (
+
+ {t("Viewer insights are disabled.")}
+
+ )}
+
)}
- {can.updateInsights && (
-
-
-
- {t("Viewer insights")}
-
-
- {user.isAdmin
- ? t(
- "As an admin you can manage if team members can see who has viewed this document"
- )
- : t(
- "As the doc owner you can manage if team members can see who has viewed this document"
- )}
-
-
- {
- await document.save({
- insightsEnabled: ev.currentTarget.checked,
- });
- }}
- />
-
- )}
) : null}
@@ -251,16 +234,6 @@ function countWords(text: string): number {
return t ? t.replace(/-/g, " ").split(/\s+/g).length : 0;
}
-const Manage = styled(Flex)`
- background: ${s("background")};
- border: 1px solid ${s("inputBorder")};
- border-bottom-width: 2px;
- border-radius: 8px;
- margin: 16px;
- padding: 16px 16px 0;
- justify-self: flex-end;
-`;
-
const ListSpacing = styled("div")`
margin-top: -0.5em;
margin-bottom: 0.5em;
diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json
index 565767a4f..425f88242 100644
--- a/shared/i18n/locales/en_US/translation.json
+++ b/shared/i18n/locales/en_US/translation.json
@@ -69,6 +69,8 @@
"Comments": "Comments",
"History": "History",
"Insights": "Insights",
+ "Disable viewer insights": "Disable viewer insights",
+ "Enable viewer insights": "Enable viewer insights",
"Home": "Home",
"Drafts": "Drafts",
"Trash": "Trash",
@@ -583,13 +585,10 @@
"Creator": "Creator",
"Last edited": "Last edited",
"Previously edited": "Previously edited",
- "Views": "Views",
"No one else has viewed yet": "No one else has viewed yet",
"Viewed {{ count }} times by {{ teamMembers }} people": "Viewed {{ count }} time by {{ teamMembers }} people",
"Viewed {{ count }} times by {{ teamMembers }} people_plural": "Viewed {{ count }} times by {{ teamMembers }} people",
- "Viewer insights": "Viewer insights",
- "As an admin you can manage if team members can see who has viewed this document": "As an admin you can manage if team members can see who has viewed this document",
- "As the doc owner you can manage if team members can see who has viewed this document": "As the doc owner you can manage if team members can see who has viewed this document",
+ "Viewer insights are disabled.": "Viewer insights are disabled.",
"Sorry, the last change could not be persisted – please reload the page": "Sorry, the last change could not be persisted – please reload the page",
"{{ count }} days": "{{ count }} day",
"{{ count }} days_plural": "{{ count }} days",
@@ -800,6 +799,7 @@
"Shared nested": "Shared nested",
"Nested documents are publicly available": "Nested documents are publicly available",
"Domain": "Domain",
+ "Views": "Views",
"Everyone": "Everyone",
"Admins": "Admins",
"Settings saved": "Settings saved",