import emojiRegex from "emoji-regex"; import { observer } from "mobx-react"; import * as React from "react"; import { useTranslation } from "react-i18next"; import { useHistory, useRouteMatch } from "react-router-dom"; import styled from "styled-components"; import { s } from "@shared/styles"; import User from "~/models/User"; import Avatar from "~/components/Avatar"; import { useDocumentContext } from "~/components/DocumentContext"; import DocumentViews from "~/components/DocumentViews"; import Flex from "~/components/Flex"; import ListItem from "~/components/List/Item"; import PaginatedList from "~/components/PaginatedList"; import Text from "~/components/Text"; import Time from "~/components/Time"; import useKeyDown from "~/hooks/useKeyDown"; import useStores from "~/hooks/useStores"; import useTextSelection from "~/hooks/useTextSelection"; import { documentPath } from "~/utils/routeHelpers"; import Sidebar from "./SidebarLayout"; function Insights() { const { views, documents } = useStores(); const { t } = useTranslation(); const match = useRouteMatch<{ documentSlug: string }>(); const history = useHistory(); const selectedText = useTextSelection(); const document = documents.getByUrl(match.params.documentSlug); const { editor } = useDocumentContext(); const text = editor?.getPlainText(); const stats = useTextStats(text ?? "", selectedText); const documentViews = document ? views.inDocument(document.id) : []; const onCloseInsights = () => { if (document) { history.push(documentPath(document)); } }; useKeyDown("Escape", onCloseInsights); return ( {document ? ( <> {t("Stats")} {stats.total.words > 0 && (
  • {t(`{{ count }} minute read`, { count: stats.total.readingTime, })}
  • )}
  • {t(`{{ count }} words`, { count: stats.total.words })}
  • {t(`{{ count }} characters`, { count: stats.total.characters, })}
  • {t(`{{ number }} emoji`, { number: stats.total.emoji })}
  • {stats.selected.characters === 0 ? (
  • {t("No text selected")}
  • ) : ( <>
  • {t(`{{ count }} words selected`, { count: stats.selected.words, })}
  • {t(`{{ count }} characters selected`, { count: stats.selected.characters, })}
  • )}
    {t("Contributors")} {t(`Created`)} ( } subtitle={ model.id === document.createdBy.id ? 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 && ( )} ) : null}
    ); } function useTextStats(text: string, selectedText: string) { const numTotalWords = countWords(text); const regex = emojiRegex(); const matches = Array.from(text.matchAll(regex)); return { total: { words: numTotalWords, characters: text.length, emoji: matches.length ?? 0, readingTime: Math.max(1, Math.floor(numTotalWords / 200)), }, selected: { words: countWords(selectedText), characters: selectedText.length, }, }; } function countWords(text: string): number { const t = text.trim(); // Hyphenated words are counted as two words return t ? t.replace(/-/g, " ").split(/\s+/g).length : 0; } const ListSpacing = styled("div")` margin-top: -0.5em; margin-bottom: 0.5em; `; const List = styled("ul")` margin: 0; padding: 0; list-style: none; li:before { content: "ยท"; display: inline-block; font-weight: 600; color: ${s("textTertiary")}; width: 10px; } `; const Content = styled(Flex)` padding: 0 16px; user-select: none; `; const Heading = styled("h3")` font-size: 15px; `; export default observer(Insights);