From d70f15c708cebda371023a390a7a02cf7aabb9dc Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 16:46:14 -0400 Subject: [PATCH 01/16] Define SessionService exports --- web/app/components/document/sidebar.ts | 2 +- web/app/components/header/nav.ts | 2 +- web/app/controllers/authenticate.ts | 2 +- web/app/routes/application.ts | 3 +-- web/app/routes/authenticate.ts | 2 +- web/app/routes/authenticated.ts | 2 +- web/app/routes/authenticated/dashboard.ts | 2 +- web/app/services/algolia.ts | 2 +- web/app/services/authenticated-user.ts | 2 +- web/app/services/fetch.ts | 2 +- web/app/services/session.d.ts | 3 +++ web/tests/acceptance/application-test.ts | 2 +- web/tests/unit/services/session-test.ts | 2 +- 13 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 web/app/services/session.d.ts diff --git a/web/app/components/document/sidebar.ts b/web/app/components/document/sidebar.ts index 5da3db2fc..05a6d1fa3 100644 --- a/web/app/components/document/sidebar.ts +++ b/web/app/components/document/sidebar.ts @@ -14,7 +14,7 @@ import cleanString from "hermes/utils/clean-string"; import { debounce } from "@ember/runloop"; import FetchService from "hermes/services/fetch"; import RouterService from "@ember/routing/router-service"; -import SessionService from "hermes/services/_session"; +import SessionService from "hermes/services/session"; import FlashMessageService from "ember-cli-flash/services/flash-messages"; import { AuthenticatedUser } from "hermes/services/authenticated-user"; import { HermesDocument, HermesUser } from "hermes/types/document"; diff --git a/web/app/components/header/nav.ts b/web/app/components/header/nav.ts index f2b477151..a40f1e6e5 100644 --- a/web/app/components/header/nav.ts +++ b/web/app/components/header/nav.ts @@ -2,7 +2,7 @@ import Component from "@glimmer/component"; import { inject as service } from "@ember/service"; import { action } from "@ember/object"; import ConfigService from "hermes/services/config"; -import SessionService from "hermes/services/_session"; +import SessionService from "hermes/services/session"; import RouterService from "@ember/routing/router-service"; import AuthenticatedUserService, { AuthenticatedUser, diff --git a/web/app/controllers/authenticate.ts b/web/app/controllers/authenticate.ts index ad97aae77..13dcd2b47 100644 --- a/web/app/controllers/authenticate.ts +++ b/web/app/controllers/authenticate.ts @@ -1,6 +1,6 @@ import Controller from "@ember/controller"; import { inject as service } from "@ember/service"; -import SessionService from "hermes/services/_session"; +import SessionService from "hermes/services/session"; import { dropTask } from "ember-concurrency"; export default class AuthenticateController extends Controller { diff --git a/web/app/routes/application.ts b/web/app/routes/application.ts index 357940912..f68e91a23 100644 --- a/web/app/routes/application.ts +++ b/web/app/routes/application.ts @@ -4,11 +4,10 @@ import { action } from "@ember/object"; import { inject as service } from "@ember/service"; import ConfigService from "hermes/services/config"; import FetchService from "hermes/services/fetch"; -import SessionService from "hermes/services/_session"; +import SessionService, { REDIRECT_STORAGE_KEY } from "hermes/services/session"; import RouterService from "@ember/routing/router-service"; import window from "ember-window-mock"; -import { REDIRECT_STORAGE_KEY } from "hermes/services/_session"; import Transition from "@ember/routing/transition"; import MetricsService from "hermes/services/_metrics"; diff --git a/web/app/routes/authenticate.ts b/web/app/routes/authenticate.ts index 2a4e3b6c6..ffa9dce6f 100644 --- a/web/app/routes/authenticate.ts +++ b/web/app/routes/authenticate.ts @@ -2,7 +2,7 @@ import Route from "@ember/routing/route"; import { inject as service } from "@ember/service"; import ConfigService from "hermes/services/config"; import RouterService from "@ember/routing/router-service"; -import SessionService from "hermes/services/_session"; +import SessionService from "hermes/services/session"; export default class AuthenticateRoute extends Route { @service("config") declare configSvc: ConfigService; diff --git a/web/app/routes/authenticated.ts b/web/app/routes/authenticated.ts index 91b78f25b..15b204d39 100644 --- a/web/app/routes/authenticated.ts +++ b/web/app/routes/authenticated.ts @@ -2,7 +2,7 @@ import Route from "@ember/routing/route"; import { inject as service } from "@ember/service"; import AuthenticatedUserService from "hermes/services/authenticated-user"; import ConfigService from "hermes/services/config"; -import SessionService from "hermes/services/_session"; +import SessionService from "hermes/services/session"; export default class AuthenticatedRoute extends Route { @service("config") declare configSvc: ConfigService; diff --git a/web/app/routes/authenticated/dashboard.ts b/web/app/routes/authenticated/dashboard.ts index c03bd026c..6ceb5420b 100644 --- a/web/app/routes/authenticated/dashboard.ts +++ b/web/app/routes/authenticated/dashboard.ts @@ -4,7 +4,7 @@ import AlgoliaService from "hermes/services/algolia"; import ConfigService from "hermes/services/config"; import FetchService from "hermes/services/fetch"; import RecentlyViewedDocsService from "hermes/services/recently-viewed-docs"; -import SessionService from "hermes/services/_session"; +import SessionService from "hermes/services/session"; import AuthenticatedUserService from "hermes/services/authenticated-user"; // @ts-ignore - Not yet typed diff --git a/web/app/services/algolia.ts b/web/app/services/algolia.ts index 84be396a3..8dbe700a2 100644 --- a/web/app/services/algolia.ts +++ b/web/app/services/algolia.ts @@ -14,7 +14,7 @@ import { FacetRecord, FacetRecords, } from "hermes/types/facets"; -import SessionService from "./_session"; +import SessionService from "./session"; export const HITS_PER_PAGE = 12; export const MAX_VALUES_PER_FACET = 100; diff --git a/web/app/services/authenticated-user.ts b/web/app/services/authenticated-user.ts index 643a73163..2ea2cdbec 100644 --- a/web/app/services/authenticated-user.ts +++ b/web/app/services/authenticated-user.ts @@ -5,7 +5,7 @@ import Store from "@ember-data/store"; import { assert } from "@ember/debug"; import { task } from "ember-concurrency"; import FetchService from "hermes/services/fetch"; -import SessionService from "./_session"; +import SessionService from "./session"; export interface AuthenticatedUser { name: string; diff --git a/web/app/services/fetch.ts b/web/app/services/fetch.ts index 02e6dc28b..16e203f6e 100644 --- a/web/app/services/fetch.ts +++ b/web/app/services/fetch.ts @@ -2,7 +2,7 @@ import Service from "@ember/service"; import fetch from "fetch"; import { inject as service } from "@ember/service"; import ConfigService from "hermes/services/config"; -import SessionService from "./_session"; +import SessionService from "./session"; interface FetchOptions { method?: string; diff --git a/web/app/services/session.d.ts b/web/app/services/session.d.ts new file mode 100644 index 000000000..c6ca1c131 --- /dev/null +++ b/web/app/services/session.d.ts @@ -0,0 +1,3 @@ +export { default } from "./_session"; +export { REDIRECT_STORAGE_KEY, isJSON } from "./_session"; +export * from "./_session"; diff --git a/web/tests/acceptance/application-test.ts b/web/tests/acceptance/application-test.ts index f34512f49..1cb8f7937 100644 --- a/web/tests/acceptance/application-test.ts +++ b/web/tests/acceptance/application-test.ts @@ -6,7 +6,7 @@ import { invalidateSession, } from "ember-simple-auth/test-support"; import { MirageTestContext, setupMirage } from "ember-cli-mirage/test-support"; -import SessionService from "hermes/services/_session"; +import SessionService from "hermes/services/session"; import { TEST_SUPPORT_URL } from "hermes/utils/hermes-urls"; module("Acceptance | application", function (hooks) { diff --git a/web/tests/unit/services/session-test.ts b/web/tests/unit/services/session-test.ts index 8effea843..80608fa52 100644 --- a/web/tests/unit/services/session-test.ts +++ b/web/tests/unit/services/session-test.ts @@ -1,6 +1,6 @@ import { module, test } from "qunit"; import { setupTest } from "ember-qunit"; -import { REDIRECT_STORAGE_KEY, isJSON } from "hermes/services/_session"; +import { REDIRECT_STORAGE_KEY, isJSON } from "hermes/services/session"; import window from "ember-window-mock"; const TEST_STORAGE_KEY = `test-${REDIRECT_STORAGE_KEY}`; From e2114413c742a4b1d24ddb362b8ec5b41a961d71 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 17:21:51 -0400 Subject: [PATCH 02/16] WIP convert document route to TS --- web/app/components/doc/row.hbs | 4 +- web/app/components/doc/row.ts | 2 +- web/app/components/document/sidebar.hbs | 1 + web/app/components/row-results.hbs | 5 +- web/app/routes/authenticated/document.ts | 106 ++++++++++++++--------- web/app/types/document.d.ts | 6 +- 6 files changed, 73 insertions(+), 51 deletions(-) diff --git a/web/app/components/doc/row.hbs b/web/app/components/doc/row.hbs index 602d79305..3082b7c4a 100644 --- a/web/app/components/doc/row.hbs +++ b/web/app/components/doc/row.hbs @@ -4,7 +4,7 @@ @route="authenticated.document" @model="{{@docID}}" @query={{hash draft=@isDraft}} - class="flex space-x-4 items-start" + class="flex items-start space-x-4" >
@@ -30,5 +30,5 @@ - {{@createdDate}} + {{@created}} diff --git a/web/app/components/doc/row.ts b/web/app/components/doc/row.ts index 55d748c60..b1a2fa435 100644 --- a/web/app/components/doc/row.ts +++ b/web/app/components/doc/row.ts @@ -3,7 +3,7 @@ import Component from "@glimmer/component"; interface DocRowComponentSignature { Args: { avatar: string; - createdDate: string; + created: string; docID: string; docNumber: string; docType: string; diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index 4826d42f4..24da671c2 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -285,6 +285,7 @@
+ {{! move the "createdDate" logic from the model to a helper }}

{{or @document.createdDate "Unknown"}}

diff --git a/web/app/components/row-results.hbs b/web/app/components/row-results.hbs index fb0ce2eee..75c6712a5 100644 --- a/web/app/components/row-results.hbs +++ b/web/app/components/row-results.hbs @@ -28,10 +28,11 @@ <:body> - {{#each @docs as |doc index|}} + {{#each @docs as |doc|}} + {{log "doc" doc}} - people.map((p) => ({ - email: p.emailAddresses[0].value, +import { schedule } from "@ember/runloop"; +import { GoogleUser } from "hermes/components/inputs/people-select"; +import ConfigService from "hermes/services/config"; +import FetchService from "hermes/services/fetch"; +import RecentlyViewedDocsService from "hermes/services/recently-viewed-docs"; +import AlgoliaService from "hermes/services/algolia"; +import SessionService from "hermes/services/session"; +import FlashMessageService from "ember-cli-flash/services/flash-messages"; +import RouterService from "@ember/routing/router-service"; +import { HermesDocument, HermesUser } from "hermes/types/document"; +import { DocumentsRouteParams } from "hermes/types/document-routes"; +import Transition from "@ember/routing/transition"; +import { HermesDocumentType } from "hermes/types/document-type"; + +const serializePeople = (people: GoogleUser[]): HermesUser[] => { + return people.map((p) => ({ + email: p.emailAddresses[0]?.value as string, imgURL: p.photos?.[0]?.url, })); +}; + +interface DocumentRouteParams { + document_id: string; + draft: boolean; +} export default class DocumentRoute extends Route { - @service algolia; - @service("config") configSvc; - @service("fetch") fetchSvc; - @service("recently-viewed-docs") recentDocs; - @service session; - @service flashMessages; - @service router; + @service("config") declare configSvcL: ConfigService; + @service("fetch") declare fetchSvc: FetchService; + @service("recently-viewed-docs") + declare recentDocs: RecentlyViewedDocsService; + @service declare algolia: AlgoliaService; + @service declare session: SessionService; + @service declare flashMessages: FlashMessageService; + @service declare router: RouterService; // Ideally we'd refresh the model when the draft query param changes, but // because of a suspected bug in Ember, we can't do that. @@ -31,7 +50,7 @@ export default class DocumentRoute extends Route { // }, // }; - showErrorMessage(err) { + showErrorMessage(err: Error) { this.flashMessages.add({ title: "Error fetching document", message: err.message, @@ -41,7 +60,7 @@ export default class DocumentRoute extends Route { }); } - async model(params, transition) { + async model(params: DocumentRouteParams, transition: Transition) { let doc = {}; let draftFetched = false; @@ -57,9 +76,9 @@ export default class DocumentRoute extends Route { "Add-To-Recently-Viewed": "true", }, }) - .then((r) => r.json()); + .then((r) => r?.json()); - doc.isDraft = params.draft; + (doc as HermesDocument).isDraft = params.draft; draftFetched = true; } catch (err) { /** @@ -85,15 +104,16 @@ export default class DocumentRoute extends Route { "Add-To-Recently-Viewed": "true", }, }) - .then((r) => r.json()); + .then((r) => r?.json()); - doc.isDraft = false; + (doc as HermesDocument).isDraft = false; } catch (err) { - this.showErrorMessage(err); + const typedError = err as Error; + this.showErrorMessage(typedError); // Transition to dashboard this.router.transitionTo("authenticated.dashboard"); - throw new Error(errorMessage); + throw new Error(typedError.message); } } @@ -101,12 +121,9 @@ export default class DocumentRoute extends Route { // make a background call to update the front-end index. void this.recentDocs.fetchAll.perform(); - if (!!doc.createdTime) { - doc.createdDate = parseDate(doc.createdTime * 1000, "long"); - } + console.log("Document fetched: ", doc); - // Build strings for created and last-modified. - doc.lastModified = `${timeAgo(new Date(doc.modifiedTime * 1000))}`; + let typedDoc = doc as HermesDocument; // Record analytics. try { @@ -115,7 +132,7 @@ export default class DocumentRoute extends Route { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ document_id: params.document_id, - product_name: doc.product, + product_name: typedDoc.product, }), }); } catch (err) { @@ -125,37 +142,39 @@ export default class DocumentRoute extends Route { // Load the document as well as the logged in user info // Preload avatars for all approvers in the Algolia index. - if (doc.contributors?.length) { + if (typedDoc.contributors?.length) { const contributors = await this.fetchSvc - .fetch(`/api/v1/people?emails=${doc.contributors.join(",")}`) - .then((r) => r.json()); + .fetch(`/api/v1/people?emails=${typedDoc.contributors.join(",")}`) + .then((r) => r?.json()); if (contributors) { - doc.contributors = serializePeople(contributors); + typedDoc.contributors = serializePeople(contributors); } else { - doc.contributors = []; + typedDoc.contributors = []; } } - if (doc.approvers?.length) { + if (typedDoc.approvers?.length) { const approvers = await this.fetchSvc - .fetch(`/api/v1/people?emails=${doc.approvers.join(",")}`) - .then((r) => r.json()); + .fetch(`/api/v1/people?emails=${typedDoc.approvers.join(",")}`) + .then((r) => r?.json()); if (approvers) { - doc.approvers = serializePeople(approvers); + typedDoc.approvers = serializePeople(approvers); } else { - doc.approvers = []; + typedDoc.approvers = []; } } let docTypes = await this.fetchSvc .fetch("/api/v1/document-types") - .then((r) => r.json()); + .then((r) => r?.json()); - let docType = docTypes.find((docType) => docType.name === doc.docType); + let docType = docTypes.find( + (docType: HermesDocumentType) => docType.name === typedDoc.docType + ); return RSVP.hash({ - doc, + typedDoc, docType, }); } @@ -167,18 +186,21 @@ export default class DocumentRoute extends Route { * `modelIsChanging` property to remove and rerender the sidebar, * resetting its local state to reflect the new model data. */ - afterModel(model, transition) { + afterModel(model: any, transition: any) { if (transition.from) { if (transition.from.name === transition.to.name) { if ( transition.from.params.document_id !== transition.to.params.document_id ) { + // @ts-ignore + this.controller.set("modelIsChanging", true); htmlElement(".sidebar-body").scrollTop = 0; - scheduleOnce("afterRender", () => { + schedule("afterRender", () => { + // @ts-ignore this.controller.set("modelIsChanging", false); }); } diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index 053360b6b..4b0b5bdbf 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -1,9 +1,5 @@ import { GoogleUser } from "hermes/components/inputs/people-select"; -/** - * NOTE: This is a partial type definition. - * We are defining it incrementally as we expand TS coverage. - */ export interface HermesDocument { readonly objectID: string; @@ -11,6 +7,8 @@ export interface HermesDocument { product?: string; modifiedAgo: string; modifiedTime: number; + created: string; // E.g., "Aug 15, 2032" + createdTime: number; docNumber: string; docType: string; title: string; From 99a369db7370eb944192ceaeec23cb3c908820d3 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 18:44:59 -0400 Subject: [PATCH 03/16] Update modifiedTime --- .../components/dashboard/latest-updates.hbs | 6 ++--- .../components/dashboard/latest-updates.ts | 7 ----- web/app/components/doc/tile.hbs | 4 +-- web/app/components/doc/tile.ts | 2 +- web/app/components/document/sidebar.hbs | 5 ++-- web/app/components/document/sidebar.ts | 2 ++ .../controllers/authenticated/dashboard.ts | 3 +++ web/app/helpers/parse-date.ts | 4 +-- web/app/helpers/time-ago.ts | 22 ++++++++++++++++ web/app/routes/authenticated/dashboard.ts | 26 +------------------ web/app/routes/authenticated/document.ts | 13 ++++++---- web/app/services/recently-viewed-docs.ts | 4 --- web/app/templates/authenticated/dashboard.hbs | 2 +- web/app/types/document.d.ts | 1 - web/app/utils/{time-ago.js => time-ago.ts} | 6 ++--- web/mirage/factories/document.ts | 1 - .../services/recently-viewed-docs-test.ts | 14 ---------- 17 files changed, 51 insertions(+), 71 deletions(-) create mode 100644 web/app/helpers/time-ago.ts rename web/app/utils/{time-ago.js => time-ago.ts} (93%) diff --git a/web/app/components/dashboard/latest-updates.hbs b/web/app/components/dashboard/latest-updates.hbs index 8612d7760..d945e644b 100644 --- a/web/app/components/dashboard/latest-updates.hbs +++ b/web/app/components/dashboard/latest-updates.hbs @@ -1,4 +1,4 @@ -
+

{{else}} -
+
{{this.emptyStateMessage}}
{{/if}} diff --git a/web/app/components/dashboard/latest-updates.ts b/web/app/components/dashboard/latest-updates.ts index 779c34b7d..1de673bd8 100644 --- a/web/app/components/dashboard/latest-updates.ts +++ b/web/app/components/dashboard/latest-updates.ts @@ -86,13 +86,6 @@ export default class DashboardLatestUpdatesComponent extends Component) => { - // Add modifiedAgo for each doc. - for (const hit of result.hits as HermesDocument[]) { - if (hit.modifiedTime) { - const modifiedAgo = new Date(hit.modifiedTime * 1000); - hit.modifiedAgo = `Modified ${timeAgo(modifiedAgo)}`; - } - } return result.hits; }); diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index 5285ee558..cdc093eef 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -40,9 +40,9 @@ @imgURL={{@avatar}} @email="{{@owner}}" /> - {{#if @modifiedAgo}} + {{#if @modifiedTime}}

- {{@modifiedAgo}} + {{time-ago @modifiedTime}}

{{/if}}
diff --git a/web/app/components/doc/tile.ts b/web/app/components/doc/tile.ts index 44fb54def..d25774dd2 100644 --- a/web/app/components/doc/tile.ts +++ b/web/app/components/doc/tile.ts @@ -8,7 +8,7 @@ interface DocTileComponentSignature { docNumber?: string; isResult?: boolean; isDraft?: boolean; - modifiedAgo?: string; + modifiedTime?: number; owner?: string; productArea?: string; snippet?: string; diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index 24da671c2..d96df225f 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -286,12 +286,13 @@
{{! move the "createdDate" logic from the model to a helper }} -

{{or @document.createdDate "Unknown"}}

+

{{or @document.created "Unknown"}}

-

{{@document.lastModified}}

+

Modified + {{time-ago @document.modifiedTime}}

diff --git a/web/app/components/document/sidebar.ts b/web/app/components/document/sidebar.ts index 05a6d1fa3..bf26e4556 100644 --- a/web/app/components/document/sidebar.ts +++ b/web/app/components/document/sidebar.ts @@ -83,6 +83,8 @@ export default class DocumentSidebarComponent extends Component( ([time, monthFormat = "short"]: [ string | number | Date | undefined, - "short" | "long" + undefined | "short" | "long" ]) => { return parseDate(time, monthFormat); } diff --git a/web/app/helpers/time-ago.ts b/web/app/helpers/time-ago.ts new file mode 100644 index 000000000..facee663e --- /dev/null +++ b/web/app/helpers/time-ago.ts @@ -0,0 +1,22 @@ +import { helper } from "@ember/component/helper"; +import timeAgo from "hermes/utils/time-ago"; + +export interface TimeAgoHelperSignature { + Args: { + Positional: [time: number]; + }; + Return: string; +} + +const timeAgoHelper = helper(([time]: [number]) => { + time = time * 1000; + return `${timeAgo(time)}`; +}); + +export default timeAgoHelper; + +declare module "@glint/environment-ember-loose/registry" { + export default interface Registry { + "time-ago": typeof timeAgoHelper; + } +} diff --git a/web/app/routes/authenticated/dashboard.ts b/web/app/routes/authenticated/dashboard.ts index 6ceb5420b..a2c3f9c46 100644 --- a/web/app/routes/authenticated/dashboard.ts +++ b/web/app/routes/authenticated/dashboard.ts @@ -6,9 +6,6 @@ import FetchService from "hermes/services/fetch"; import RecentlyViewedDocsService from "hermes/services/recently-viewed-docs"; import SessionService from "hermes/services/session"; import AuthenticatedUserService from "hermes/services/authenticated-user"; - -// @ts-ignore - Not yet typed -import timeAgo from "hermes/utils/time-ago"; import { HermesDocument } from "hermes/types/document"; export default class DashboardRoute extends Route { @@ -31,28 +28,7 @@ export default class DashboardRoute extends Route { " AND appCreated:true" + " AND status:In-Review", }) - .then((result) => { - // Add modifiedAgo for each doc. - for (const hit of result.hits) { - this.fetchSvc - .fetch("/api/v1/documents/" + hit.objectID) - .then((resp) => resp?.json()) - .then((doc) => { - if (doc.modifiedTime) { - const modifiedDate = new Date(doc.modifiedTime * 1000); - // @ts-ignore - hit.modifiedAgo = `Modified ${timeAgo(modifiedDate)}`; - } - }) - .catch((err) => { - console.log( - `Error getting document waiting for review (${hit.objectID}):`, - err - ); - }); - } - return result.hits as HermesDocument[]; - }); + .then((result) => result.hits as HermesDocument[]); /** * If the user is loading the dashboard for the first time, diff --git a/web/app/routes/authenticated/document.ts b/web/app/routes/authenticated/document.ts index b1754c937..e1005e78c 100644 --- a/web/app/routes/authenticated/document.ts +++ b/web/app/routes/authenticated/document.ts @@ -18,6 +18,7 @@ import { HermesDocument, HermesUser } from "hermes/types/document"; import { DocumentsRouteParams } from "hermes/types/document-routes"; import Transition from "@ember/routing/transition"; import { HermesDocumentType } from "hermes/types/document-type"; +import AuthenticatedDocumentController from "hermes/controllers/authenticated/document"; const serializePeople = (people: GoogleUser[]): HermesUser[] => { return people.map((p) => ({ @@ -41,6 +42,8 @@ export default class DocumentRoute extends Route { @service declare flashMessages: FlashMessageService; @service declare router: RouterService; + declare controller: AuthenticatedDocumentController; + // Ideally we'd refresh the model when the draft query param changes, but // because of a suspected bug in Ember, we can't do that. // https://github.com/emberjs/ember.js/issues/19260 @@ -64,6 +67,7 @@ export default class DocumentRoute extends Route { let doc = {}; let draftFetched = false; + console.log("params.draft", params.draft); // Get doc data from the app backend. if (params.draft) { try { @@ -77,7 +81,6 @@ export default class DocumentRoute extends Route { }, }) .then((r) => r?.json()); - (doc as HermesDocument).isDraft = params.draft; draftFetched = true; } catch (err) { @@ -107,6 +110,7 @@ export default class DocumentRoute extends Route { .then((r) => r?.json()); (doc as HermesDocument).isDraft = false; + console.log("isDraft?", (doc as HermesDocument).isDraft); } catch (err) { const typedError = err as Error; this.showErrorMessage(typedError); @@ -165,6 +169,8 @@ export default class DocumentRoute extends Route { } } + console.log("typedDoc", typedDoc); + let docTypes = await this.fetchSvc .fetch("/api/v1/document-types") .then((r) => r?.json()); @@ -174,7 +180,7 @@ export default class DocumentRoute extends Route { ); return RSVP.hash({ - typedDoc, + doc: typedDoc, docType, }); } @@ -193,14 +199,11 @@ export default class DocumentRoute extends Route { transition.from.params.document_id !== transition.to.params.document_id ) { - // @ts-ignore - this.controller.set("modelIsChanging", true); htmlElement(".sidebar-body").scrollTop = 0; schedule("afterRender", () => { - // @ts-ignore this.controller.set("modelIsChanging", false); }); } diff --git a/web/app/services/recently-viewed-docs.ts b/web/app/services/recently-viewed-docs.ts index 008d089f9..83542f212 100644 --- a/web/app/services/recently-viewed-docs.ts +++ b/web/app/services/recently-viewed-docs.ts @@ -86,10 +86,6 @@ export default class RecentlyViewedDocsService extends Service { */ docResponses.forEach((response) => { if (response.status == "fulfilled") { - let recentlyViewed = response.value as RecentlyViewedDoc; - recentlyViewed.doc.modifiedAgo = `Modified ${timeAgo( - new Date(recentlyViewed.doc.modifiedTime * 1000) - )}`; newAll.push(response.value); } }); diff --git a/web/app/templates/authenticated/dashboard.hbs b/web/app/templates/authenticated/dashboard.hbs index d2649357c..38e8ebda3 100644 --- a/web/app/templates/authenticated/dashboard.hbs +++ b/web/app/templates/authenticated/dashboard.hbs @@ -34,7 +34,7 @@ @avatar={{get r.doc.ownerPhotos 0}} @docID={{r.doc.objectID}} @docNumber={{r.doc.docNumber}} - @modifiedAgo={{r.doc.modifiedAgo}} + @modifiedTime={{r.doc.modifiedTime}} @owner={{get r.doc.owners 0}} @productArea={{r.doc.product}} @status={{lowercase r.doc.status}} diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index 4b0b5bdbf..7099d3549 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -5,7 +5,6 @@ export interface HermesDocument { status: string; product?: string; - modifiedAgo: string; modifiedTime: number; created: string; // E.g., "Aug 15, 2032" createdTime: number; diff --git a/web/app/utils/time-ago.js b/web/app/utils/time-ago.ts similarity index 93% rename from web/app/utils/time-ago.js rename to web/app/utils/time-ago.ts index 05352a56b..f4ee6d1d1 100644 --- a/web/app/utils/time-ago.js +++ b/web/app/utils/time-ago.ts @@ -1,9 +1,9 @@ -export default function timeAgo(dateString) { +export default function timeAgo(date: number): string { const now = Date.now(); - const before = new Date(Date.parse(dateString)); + const before = new Date(date).getTime(); const elapsed = now - before; - const elapsedSeconds = elapsed / 1000; + if (elapsedSeconds < 2) { return "1 second ago"; } diff --git a/web/mirage/factories/document.ts b/web/mirage/factories/document.ts index 741383e46..5b97e5e1b 100644 --- a/web/mirage/factories/document.ts +++ b/web/mirage/factories/document.ts @@ -26,7 +26,6 @@ export default Factory.extend({ status: "Draft", product: "Vault", docType: "RFC", - modifiedAgo: 1000000000, modifiedTime: 1, createdTime: 1, appCreated: true, diff --git a/web/tests/unit/services/recently-viewed-docs-test.ts b/web/tests/unit/services/recently-viewed-docs-test.ts index 91eaaa2c7..f2b1e46f3 100644 --- a/web/tests/unit/services/recently-viewed-docs-test.ts +++ b/web/tests/unit/services/recently-viewed-docs-test.ts @@ -37,20 +37,6 @@ module("Unit | Service | recently-viewed-docs", function (hooks) { "recently viewed docs retrieved and trimmed to 4" ); - this.recentDocs.all?.forEach((recentDoc: RecentlyViewedDoc) => { - /** - * The Mirage factory sets the modifiedTime to 1 (1970), - * while we set our MockDate to 2000. That's obviously a 30 year difference, - * but our `timeAgo` function is inexact, assuming 28-day months, so it computes - * the difference as 32 years. - */ - assert.equal( - recentDoc.doc.modifiedAgo, - "Modified 32 years ago", - "modifiedAgo property is added" - ); - }); - MockDate.reset(); }); }); From f509ecdf4be6e07c8690b815dfd250cc2bfb25a1 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 19:42:53 -0400 Subject: [PATCH 04/16] Remove thumbnail, update Tile modifiedAgo --- web/app/components/dashboard/latest-updates.hbs | 3 +-- web/app/components/doc/tile.ts | 1 - web/app/types/document.d.ts | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/web/app/components/dashboard/latest-updates.hbs b/web/app/components/dashboard/latest-updates.hbs index d945e644b..e15b88a74 100644 --- a/web/app/components/dashboard/latest-updates.hbs +++ b/web/app/components/dashboard/latest-updates.hbs @@ -38,11 +38,10 @@ @avatar={{get doc.ownerPhotos 0}} @docID={{doc.objectID}} @docNumber={{doc.docNumber}} - @modifiedAgo={{time-ago doc.modifiedTime}} + @modifiedTime={{doc.modifiedTime}} @owner={{get doc.owners 0}} @productArea={{doc.product}} @status={{lowercase doc.status}} - @thumbnail={{doc.googleMetadata.thumbnailLink}} @title={{doc.title}} /> {{/each}} diff --git a/web/app/components/doc/tile.ts b/web/app/components/doc/tile.ts index d25774dd2..88083a539 100644 --- a/web/app/components/doc/tile.ts +++ b/web/app/components/doc/tile.ts @@ -13,7 +13,6 @@ interface DocTileComponentSignature { productArea?: string; snippet?: string; status?: string; - thumbnail?: string; title?: string; }; } diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index 7099d3549..7b923519d 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -23,7 +23,6 @@ export interface HermesDocument { isDraft?: boolean; customEditableFields?: CustomEditableFields; - thumbnail?: string; _snippetResult?: { content: { value: string; From 0c7bd3bba0f26d8895b43ebea9700eb8040c696a Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 19:48:10 -0400 Subject: [PATCH 05/16] Cleanup --- web/app/components/doc/row.hbs | 2 +- web/app/components/doc/row.ts | 2 +- web/app/components/document/sidebar.hbs | 1 - web/app/components/document/sidebar.ts | 2 -- web/app/components/row-results.hbs | 3 +-- 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/web/app/components/doc/row.hbs b/web/app/components/doc/row.hbs index 3082b7c4a..2b20ba924 100644 --- a/web/app/components/doc/row.hbs +++ b/web/app/components/doc/row.hbs @@ -30,5 +30,5 @@ - {{@created}} + {{@createdDate}} diff --git a/web/app/components/doc/row.ts b/web/app/components/doc/row.ts index b1a2fa435..55d748c60 100644 --- a/web/app/components/doc/row.ts +++ b/web/app/components/doc/row.ts @@ -3,7 +3,7 @@ import Component from "@glimmer/component"; interface DocRowComponentSignature { Args: { avatar: string; - created: string; + createdDate: string; docID: string; docNumber: string; docType: string; diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index d96df225f..c749d9e80 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -285,7 +285,6 @@
- {{! move the "createdDate" logic from the model to a helper }}

{{or @document.created "Unknown"}}

diff --git a/web/app/components/document/sidebar.ts b/web/app/components/document/sidebar.ts index bf26e4556..05a6d1fa3 100644 --- a/web/app/components/document/sidebar.ts +++ b/web/app/components/document/sidebar.ts @@ -83,8 +83,6 @@ export default class DocumentSidebarComponent extends Component <:body> {{#each @docs as |doc|}} - {{log "doc" doc}} Date: Tue, 5 Sep 2023 19:49:53 -0400 Subject: [PATCH 06/16] Fix modifiedTime type --- web/app/components/doc/tile.hbs | 8 +++----- web/app/components/doc/tile.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index cdc093eef..b380ca8a4 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -40,11 +40,9 @@ @imgURL={{@avatar}} @email="{{@owner}}" /> - {{#if @modifiedTime}} -

- {{time-ago @modifiedTime}} -

- {{/if}} +

+ {{time-ago @modifiedTime}} +

{{#if (and @isResult @snippet)}} diff --git a/web/app/components/doc/tile.ts b/web/app/components/doc/tile.ts index 88083a539..d6a6c2ef7 100644 --- a/web/app/components/doc/tile.ts +++ b/web/app/components/doc/tile.ts @@ -8,7 +8,7 @@ interface DocTileComponentSignature { docNumber?: string; isResult?: boolean; isDraft?: boolean; - modifiedTime?: number; + modifiedTime: number; owner?: string; productArea?: string; snippet?: string; From c1173aa6a48c0619ed4de6dbbe60dedbfe2d1292 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 19:59:45 -0400 Subject: [PATCH 07/16] Update `modifiedAgo` and `timeAgo` --- .../components/dashboard/latest-updates.hbs | 6 ++--- .../components/dashboard/latest-updates.ts | 11 +------- web/app/components/doc/tile.hbs | 8 +++--- web/app/components/doc/tile.ts | 2 +- web/app/helpers/time-ago.ts | 22 ++++++++++++++++ web/app/routes/authenticated/dashboard.ts | 25 +------------------ web/app/routes/authenticated/document.ts | 3 --- web/app/services/recently-viewed-docs.ts | 4 --- web/app/templates/authenticated/dashboard.hbs | 2 +- web/app/types/document.d.ts | 1 - web/app/utils/{time-ago.js => time-ago.ts} | 4 +-- web/mirage/factories/document.ts | 1 - .../services/recently-viewed-docs-test.ts | 18 +------------ 13 files changed, 35 insertions(+), 72 deletions(-) create mode 100644 web/app/helpers/time-ago.ts rename web/app/utils/{time-ago.js => time-ago.ts} (93%) diff --git a/web/app/components/dashboard/latest-updates.hbs b/web/app/components/dashboard/latest-updates.hbs index 8612d7760..bf58f0555 100644 --- a/web/app/components/dashboard/latest-updates.hbs +++ b/web/app/components/dashboard/latest-updates.hbs @@ -1,4 +1,4 @@ -
+

{{else}} -
+
{{this.emptyStateMessage}}
{{/if}} diff --git a/web/app/components/dashboard/latest-updates.ts b/web/app/components/dashboard/latest-updates.ts index 779c34b7d..fc62b5ec6 100644 --- a/web/app/components/dashboard/latest-updates.ts +++ b/web/app/components/dashboard/latest-updates.ts @@ -85,16 +85,7 @@ export default class DashboardLatestUpdatesComponent extends Component) => { - // Add modifiedAgo for each doc. - for (const hit of result.hits as HermesDocument[]) { - if (hit.modifiedTime) { - const modifiedAgo = new Date(hit.modifiedTime * 1000); - hit.modifiedAgo = `Modified ${timeAgo(modifiedAgo)}`; - } - } - return result.hits; - }); + .then((result: SearchResponse) => result.hits); // Update the docsToShow array with the new docs. this.docsToShow = newDocsToShow as HermesDocument[]; diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index 5285ee558..b380ca8a4 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -40,11 +40,9 @@ @imgURL={{@avatar}} @email="{{@owner}}" /> - {{#if @modifiedAgo}} -

- {{@modifiedAgo}} -

- {{/if}} +

+ {{time-ago @modifiedTime}} +

{{#if (and @isResult @snippet)}} diff --git a/web/app/components/doc/tile.ts b/web/app/components/doc/tile.ts index 44fb54def..e27ddabce 100644 --- a/web/app/components/doc/tile.ts +++ b/web/app/components/doc/tile.ts @@ -8,7 +8,7 @@ interface DocTileComponentSignature { docNumber?: string; isResult?: boolean; isDraft?: boolean; - modifiedAgo?: string; + modifiedTime: number; owner?: string; productArea?: string; snippet?: string; diff --git a/web/app/helpers/time-ago.ts b/web/app/helpers/time-ago.ts new file mode 100644 index 000000000..facee663e --- /dev/null +++ b/web/app/helpers/time-ago.ts @@ -0,0 +1,22 @@ +import { helper } from "@ember/component/helper"; +import timeAgo from "hermes/utils/time-ago"; + +export interface TimeAgoHelperSignature { + Args: { + Positional: [time: number]; + }; + Return: string; +} + +const timeAgoHelper = helper(([time]: [number]) => { + time = time * 1000; + return `${timeAgo(time)}`; +}); + +export default timeAgoHelper; + +declare module "@glint/environment-ember-loose/registry" { + export default interface Registry { + "time-ago": typeof timeAgoHelper; + } +} diff --git a/web/app/routes/authenticated/dashboard.ts b/web/app/routes/authenticated/dashboard.ts index 6ceb5420b..cf94f8d31 100644 --- a/web/app/routes/authenticated/dashboard.ts +++ b/web/app/routes/authenticated/dashboard.ts @@ -6,8 +6,6 @@ import FetchService from "hermes/services/fetch"; import RecentlyViewedDocsService from "hermes/services/recently-viewed-docs"; import SessionService from "hermes/services/session"; import AuthenticatedUserService from "hermes/services/authenticated-user"; - -// @ts-ignore - Not yet typed import timeAgo from "hermes/utils/time-ago"; import { HermesDocument } from "hermes/types/document"; @@ -31,28 +29,7 @@ export default class DashboardRoute extends Route { " AND appCreated:true" + " AND status:In-Review", }) - .then((result) => { - // Add modifiedAgo for each doc. - for (const hit of result.hits) { - this.fetchSvc - .fetch("/api/v1/documents/" + hit.objectID) - .then((resp) => resp?.json()) - .then((doc) => { - if (doc.modifiedTime) { - const modifiedDate = new Date(doc.modifiedTime * 1000); - // @ts-ignore - hit.modifiedAgo = `Modified ${timeAgo(modifiedDate)}`; - } - }) - .catch((err) => { - console.log( - `Error getting document waiting for review (${hit.objectID}):`, - err - ); - }); - } - return result.hits as HermesDocument[]; - }); + .then((result) => result.hits as HermesDocument[]); /** * If the user is loading the dashboard for the first time, diff --git a/web/app/routes/authenticated/document.ts b/web/app/routes/authenticated/document.ts index 4cd0147f3..b96c3a19d 100644 --- a/web/app/routes/authenticated/document.ts +++ b/web/app/routes/authenticated/document.ts @@ -105,9 +105,6 @@ export default class DocumentRoute extends Route { doc.createdDate = parseDate(doc.createdTime * 1000, "long"); } - // Build strings for created and last-modified. - doc.lastModified = `${timeAgo(new Date(doc.modifiedTime * 1000))}`; - // Record analytics. try { await this.fetchSvc.fetch("/api/v1/web/analytics", { diff --git a/web/app/services/recently-viewed-docs.ts b/web/app/services/recently-viewed-docs.ts index 008d089f9..83542f212 100644 --- a/web/app/services/recently-viewed-docs.ts +++ b/web/app/services/recently-viewed-docs.ts @@ -86,10 +86,6 @@ export default class RecentlyViewedDocsService extends Service { */ docResponses.forEach((response) => { if (response.status == "fulfilled") { - let recentlyViewed = response.value as RecentlyViewedDoc; - recentlyViewed.doc.modifiedAgo = `Modified ${timeAgo( - new Date(recentlyViewed.doc.modifiedTime * 1000) - )}`; newAll.push(response.value); } }); diff --git a/web/app/templates/authenticated/dashboard.hbs b/web/app/templates/authenticated/dashboard.hbs index d2649357c..38e8ebda3 100644 --- a/web/app/templates/authenticated/dashboard.hbs +++ b/web/app/templates/authenticated/dashboard.hbs @@ -34,7 +34,7 @@ @avatar={{get r.doc.ownerPhotos 0}} @docID={{r.doc.objectID}} @docNumber={{r.doc.docNumber}} - @modifiedAgo={{r.doc.modifiedAgo}} + @modifiedTime={{r.doc.modifiedTime}} @owner={{get r.doc.owners 0}} @productArea={{r.doc.product}} @status={{lowercase r.doc.status}} diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index 053360b6b..24a36e52d 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -9,7 +9,6 @@ export interface HermesDocument { status: string; product?: string; - modifiedAgo: string; modifiedTime: number; docNumber: string; docType: string; diff --git a/web/app/utils/time-ago.js b/web/app/utils/time-ago.ts similarity index 93% rename from web/app/utils/time-ago.js rename to web/app/utils/time-ago.ts index 05352a56b..a7dcce4c1 100644 --- a/web/app/utils/time-ago.js +++ b/web/app/utils/time-ago.ts @@ -1,6 +1,6 @@ -export default function timeAgo(dateString) { +export default function timeAgo(date: number) { const now = Date.now(); - const before = new Date(Date.parse(dateString)); + const before = new Date(date).getTime(); const elapsed = now - before; const elapsedSeconds = elapsed / 1000; diff --git a/web/mirage/factories/document.ts b/web/mirage/factories/document.ts index 741383e46..5b97e5e1b 100644 --- a/web/mirage/factories/document.ts +++ b/web/mirage/factories/document.ts @@ -26,7 +26,6 @@ export default Factory.extend({ status: "Draft", product: "Vault", docType: "RFC", - modifiedAgo: 1000000000, modifiedTime: 1, createdTime: 1, appCreated: true, diff --git a/web/tests/unit/services/recently-viewed-docs-test.ts b/web/tests/unit/services/recently-viewed-docs-test.ts index 91eaaa2c7..a0eb5cd54 100644 --- a/web/tests/unit/services/recently-viewed-docs-test.ts +++ b/web/tests/unit/services/recently-viewed-docs-test.ts @@ -1,8 +1,6 @@ import { module, test } from "qunit"; import { setupTest } from "ember-qunit"; -import RecentlyViewedDocsService, { - RecentlyViewedDoc, -} from "hermes/services/recently-viewed-docs"; +import RecentlyViewedDocsService from "hermes/services/recently-viewed-docs"; import { MirageTestContext, setupMirage } from "ember-cli-mirage/test-support"; import { waitUntil } from "@ember/test-helpers"; import MockDate from "mockdate"; @@ -37,20 +35,6 @@ module("Unit | Service | recently-viewed-docs", function (hooks) { "recently viewed docs retrieved and trimmed to 4" ); - this.recentDocs.all?.forEach((recentDoc: RecentlyViewedDoc) => { - /** - * The Mirage factory sets the modifiedTime to 1 (1970), - * while we set our MockDate to 2000. That's obviously a 30 year difference, - * but our `timeAgo` function is inexact, assuming 28-day months, so it computes - * the difference as 32 years. - */ - assert.equal( - recentDoc.doc.modifiedAgo, - "Modified 32 years ago", - "modifiedAgo property is added" - ); - }); - MockDate.reset(); }); }); From 32961e7f071a68dd047adca427f61dda35db0ba2 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 20:16:11 -0400 Subject: [PATCH 08/16] Add tests --- web/app/helpers/time-ago.ts | 5 ++- .../integration/helpers/time-ago-test.ts | 45 +++++++++++++++++++ web/tests/unit/utils/time-ago-test.ts | 20 +++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 web/tests/integration/helpers/time-ago-test.ts create mode 100644 web/tests/unit/utils/time-ago-test.ts diff --git a/web/app/helpers/time-ago.ts b/web/app/helpers/time-ago.ts index facee663e..884b06048 100644 --- a/web/app/helpers/time-ago.ts +++ b/web/app/helpers/time-ago.ts @@ -9,7 +9,10 @@ export interface TimeAgoHelperSignature { } const timeAgoHelper = helper(([time]: [number]) => { - time = time * 1000; + // if the time is in seconds, convert to milliseconds + if (time < 10000000000) { + time = time * 1000; + } return `${timeAgo(time)}`; }); diff --git a/web/tests/integration/helpers/time-ago-test.ts b/web/tests/integration/helpers/time-ago-test.ts new file mode 100644 index 000000000..0ec3ce22d --- /dev/null +++ b/web/tests/integration/helpers/time-ago-test.ts @@ -0,0 +1,45 @@ +import { module, test } from "qunit"; +import { setupRenderingTest } from "ember-qunit"; +import { TestContext, render } from "@ember/test-helpers"; +import { hbs } from "ember-cli-htmlbars"; +import MockDate from "mockdate"; + +interface TimeAgoTestContext extends TestContext { + fiveSecondsAgo: number; + twoYearsAgo: number; + sevenMonthsAgo: number; +} + +module("Integration | Helper | time-ago", function (hooks) { + setupRenderingTest(hooks); + + test("it computes the time ago", async function (assert) { + MockDate.set("2000-01-01T06:00:00.000-07:00"); + const now = Date.now(); + const fiveSecondsAgo = now - 5000; + const sevenMonthsAgo = now - 18144000000; + const twoYearsAgo = now - 63072000000; + + this.set("fiveSecondsAgo", fiveSecondsAgo); + this.set("twoYearsAgo", twoYearsAgo); + this.set("sevenMonthsAgo", sevenMonthsAgo); + + await render(hbs` +
+ {{time-ago this.fiveSecondsAgo}} +
+
+ {{time-ago this.twoYearsAgo}} +
+
+ {{time-ago this.sevenMonthsAgo}} +
+ `); + + assert.dom(".one").hasText("5 seconds ago"); + assert.dom(".two").hasText("2 years ago"); + assert.dom(".three").hasText("7 months ago"); + + MockDate.reset(); + }); +}); diff --git a/web/tests/unit/utils/time-ago-test.ts b/web/tests/unit/utils/time-ago-test.ts new file mode 100644 index 000000000..fc68fb41c --- /dev/null +++ b/web/tests/unit/utils/time-ago-test.ts @@ -0,0 +1,20 @@ +import timeAgo from "hermes/utils/time-ago"; +import { module, test } from "qunit"; +import MockDate from "mockdate"; + +module("Unit | Utility | time-ago", function () { + test('it returns a "time ago" value for a date', function (assert) { + MockDate.set("2000-01-01T06:00:00.000-07:00"); + + const now = Date.now(); + + assert.equal("2 years ago", timeAgo(now - 63072000000)); + assert.equal("5 seconds ago", timeAgo(now - 5000)); + assert.equal("1 minute ago", timeAgo(now - 60000)); + assert.equal("5 hours ago", timeAgo(now - 18000000)); + assert.equal("3 months ago", timeAgo(now - 7776000000)); + assert.equal("2 years ago", timeAgo(now - 63072000000)); + + MockDate.reset(); + }); +}); From 7097b8ea8715c47d2a2d3af39d4d8aa1dacc55c8 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Tue, 5 Sep 2023 20:20:47 -0400 Subject: [PATCH 09/16] Rework "modifiedTime" calculations --- web/app/helpers/time-ago.ts | 10 ++++------ web/app/utils/time-ago.ts | 4 ++-- web/tests/integration/helpers/time-ago-test.ts | 8 ++++---- web/tests/unit/utils/time-ago-test.ts | 1 - 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/web/app/helpers/time-ago.ts b/web/app/helpers/time-ago.ts index 884b06048..15c3385bf 100644 --- a/web/app/helpers/time-ago.ts +++ b/web/app/helpers/time-ago.ts @@ -8,13 +8,11 @@ export interface TimeAgoHelperSignature { Return: string; } -const timeAgoHelper = helper(([time]: [number]) => { - // if the time is in seconds, convert to milliseconds - if (time < 10000000000) { - time = time * 1000; +const timeAgoHelper = helper( + ([secondsAgo]: [number]) => { + return `${timeAgo(secondsAgo)}`; } - return `${timeAgo(time)}`; -}); +); export default timeAgoHelper; diff --git a/web/app/utils/time-ago.ts b/web/app/utils/time-ago.ts index a7dcce4c1..19fbaa9dc 100644 --- a/web/app/utils/time-ago.ts +++ b/web/app/utils/time-ago.ts @@ -1,6 +1,6 @@ -export default function timeAgo(date: number) { +export default function timeAgo(timeInSeconds: number) { const now = Date.now(); - const before = new Date(date).getTime(); + const before = new Date(timeInSeconds * 1000).getTime(); const elapsed = now - before; const elapsedSeconds = elapsed / 1000; diff --git a/web/tests/integration/helpers/time-ago-test.ts b/web/tests/integration/helpers/time-ago-test.ts index 0ec3ce22d..a778dfbf0 100644 --- a/web/tests/integration/helpers/time-ago-test.ts +++ b/web/tests/integration/helpers/time-ago-test.ts @@ -15,10 +15,10 @@ module("Integration | Helper | time-ago", function (hooks) { test("it computes the time ago", async function (assert) { MockDate.set("2000-01-01T06:00:00.000-07:00"); - const now = Date.now(); - const fiveSecondsAgo = now - 5000; - const sevenMonthsAgo = now - 18144000000; - const twoYearsAgo = now - 63072000000; + const now = Date.now() / 1000; + const fiveSecondsAgo = now - 5; + const twoYearsAgo = now - 63072000; + const sevenMonthsAgo = now - 18144000; this.set("fiveSecondsAgo", fiveSecondsAgo); this.set("twoYearsAgo", twoYearsAgo); diff --git a/web/tests/unit/utils/time-ago-test.ts b/web/tests/unit/utils/time-ago-test.ts index fc68fb41c..e2cdcbaaf 100644 --- a/web/tests/unit/utils/time-ago-test.ts +++ b/web/tests/unit/utils/time-ago-test.ts @@ -8,7 +8,6 @@ module("Unit | Utility | time-ago", function () { const now = Date.now(); - assert.equal("2 years ago", timeAgo(now - 63072000000)); assert.equal("5 seconds ago", timeAgo(now - 5000)); assert.equal("1 minute ago", timeAgo(now - 60000)); assert.equal("5 hours ago", timeAgo(now - 18000000)); From 028722218902266b791acf431ad4d61642893f93 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 6 Sep 2023 10:10:12 -0400 Subject: [PATCH 10/16] Fix sidebar timeAgo; add documentation --- web/app/components/document/sidebar.hbs | 2 +- web/app/utils/time-ago.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index 4826d42f4..40dc4e9ef 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -290,7 +290,7 @@
-

{{@document.lastModified}}

+

{{time-ago @document.modifiedTime}}

diff --git a/web/app/utils/time-ago.ts b/web/app/utils/time-ago.ts index 19fbaa9dc..c7296e364 100644 --- a/web/app/utils/time-ago.ts +++ b/web/app/utils/time-ago.ts @@ -1,3 +1,10 @@ +/** + * A simplified "time ago" calculation, based on 28-day months. + * Intended to give a rough estimate of how long ago something happened. + * Used by the `time-ago` helper to convert numeric timestamps to strings. + * + * TODO: Replace with something more precise. + */ export default function timeAgo(timeInSeconds: number) { const now = Date.now(); const before = new Date(timeInSeconds * 1000).getTime(); From 9365ad131619ae9e47934d37d98aa8c7abd0b9c3 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 6 Sep 2023 10:17:18 -0400 Subject: [PATCH 11/16] Add "Modified" to tile --- web/app/components/doc/tile.hbs | 1 + 1 file changed, 1 insertion(+) diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index b380ca8a4..4fd326357 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -41,6 +41,7 @@ @email="{{@owner}}" />

+ Modified {{time-ago @modifiedTime}}

From 1637b68d16b7e23c267d73d468b8a48243370d40 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 6 Sep 2023 10:33:06 -0400 Subject: [PATCH 12/16] Remove console.logs --- web/app/routes/authenticated/document.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/web/app/routes/authenticated/document.ts b/web/app/routes/authenticated/document.ts index eb8ea0dc8..a7771e65d 100644 --- a/web/app/routes/authenticated/document.ts +++ b/web/app/routes/authenticated/document.ts @@ -63,7 +63,6 @@ export default class DocumentRoute extends Route { let doc = {}; let draftFetched = false; - console.log("params.draft", params.draft); // Get doc data from the app backend. if (params.draft) { try { @@ -106,7 +105,6 @@ export default class DocumentRoute extends Route { .then((r) => r?.json()); (doc as HermesDocument).isDraft = false; - console.log("isDraft?", (doc as HermesDocument).isDraft); } catch (err) { const typedError = err as Error; this.showErrorMessage(typedError); @@ -121,8 +119,6 @@ export default class DocumentRoute extends Route { // make a background call to update the front-end index. void this.recentDocs.fetchAll.perform(); - console.log("Document fetched: ", doc); - let typedDoc = doc as HermesDocument; // Record analytics. @@ -165,8 +161,6 @@ export default class DocumentRoute extends Route { } } - console.log("typedDoc", typedDoc); - let docTypes = await this.fetchSvc .fetch("/api/v1/document-types") .then((r) => r?.json()); From adb2db109b85a28a1401dcaf967f7063c654e005 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Wed, 6 Sep 2023 10:35:02 -0400 Subject: [PATCH 13/16] Update document.ts --- web/app/routes/authenticated/document.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/app/routes/authenticated/document.ts b/web/app/routes/authenticated/document.ts index a7771e65d..8ef126942 100644 --- a/web/app/routes/authenticated/document.ts +++ b/web/app/routes/authenticated/document.ts @@ -28,6 +28,11 @@ interface DocumentRouteParams { draft: boolean; } +interface DocumentRouteModel { + doc: HermesDocument; + docType: HermesDocumentType; +} + export default class DocumentRoute extends Route { @service("config") declare configSvcL: ConfigService; @service("fetch") declare fetchSvc: FetchService; @@ -182,7 +187,7 @@ export default class DocumentRoute extends Route { * `modelIsChanging` property to remove and rerender the sidebar, * resetting its local state to reflect the new model data. */ - afterModel(model: any, transition: any) { + afterModel(_model: DocumentRouteModel, transition: any) { if (transition.from) { if (transition.from.name === transition.to.name) { if ( From a02f44287ce92af5b6f096687572e789d10be2e1 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Thu, 7 Sep 2023 21:11:38 -0400 Subject: [PATCH 14/16] Fix incorrect types and test --- web/app/components/doc/tile.hbs | 10 ++++++---- web/app/components/doc/tile.ts | 2 +- web/app/components/document/sidebar.hbs | 4 +++- web/app/types/document.d.ts | 2 +- web/tests/unit/utils/time-ago-test.ts | 13 +++++++------ 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/web/app/components/doc/tile.hbs b/web/app/components/doc/tile.hbs index 4fd326357..022763e51 100644 --- a/web/app/components/doc/tile.hbs +++ b/web/app/components/doc/tile.hbs @@ -40,10 +40,12 @@ @imgURL={{@avatar}} @email="{{@owner}}" /> -

- Modified - {{time-ago @modifiedTime}} -

+ {{#if @modifiedTime}} +

+ Modified + {{time-ago @modifiedTime}} +

+ {{/if}}

{{#if (and @isResult @snippet)}} diff --git a/web/app/components/doc/tile.ts b/web/app/components/doc/tile.ts index e27ddabce..d25774dd2 100644 --- a/web/app/components/doc/tile.ts +++ b/web/app/components/doc/tile.ts @@ -8,7 +8,7 @@ interface DocTileComponentSignature { docNumber?: string; isResult?: boolean; isDraft?: boolean; - modifiedTime: number; + modifiedTime?: number; owner?: string; productArea?: string; snippet?: string; diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index 40dc4e9ef..5aebbd3cc 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -290,7 +290,9 @@
-

{{time-ago @document.modifiedTime}}

+ {{#if @document.modifiedTime}} +

{{time-ago @document.modifiedTime}}

+ {{/if}}
diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index 24a36e52d..5334899d1 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -9,7 +9,7 @@ export interface HermesDocument { status: string; product?: string; - modifiedTime: number; + modifiedTime?: number; // Not available on drafts fetched as Hits from backend docNumber: string; docType: string; title: string; diff --git a/web/tests/unit/utils/time-ago-test.ts b/web/tests/unit/utils/time-ago-test.ts index e2cdcbaaf..c54ba1aca 100644 --- a/web/tests/unit/utils/time-ago-test.ts +++ b/web/tests/unit/utils/time-ago-test.ts @@ -6,13 +6,14 @@ module("Unit | Utility | time-ago", function () { test('it returns a "time ago" value for a date', function (assert) { MockDate.set("2000-01-01T06:00:00.000-07:00"); - const now = Date.now(); + const now = Date.now() / 1000; - assert.equal("5 seconds ago", timeAgo(now - 5000)); - assert.equal("1 minute ago", timeAgo(now - 60000)); - assert.equal("5 hours ago", timeAgo(now - 18000000)); - assert.equal("3 months ago", timeAgo(now - 7776000000)); - assert.equal("2 years ago", timeAgo(now - 63072000000)); + assert.equal("5 seconds ago", timeAgo(now - 5)); + assert.equal("1 minute ago", timeAgo(now - 60)); + assert.equal("5 minutes ago", timeAgo(now - 300)); + assert.equal("6 hours ago", timeAgo(now - 21600)); + assert.equal("2 months ago", timeAgo(now - 5184000)); + assert.equal("2 years ago", timeAgo(now - 63072000)); MockDate.reset(); }); From f16922da666b7a1acf1a3c2792f11875bed9044b Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Mon, 18 Sep 2023 17:27:07 -0400 Subject: [PATCH 15/16] Add created types --- web/app/types/document.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index bb7cb2da0..dcd60e76e 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -5,6 +5,8 @@ export interface HermesDocument { status: string; product?: string; + created: string; // E.g., "Aug 16, 2023" + createdTime: number; modifiedTime?: number; // Not available on drafts fetched as Hits from backend docNumber: string; docType: string; From 6712e10c0e34cb53af1b76d441128524a53228ae Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Mon, 18 Sep 2023 17:47:53 -0400 Subject: [PATCH 16/16] Clarify types --- web/app/components/document/sidebar.hbs | 2 +- web/app/components/row-results.hbs | 2 +- web/app/helpers/parse-date.ts | 7 ++----- web/app/types/document.d.ts | 21 +++++++++++++++++++-- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/web/app/components/document/sidebar.hbs b/web/app/components/document/sidebar.hbs index d59caa1d1..09331a5f9 100644 --- a/web/app/components/document/sidebar.hbs +++ b/web/app/components/document/sidebar.hbs @@ -285,7 +285,7 @@
-

{{or @document.created "Unknown"}}

+

{{or (parse-date @document.created "long") "Unknown"}}

diff --git a/web/app/components/row-results.hbs b/web/app/components/row-results.hbs index df8d25010..fbac6639f 100644 --- a/web/app/components/row-results.hbs +++ b/web/app/components/row-results.hbs @@ -31,7 +31,7 @@ {{#each @docs as |doc|}} ( - ([time, monthFormat = "short"]: [ - string | number | Date | undefined, - undefined | "short" | "long" - ]) => { + ([time, monthFormat = "short"]) => { return parseDate(time, monthFormat); } ); diff --git a/web/app/types/document.d.ts b/web/app/types/document.d.ts index dcd60e76e..0f1eba0e8 100644 --- a/web/app/types/document.d.ts +++ b/web/app/types/document.d.ts @@ -5,9 +5,26 @@ export interface HermesDocument { status: string; product?: string; - created: string; // E.g., "Aug 16, 2023" + + /** + * A human-readable date string, e.g., "Aug 16, 2028". + * Mutated in the layout by the `parse-date` helper to place + * the date before the month. + */ + created: string; + + /** + * A timestamp in seconds. Used for sorting. + */ createdTime: number; - modifiedTime?: number; // Not available on drafts fetched as Hits from backend + + /** + * A timestamp in seconds. Used Translated by the `time-ago` helper + * into a human-readable string, e.g., "2 days ago." + * Not available on drafts fetched as Hits from backend. + */ + modifiedTime?: number; + docNumber: string; docType: string; title: string;