Reduce size of Insights display toggle

This commit is contained in:
Tom Moor
2024-03-08 23:03:06 -05:00
parent 1a454d6dbb
commit f3334cedb2
4 changed files with 173 additions and 120 deletions

View File

@ -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: <EyeIcon />,
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,

View File

@ -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<HTMLDivElement>(null);
const menu = useMenuState();
const context = useActionContext();
const items: MenuItem[] = [actionToMenuItem(toggleViewerInsights, context)];
return (
<>
<MenuButton {...menu}>
{(props) => (
<Button {...props}>
<MoreIcon />
</Button>
)}
</MenuButton>
<ContextMenu {...menu} menuRef={menuRef} aria-label={t("Notification")}>
<Template {...menu} items={items} />
</ContextMenu>
</>
);
};
const Button = styled(NudeButton)`
color: ${s("textSecondary")};
&:${hover},
&:active {
color: ${s("text")};
background: ${s("sidebarControlHoverBackground")};
}
`;
export default InsightsMenu;

View File

@ -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() {
</List>
</Text>
</Content>
{document.insightsEnabled && (
<>
<Content column>
<Heading>{t("Contributors")}</Heading>
<Text as="p" type="secondary" size="small">
{t(`Created`)}{" "}
<Time dateTime={document.createdAt} addSuffix />.
<br />
{t(`Last updated`)}{" "}
<Time dateTime={document.updatedAt} addSuffix />.
</Text>
<ListSpacing>
{document.sourceMetadata?.createdByName && (
<ListItem
title={document.sourceMetadata?.createdByName}
image={
<Avatar
model={{
color: stringToColor(
document.sourceMetadata.createdByName
),
avatarUrl: null,
initial: document.sourceMetadata.createdByName[0],
}}
size={32}
/>
}
subtitle={t("Creator")}
border={false}
small
<Content column>
<Heading>{t("Contributors")}</Heading>
<Text as="p" type="secondary" size="small">
{t(`Created`)} <Time dateTime={document.createdAt} addSuffix />.
<br />
{t(`Last updated`)}{" "}
<Time dateTime={document.updatedAt} addSuffix />.
</Text>
<ListSpacing>
{document.sourceMetadata?.createdByName && (
<ListItem
title={document.sourceMetadata?.createdByName}
image={
<Avatar
model={{
color: stringToColor(
document.sourceMetadata.createdByName
),
avatarUrl: null,
initial: document.sourceMetadata.createdByName[0],
}}
size={32}
/>
)}
<PaginatedList
aria-label={t("Contributors")}
items={document.collaborators}
renderItem={(model: User) => (
<ListItem
key={model.id}
title={model.name}
image={<Avatar model={model} size={32} />}
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
/>
)}
<PaginatedList
aria-label={t("Contributors")}
items={document.collaborators}
renderItem={(model: User) => (
<ListItem
key={model.id}
title={model.name}
image={<Avatar model={model} size={32} />}
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
/>
</ListSpacing>
</Content>
<Content column>
<Heading>{t("Views")}</Heading>
<Text as="p" type="secondary" size="small">
{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,
}
)}
.
</Text>
{documentViews.length > 1 && (
<ListSpacing>
<DocumentViews document={document} isOpen />
</ListSpacing>
)}
</Content>
</>
/>
</ListSpacing>
</Content>
{(document.insightsEnabled || can.updateInsights) && (
<Content column>
<Heading>
<Flex justify="space-between">
{t("Viewed by")}
{can.updateInsights && <InsightsMenu />}
</Flex>
</Heading>
{document.insightsEnabled ? (
<>
<Text as="p" type="secondary" size="small">
{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,
}
)}
.
</Text>
{documentViews.length > 1 && (
<ListSpacing>
<DocumentViews document={document} isOpen />
</ListSpacing>
)}
</>
) : (
<Text as="p" type="secondary" size="small">
{t("Viewer insights are disabled.")}
</Text>
)}
</Content>
)}
</div>
{can.updateInsights && (
<Manage>
<Flex column>
<Text as="p" size="small" weight="bold">
{t("Viewer insights")}
</Text>
<Text as="p" type="secondary" size="small">
{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"
)}
</Text>
</Flex>
<Switch
checked={document.insightsEnabled}
onChange={async (ev) => {
await document.save({
insightsEnabled: ev.currentTarget.checked,
});
}}
/>
</Manage>
)}
</Flex>
) : null}
</Sidebar>
@ -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;

View File

@ -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",