From 26b9566b96da74ca8ba633ef01168c7d97c7afcf Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sun, 20 Sep 2020 23:37:09 -0700 Subject: [PATCH] fix: Various fixes for unread tracking --- app/components/Badge.js | 3 +- app/components/DocumentMeta.js | 33 ++++++++++++------- .../DocumentPreview/DocumentPreview.js | 3 +- app/components/Time.js | 16 ++++++++- app/models/Document.js | 2 +- app/stores/DocumentsStore.js | 19 ++--------- server/api/documents.js | 6 +++- 7 files changed, 49 insertions(+), 33 deletions(-) diff --git a/app/components/Badge.js b/app/components/Badge.js index c4d3f3604..c138111ff 100644 --- a/app/components/Badge.js +++ b/app/components/Badge.js @@ -6,7 +6,8 @@ const Badge = styled.span` padding: 2px 6px 3px; background-color: ${({ yellow, primary, theme }) => yellow ? theme.yellow : primary ? theme.primary : theme.textTertiary}; - color: ${({ primary, theme }) => (primary ? theme.white : theme.background)}; + color: ${({ primary, yellow, theme }) => + primary ? theme.white : yellow ? theme.text : theme.background}; border-radius: 4px; font-size: 11px; font-weight: 500; diff --git a/app/components/DocumentMeta.js b/app/components/DocumentMeta.js index 08eede428..31a96d403 100644 --- a/app/components/DocumentMeta.js +++ b/app/components/DocumentMeta.js @@ -18,8 +18,7 @@ const Container = styled(Flex)` `; const Modified = styled.span` - color: ${(props) => - props.highlight ? props.theme.text : props.theme.textTertiary}; + color: ${(props) => props.theme.textTertiary}; font-weight: ${(props) => (props.highlight ? "600" : "400")}; `; @@ -28,6 +27,7 @@ type Props = { auth: AuthStore, showCollection?: boolean, showPublished?: boolean, + showLastViewed?: boolean, document: Document, children: React.Node, to?: string, @@ -38,6 +38,7 @@ function DocumentMeta({ collections, showPublished, showCollection, + showLastViewed, document, children, to, @@ -66,37 +67,37 @@ function DocumentMeta({ if (deletedAt) { content = ( - deleted ); } else if (archivedAt) { content = ( - archived ); } else if (createdAt === updatedAt) { content = ( - created ); } else if (publishedAt && (publishedAt === updatedAt || showPublished)) { content = ( - published ); } else if (isDraft) { content = ( - saved ); } else { content = ( - updated ); } @@ -105,12 +106,20 @@ function DocumentMeta({ const updatedByMe = auth.user && auth.user.id === updatedBy.id; const timeSinceNow = () => { - if (!lastViewedAt) - return Never viewed; + if (isDraft || !showLastViewed) { + return null; + } + if (!lastViewedAt) { + return ( + <> + • Never viewed + + ); + } return ( - Viewed ); }; @@ -127,7 +136,7 @@ function DocumentMeta({ )} -  • {timeSinceNow()} +  {timeSinceNow()} {children} ); diff --git a/app/components/DocumentPreview/DocumentPreview.js b/app/components/DocumentPreview/DocumentPreview.js index 2cd050956..d8f59be99 100644 --- a/app/components/DocumentPreview/DocumentPreview.js +++ b/app/components/DocumentPreview/DocumentPreview.js @@ -86,6 +86,7 @@ class DocumentPreview extends React.Component { > + {document.isNew && <Badge yellow>New</Badge>} {!document.isDraft && !document.isArchived && !document.isTemplate && ( @@ -105,7 +106,6 @@ class DocumentPreview extends React.Component<Props> { {document.isTemplate && showTemplate && ( <Badge primary>Template</Badge> )} - {document.isNew && <Badge yellow>New</Badge>} <SecondaryActions> {document.isTemplate && !document.isArchived && @@ -134,6 +134,7 @@ class DocumentPreview extends React.Component<Props> { document={document} showCollection={showCollection} showPublished={showPublished} + showLastViewed /> </DocumentLink> ); diff --git a/app/components/Time.js b/app/components/Time.js index d7b55f329..bee88cf66 100644 --- a/app/components/Time.js +++ b/app/components/Time.js @@ -24,6 +24,8 @@ type Props = { dateTime: string, children?: React.Node, tooltipDelay?: number, + addSuffix?: boolean, + shorten?: boolean, }; class Time extends React.Component<Props> { @@ -40,6 +42,18 @@ class Time extends React.Component<Props> { } render() { + const { shorten, addSuffix } = this.props; + let content = distanceInWordsToNow(this.props.dateTime, { + addSuffix, + }); + + if (shorten) { + content = content + .replace("about", "") + .replace("minute", "min") + .replace("less than a minute ago", "just now"); + } + return ( <Tooltip tooltip={format(this.props.dateTime, "MMMM Do, YYYY h:mm a")} @@ -47,7 +61,7 @@ class Time extends React.Component<Props> { placement="bottom" > <time dateTime={this.props.dateTime}> - {this.props.children || distanceInWordsToNow(this.props.dateTime)} + {this.props.children || content} </time> </Tooltip> ); diff --git a/app/models/Document.js b/app/models/Document.js index 91818e040..1ec8134f9 100644 --- a/app/models/Document.js +++ b/app/models/Document.js @@ -21,11 +21,11 @@ export default class Document extends BaseModel { @observable isSaving: boolean = false; @observable embedsDisabled: boolean = false; @observable injectTemplate: boolean = false; + @observable lastViewedAt: ?string; store: DocumentsStore; collaborators: User[]; collectionId: string; - @observable lastViewedAt: ?string; createdAt: string; createdBy: User; updatedAt: string; diff --git a/app/stores/DocumentsStore.js b/app/stores/DocumentsStore.js index 1e9e1046e..c14b9e180 100644 --- a/app/stores/DocumentsStore.js +++ b/app/stores/DocumentsStore.js @@ -23,7 +23,6 @@ type ImportOptions = { }; export default class DocumentsStore extends BaseStore<Document> { - @observable recentlyViewedIds: string[] = []; @observable searchCache: Map<string, SearchResult[]> = new Map(); @observable starredIds: Map<string, boolean> = new Map(); @observable backlinks: Map<string, string[]> = new Map(); @@ -50,8 +49,8 @@ export default class DocumentsStore extends BaseStore<Document> { @computed get recentlyViewed(): Document[] { return orderBy( - compact(this.recentlyViewedIds.map((id) => this.data.get(id))), - "updatedAt", + filter(this.all, (d) => d.lastViewedAt), + "lastViewedAt", "desc" ); } @@ -299,15 +298,7 @@ export default class DocumentsStore extends BaseStore<Document> { @action fetchRecentlyViewed = async (options: ?PaginationParams): Promise<*> => { - const data = await this.fetchNamedPage("viewed", options); - - runInAction("DocumentsStore#fetchRecentlyViewed", () => { - // $FlowFixMe - this.recentlyViewedIds.replace( - uniq(this.recentlyViewedIds.concat(map(data, "id"))) - ); - }); - return data; + return this.fetchNamedPage("viewed", options); }; @action @@ -541,10 +532,6 @@ export default class DocumentsStore extends BaseStore<Document> { async delete(document: Document) { await super.delete(document); - runInAction(() => { - this.recentlyViewedIds = without(this.recentlyViewedIds, document.id); - }); - // check to see if we have any shares related to this document already // loaded in local state. If so we can go ahead and remove those too. const share = this.rootStore.shares.getByDocumentId(document.id); diff --git a/server/api/documents.js b/server/api/documents.js index 6e760b2b4..f8a375842 100644 --- a/server/api/documents.js +++ b/server/api/documents.js @@ -283,7 +283,11 @@ router.post("documents.viewed", auth(), pagination(), async (ctx) => { limit: ctx.state.pagination.limit, }); - const documents = views.map((view) => view.document); + const documents = views.map((view) => { + const document = view.document; + document.views = [view]; + return document; + }); const data = await Promise.all( documents.map((document) => presentDocument(document)) );