From 5c839998c1aff537917cb5f6ca456857ab8b85f2 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Sat, 9 Sep 2023 21:01:14 -0400 Subject: [PATCH] fix: Initials do not display on notification avatars (#5803) --- app/components/PaginatedList.test.tsx | 2 +- app/components/PaginatedList.tsx | 2 +- app/components/WebsocketProvider.tsx | 2 +- app/hooks/usePolicy.ts | 4 +- app/models/ApiKey.ts | 4 +- app/models/AuthenticationProvider.ts | 4 +- app/models/Collection.ts | 2 +- app/models/CollectionGroupMembership.ts | 4 +- app/models/Comment.ts | 13 ++-- app/models/Document.ts | 2 +- app/models/Event.ts | 8 +-- app/models/FileOperation.ts | 6 +- app/models/Group.ts | 6 +- app/models/GroupMembership.ts | 4 +- app/models/Integration.ts | 4 +- app/models/Membership.ts | 4 +- app/models/Notification.ts | 14 +++-- app/models/ParanoidModel.ts | 5 -- app/models/Pin.ts | 4 +- app/models/Policy.ts | 4 +- app/models/Revision.ts | 8 +-- app/models/SearchQuery.ts | 6 +- app/models/Share.ts | 4 +- app/models/Star.ts | 8 +-- app/models/Subscription.ts | 7 +-- app/models/Team.ts | 4 +- app/models/User.ts | 6 +- app/models/View.ts | 4 +- app/models/WebhookSubscription.ts | 4 +- app/models/{BaseModel.ts => base/Model.ts} | 27 +++++---- app/models/base/ParanoidModel.ts | 5 ++ app/models/decorators/Field.ts | 4 +- app/models/decorators/Relation.ts | 59 +++++++++++++++++++ app/scenes/Collection/MembershipPreview.tsx | 2 +- app/scenes/GroupNew.tsx | 5 +- app/scenes/Search/Search.tsx | 2 +- app/scenes/Settings/Members.tsx | 2 +- app/scenes/Settings/Shares.tsx | 2 +- app/stores/ApiKeysStore.ts | 4 +- app/stores/AuthStore.ts | 20 +++---- app/stores/AuthenticationProvidersStore.ts | 4 +- app/stores/CollectionGroupMembershipsStore.ts | 4 +- app/stores/CollectionsStore.ts | 4 +- app/stores/CommentsStore.ts | 4 +- app/stores/DocumentsStore.ts | 22 +++---- app/stores/EventsStore.ts | 4 +- app/stores/FileOperationsStore.ts | 4 +- app/stores/GroupMembershipsStore.ts | 4 +- app/stores/GroupsStore.ts | 4 +- app/stores/IntegrationsStore.ts | 4 +- app/stores/MembershipsStore.ts | 4 +- app/stores/NotificationsStore.ts | 4 +- app/stores/PinsStore.ts | 4 +- app/stores/PoliciesStore.ts | 4 +- app/stores/RevisionsStore.ts | 4 +- app/stores/SearchesStore.ts | 4 +- app/stores/SharesStore.ts | 4 +- app/stores/StarsStore.ts | 4 +- app/stores/SubscriptionsStore.ts | 4 +- app/stores/UsersStore.ts | 4 +- app/stores/ViewsStore.ts | 4 +- app/stores/WebhookSubscriptionStore.ts | 4 +- app/stores/{BaseStore.ts => base/Store.ts} | 6 +- app/stores/index.ts | 6 ++ app/typings/window.d.ts | 4 ++ 65 files changed, 238 insertions(+), 165 deletions(-) delete mode 100644 app/models/ParanoidModel.ts rename app/models/{BaseModel.ts => base/Model.ts} (84%) create mode 100644 app/models/base/ParanoidModel.ts create mode 100644 app/models/decorators/Relation.ts rename app/stores/{BaseStore.ts => base/Store.ts} (97%) diff --git a/app/components/PaginatedList.test.tsx b/app/components/PaginatedList.test.tsx index c59a86c56..079757954 100644 --- a/app/components/PaginatedList.test.tsx +++ b/app/components/PaginatedList.test.tsx @@ -3,8 +3,8 @@ import { shallow } from "enzyme"; import { TFunction } from "i18next"; import * as React from "react"; import { getI18n } from "react-i18next"; -import { DEFAULT_PAGINATION_LIMIT } from "~/stores/BaseStore"; import RootStore from "~/stores/RootStore"; +import { DEFAULT_PAGINATION_LIMIT } from "~/stores/base/Store"; import { runAllPromises } from "~/test/support"; import { Component as PaginatedList } from "./PaginatedList"; diff --git a/app/components/PaginatedList.tsx b/app/components/PaginatedList.tsx index 4e30ec530..407bbb038 100644 --- a/app/components/PaginatedList.tsx +++ b/app/components/PaginatedList.tsx @@ -5,8 +5,8 @@ import * as React from "react"; import { withTranslation, WithTranslation } from "react-i18next"; import { Waypoint } from "react-waypoint"; import { CompositeStateReturn } from "reakit/Composite"; -import { DEFAULT_PAGINATION_LIMIT } from "~/stores/BaseStore"; import RootStore from "~/stores/RootStore"; +import { DEFAULT_PAGINATION_LIMIT } from "~/stores/base/Store"; import ArrowKeyNavigation from "~/components/ArrowKeyNavigation"; import DelayedMount from "~/components/DelayedMount"; import PlaceholderList from "~/components/List/Placeholder"; diff --git a/app/components/WebsocketProvider.tsx b/app/components/WebsocketProvider.tsx index 6c19b0629..f12495dec 100644 --- a/app/components/WebsocketProvider.tsx +++ b/app/components/WebsocketProvider.tsx @@ -317,7 +317,7 @@ class WebsocketProvider extends React.Component { }); } - auth.team?.updateFromJson(event); + auth.team?.updateData(event); }); this.socket.on( diff --git a/app/hooks/usePolicy.ts b/app/hooks/usePolicy.ts index 996e4ea2c..f674fe2cc 100644 --- a/app/hooks/usePolicy.ts +++ b/app/hooks/usePolicy.ts @@ -1,5 +1,5 @@ import * as React from "react"; -import BaseModel from "~/models/BaseModel"; +import Model from "~/models/base/Model"; import useStores from "./useStores"; /** @@ -9,7 +9,7 @@ import useStores from "./useStores"; * @param entity The model or model id * @returns The policy for the model */ -export default function usePolicy(entity?: string | BaseModel | null) { +export default function usePolicy(entity?: string | Model | null) { const { policies } = useStores(); const triggered = React.useRef(false); const entityId = entity diff --git a/app/models/ApiKey.ts b/app/models/ApiKey.ts index 8d02e76aa..5d69535fc 100644 --- a/app/models/ApiKey.ts +++ b/app/models/ApiKey.ts @@ -1,8 +1,8 @@ import { observable } from "mobx"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class ApiKey extends BaseModel { +class ApiKey extends Model { @Field @observable id: string; diff --git a/app/models/AuthenticationProvider.ts b/app/models/AuthenticationProvider.ts index f292651a8..6d5dd62a5 100644 --- a/app/models/AuthenticationProvider.ts +++ b/app/models/AuthenticationProvider.ts @@ -1,8 +1,8 @@ import { computed, observable } from "mobx"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class AuthenticationProvider extends BaseModel { +class AuthenticationProvider extends Model { id: string; displayName: string; diff --git a/app/models/Collection.ts b/app/models/Collection.ts index b7c43099d..8831d3130 100644 --- a/app/models/Collection.ts +++ b/app/models/Collection.ts @@ -8,7 +8,7 @@ import { import { sortNavigationNodes } from "@shared/utils/collections"; import type CollectionsStore from "~/stores/CollectionsStore"; import Document from "~/models/Document"; -import ParanoidModel from "~/models/ParanoidModel"; +import ParanoidModel from "~/models/base/ParanoidModel"; import { client } from "~/utils/ApiClient"; import Field from "./decorators/Field"; diff --git a/app/models/CollectionGroupMembership.ts b/app/models/CollectionGroupMembership.ts index af533ed8d..1dbab80a0 100644 --- a/app/models/CollectionGroupMembership.ts +++ b/app/models/CollectionGroupMembership.ts @@ -1,8 +1,8 @@ import { observable } from "mobx"; import { CollectionPermission } from "@shared/types"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; -class CollectionGroupMembership extends BaseModel { +class CollectionGroupMembership extends Model { id: string; groupId: string; diff --git a/app/models/Comment.ts b/app/models/Comment.ts index 1d7b05ae3..e92ea2111 100644 --- a/app/models/Comment.ts +++ b/app/models/Comment.ts @@ -3,10 +3,11 @@ import { computed, observable } from "mobx"; import { now } from "mobx-utils"; import type { ProsemirrorData } from "@shared/types"; import User from "~/models/User"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; +import Relation from "./decorators/Relation"; -class Comment extends BaseModel { +class Comment extends Model { /** * Map to keep track of which users are currently typing a reply in this * comments thread. @@ -40,18 +41,16 @@ class Comment extends BaseModel { @observable documentId: string; - createdAt: string; - + @Relation(() => User) createdBy: User; createdById: string; resolvedAt: string; + @Relation(() => User) resolvedBy: User; - updatedAt: string; - /** * An array of users that are currently typing a reply in this comments thread. */ @@ -60,7 +59,7 @@ class Comment extends BaseModel { return Array.from(this.typingUsers.entries()) .filter(([, lastReceivedDate]) => lastReceivedDate > subSeconds(now(), 3)) .map(([userId]) => this.store.rootStore.users.get(userId)) - .filter(Boolean); + .filter(Boolean) as User[]; } } diff --git a/app/models/Document.ts b/app/models/Document.ts index 1e75be5a0..bd3da68f3 100644 --- a/app/models/Document.ts +++ b/app/models/Document.ts @@ -8,8 +8,8 @@ import { isRTL } from "@shared/utils/rtl"; import DocumentsStore from "~/stores/DocumentsStore"; import User from "~/models/User"; import { client } from "~/utils/ApiClient"; -import ParanoidModel from "./ParanoidModel"; import View from "./View"; +import ParanoidModel from "./base/ParanoidModel"; import Field from "./decorators/Field"; type SaveOptions = { diff --git a/app/models/Event.ts b/app/models/Event.ts index 5950c9a84..49f230afc 100644 --- a/app/models/Event.ts +++ b/app/models/Event.ts @@ -1,7 +1,8 @@ -import BaseModel from "./BaseModel"; import User from "./User"; +import Model from "./base/Model"; +import Relation from "./decorators/Relation"; -class Event extends BaseModel { +class Event extends Model { id: string; name: string; @@ -18,8 +19,7 @@ class Event extends BaseModel { userId: string; - createdAt: string; - + @Relation(() => User) actor: User; data: { diff --git a/app/models/FileOperation.ts b/app/models/FileOperation.ts index cf0f9d484..b4a1f0776 100644 --- a/app/models/FileOperation.ts +++ b/app/models/FileOperation.ts @@ -1,10 +1,10 @@ import { computed } from "mobx"; import { FileOperationFormat, FileOperationType } from "@shared/types"; import { bytesToHumanReadable } from "@shared/utils/files"; -import BaseModel from "./BaseModel"; import User from "./User"; +import Model from "./base/Model"; -class FileOperation extends BaseModel { +class FileOperation extends Model { id: string; state: string; @@ -23,8 +23,6 @@ class FileOperation extends BaseModel { user: User; - createdAt: string; - @computed get sizeInMB(): string { return bytesToHumanReadable(this.size); diff --git a/app/models/Group.ts b/app/models/Group.ts index 2a20f73e7..3c6e20ef3 100644 --- a/app/models/Group.ts +++ b/app/models/Group.ts @@ -1,8 +1,8 @@ import { observable } from "mobx"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class Group extends BaseModel { +class Group extends Model { @Field @observable id: string; @@ -12,8 +12,6 @@ class Group extends BaseModel { name: string; memberCount: number; - - updatedAt: string; } export default Group; diff --git a/app/models/GroupMembership.ts b/app/models/GroupMembership.ts index ef71c6481..43cd19cff 100644 --- a/app/models/GroupMembership.ts +++ b/app/models/GroupMembership.ts @@ -1,7 +1,7 @@ -import BaseModel from "./BaseModel"; import User from "./User"; +import Model from "./base/Model"; -class GroupMembership extends BaseModel { +class GroupMembership extends Model { id: string; userId: string; diff --git a/app/models/Integration.ts b/app/models/Integration.ts index 5799ea78b..c73a4d5b6 100644 --- a/app/models/Integration.ts +++ b/app/models/Integration.ts @@ -4,10 +4,10 @@ import type { IntegrationSettings, IntegrationType, } from "@shared/types"; -import BaseModel from "~/models/BaseModel"; +import Model from "~/models/base/Model"; import Field from "./decorators/Field"; -class Integration extends BaseModel { +class Integration extends Model { id: string; type: IntegrationType; diff --git a/app/models/Membership.ts b/app/models/Membership.ts index fdd4abbc7..1c6ef4ebb 100644 --- a/app/models/Membership.ts +++ b/app/models/Membership.ts @@ -1,8 +1,8 @@ import { observable } from "mobx"; import { CollectionPermission } from "@shared/types"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; -class Membership extends BaseModel { +class Membership extends Model { id: string; userId: string; diff --git a/app/models/Notification.ts b/app/models/Notification.ts index deedfbee2..a3a27e2e4 100644 --- a/app/models/Notification.ts +++ b/app/models/Notification.ts @@ -7,13 +7,14 @@ import { documentPath, settingsPath, } from "~/utils/routeHelpers"; -import BaseModel from "./BaseModel"; import Comment from "./Comment"; import Document from "./Document"; import User from "./User"; +import Model from "./base/Model"; import Field from "./decorators/Field"; +import Relation from "./decorators/Relation"; -class Notification extends BaseModel { +class Notification extends Model { @Field @observable id: string; @@ -35,6 +36,7 @@ class Notification extends BaseModel { /** * The user that triggered the notification. */ + @Relation(() => User) actor?: User; /** @@ -45,6 +47,7 @@ class Notification extends BaseModel { /** * The document that the notification is associated with. */ + @Relation(() => Document) document?: Document; /** @@ -55,6 +58,7 @@ class Notification extends BaseModel { /** * The comment that the notification is associated with. */ + @Relation(() => Comment) comment?: Comment; /** @@ -137,9 +141,9 @@ class Notification extends BaseModel { return this.document ? documentPath(this.document) : ""; } case NotificationEventType.CreateCollection: { - const collection = this.store.rootStore.documents.get( - this.collectionId - ); + const collection = this.collectionId + ? this.store.rootStore.documents.get(this.collectionId) + : undefined; return collection ? collectionPath(collection.url) : ""; } case NotificationEventType.MentionedInDocument: { diff --git a/app/models/ParanoidModel.ts b/app/models/ParanoidModel.ts deleted file mode 100644 index 4af7d9a69..000000000 --- a/app/models/ParanoidModel.ts +++ /dev/null @@ -1,5 +0,0 @@ -import BaseModel from "./BaseModel"; - -export default abstract class ParanoidModel extends BaseModel { - deletedAt: string | undefined; -} diff --git a/app/models/Pin.ts b/app/models/Pin.ts index a1488ada0..5543100d5 100644 --- a/app/models/Pin.ts +++ b/app/models/Pin.ts @@ -1,8 +1,8 @@ import { observable } from "mobx"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class Pin extends BaseModel { +class Pin extends Model { id: string; collectionId: string; documentId: string; diff --git a/app/models/Policy.ts b/app/models/Policy.ts index cc5cc10ae..b6517d2a1 100644 --- a/app/models/Policy.ts +++ b/app/models/Policy.ts @@ -1,6 +1,6 @@ -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; -class Policy extends BaseModel { +class Policy extends Model { id: string; abilities: Record; diff --git a/app/models/Revision.ts b/app/models/Revision.ts index c356bf17e..b449b523c 100644 --- a/app/models/Revision.ts +++ b/app/models/Revision.ts @@ -1,9 +1,10 @@ import { computed } from "mobx"; import { isRTL } from "@shared/utils/rtl"; -import BaseModel from "./BaseModel"; import User from "./User"; +import Model from "./base/Model"; +import Relation from "./decorators/Relation"; -class Revision extends BaseModel { +class Revision extends Model { id: string; documentId: string; @@ -20,8 +21,7 @@ class Revision extends BaseModel { /** HTML string representing the revision as a diff from the previous version */ html: string; - createdAt: string; - + @Relation(() => User) createdBy: User; /** diff --git a/app/models/SearchQuery.ts b/app/models/SearchQuery.ts index f26921bd0..c164c698f 100644 --- a/app/models/SearchQuery.ts +++ b/app/models/SearchQuery.ts @@ -1,13 +1,11 @@ import { client } from "~/utils/ApiClient"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; -class SearchQuery extends BaseModel { +class SearchQuery extends Model { id: string; query: string; - createdAt: string; - delete = async () => { this.isSaving = true; diff --git a/app/models/Share.ts b/app/models/Share.ts index 2533159ea..eee75412c 100644 --- a/app/models/Share.ts +++ b/app/models/Share.ts @@ -1,9 +1,9 @@ import { observable } from "mobx"; -import BaseModel from "./BaseModel"; import User from "./User"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class Share extends BaseModel { +class Share extends Model { @Field @observable id: string; diff --git a/app/models/Star.ts b/app/models/Star.ts index cd87623a9..b5c43ab80 100644 --- a/app/models/Star.ts +++ b/app/models/Star.ts @@ -1,8 +1,9 @@ import { observable } from "mobx"; -import BaseModel from "./BaseModel"; +import type StarsStore from "~/stores/StarsStore"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class Star extends BaseModel { +class Star extends Model { id: string; @Field @@ -13,8 +14,7 @@ class Star extends BaseModel { collectionId: string; - createdAt: string; - updatedAt: string; + store: StarsStore; next(): Star | undefined { const index = this.store.orderedData.indexOf(this); diff --git a/app/models/Subscription.ts b/app/models/Subscription.ts index ca0bcdd38..1321e067a 100644 --- a/app/models/Subscription.ts +++ b/app/models/Subscription.ts @@ -1,12 +1,12 @@ import { observable } from "mobx"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; /** * A subscription represents a request for a user to receive notifications for * a document. */ -class Subscription extends BaseModel { +class Subscription extends Model { @Field @observable id: string; @@ -21,9 +21,6 @@ class Subscription extends BaseModel { @Field @observable event: string; - - createdAt: string; - updatedAt: string; } export default Subscription; diff --git a/app/models/Team.ts b/app/models/Team.ts index 9130156da..4d4cc7c60 100644 --- a/app/models/Team.ts +++ b/app/models/Team.ts @@ -2,10 +2,10 @@ import { computed, observable } from "mobx"; import { TeamPreferenceDefaults } from "@shared/constants"; import { TeamPreference, TeamPreferences } from "@shared/types"; import { stringToColor } from "@shared/utils/color"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class Team extends BaseModel { +class Team extends Model { @Field @observable id: string; diff --git a/app/models/User.ts b/app/models/User.ts index 3b031d581..aa0dcdc19 100644 --- a/app/models/User.ts +++ b/app/models/User.ts @@ -12,7 +12,7 @@ import { } from "@shared/types"; import type { NotificationSettings } from "@shared/types"; import { client } from "~/utils/ApiClient"; -import ParanoidModel from "./ParanoidModel"; +import ParanoidModel from "./base/ParanoidModel"; import Field from "./decorators/Field"; class User extends ParanoidModel { @@ -96,7 +96,9 @@ class User extends ParanoidModel { get separateEditMode(): boolean { return !this.getPreference( UserPreference.SeamlessEdit, - this.store.rootStore.auth.team.getPreference(TeamPreference.SeamlessEdit) + this.store.rootStore.auth?.team?.getPreference( + TeamPreference.SeamlessEdit + ) ); } diff --git a/app/models/View.ts b/app/models/View.ts index 86beae512..4a9816892 100644 --- a/app/models/View.ts +++ b/app/models/View.ts @@ -1,8 +1,8 @@ import { action } from "mobx"; -import BaseModel from "./BaseModel"; import User from "./User"; +import Model from "./base/Model"; -class View extends BaseModel { +class View extends Model { id: string; documentId: string; diff --git a/app/models/WebhookSubscription.ts b/app/models/WebhookSubscription.ts index 093df0315..72197eee2 100644 --- a/app/models/WebhookSubscription.ts +++ b/app/models/WebhookSubscription.ts @@ -1,8 +1,8 @@ import { observable } from "mobx"; -import BaseModel from "./BaseModel"; +import Model from "./base/Model"; import Field from "./decorators/Field"; -class WebhookSubscription extends BaseModel { +class WebhookSubscription extends Model { @Field @observable id: string; diff --git a/app/models/BaseModel.ts b/app/models/base/Model.ts similarity index 84% rename from app/models/BaseModel.ts rename to app/models/base/Model.ts index 5c1c70e90..d8fba82dc 100644 --- a/app/models/BaseModel.ts +++ b/app/models/base/Model.ts @@ -1,9 +1,10 @@ import pick from "lodash/pick"; -import { set, observable } from "mobx"; +import { set, observable, action } from "mobx"; +import type Store from "~/stores/base/Store"; import Logger from "~/utils/Logger"; -import { getFieldsForModel } from "./decorators/Field"; +import { getFieldsForModel } from "../decorators/Field"; -export default abstract class BaseModel { +export default abstract class Model { @observable id: string; @@ -17,12 +18,12 @@ export default abstract class BaseModel { updatedAt: string; - store: any; + store: Store; - constructor(fields: Record, store: any) { - this.updateFromJson(fields); - this.isNew = !this.id; + constructor(fields: Record, store: Store) { this.store = store; + this.updateData(fields); + this.isNew = !this.id; } save = async ( @@ -59,10 +60,14 @@ export default abstract class BaseModel { } }; - updateFromJson = (data: any) => { - set(this, { ...data, isNew: false }); + updateData = action((data: any) => { + for (const key in data) { + this[key] = data[key]; + } + + this.isNew = false; this.persistedAttributes = this.toAPI(); - }; + }); fetch = (options?: any) => this.store.fetch(this.id, options); @@ -134,5 +139,5 @@ export default abstract class BaseModel { ); } - protected persistedAttributes: Partial = {}; + protected persistedAttributes: Partial = {}; } diff --git a/app/models/base/ParanoidModel.ts b/app/models/base/ParanoidModel.ts new file mode 100644 index 000000000..b7211e0f1 --- /dev/null +++ b/app/models/base/ParanoidModel.ts @@ -0,0 +1,5 @@ +import Model from "./Model"; + +export default abstract class ParanoidModel extends Model { + deletedAt: string | undefined; +} diff --git a/app/models/decorators/Field.ts b/app/models/decorators/Field.ts index 5dabf8b87..f62e57c0a 100644 --- a/app/models/decorators/Field.ts +++ b/app/models/decorators/Field.ts @@ -1,6 +1,8 @@ +import type Model from "../base/Model"; + const fields = new Map(); -export const getFieldsForModel = (target: any) => +export const getFieldsForModel = (target: Model) => fields.get(target.constructor.name); /** diff --git a/app/models/decorators/Relation.ts b/app/models/decorators/Relation.ts new file mode 100644 index 000000000..adad623e1 --- /dev/null +++ b/app/models/decorators/Relation.ts @@ -0,0 +1,59 @@ +import invariant from "invariant"; +import type Model from "../base/Model"; + +type RelationOptions = { + /** Whether this relation is required */ + required?: boolean; +}; + +/** + * A decorator that records this key as a relation field on the model. + * Properties decorated with @Relation will merge and read their data from + * the associated store. + * + * @param classResolver A function that returns the class of the relation + * @param options Optional options for the relation definition + */ +export default function Relation( + classResolver: () => T, + options?: RelationOptions +) { + return function (target: any, propertyKey: string) { + const idKey = `${String(propertyKey)}Id`; + const relationClass = classResolver(); + const relationClassName = relationClass.name; + + Object.defineProperty(target, propertyKey, { + get() { + const id: string | undefined = this[idKey]; + + if (!id) { + return undefined; + } + + const store = + this.store.rootStore[`${relationClassName.toLowerCase()}s`]; + invariant(store, `Store for ${relationClassName} not found`); + + return store.get(id); + }, + set(newValue: Model | Partial | undefined) { + this[idKey] = newValue ? newValue.id : undefined; + + if (newValue) { + const store = + this.store.rootStore[`${relationClassName.toLowerCase()}s`]; + invariant(store, `Store for ${relationClassName} not found`); + + store.add(newValue); + } else if (options?.required) { + throw new Error( + `Cannot set required ${String(propertyKey)} to undefined` + ); + } + }, + enumerable: true, + configurable: true, + }); + }; +} diff --git a/app/scenes/Collection/MembershipPreview.tsx b/app/scenes/Collection/MembershipPreview.tsx index 881487df8..15c03d1ea 100644 --- a/app/scenes/Collection/MembershipPreview.tsx +++ b/app/scenes/Collection/MembershipPreview.tsx @@ -3,7 +3,7 @@ import { observer } from "mobx-react"; import * as React from "react"; import { useTranslation } from "react-i18next"; import styled from "styled-components"; -import { PAGINATION_SYMBOL } from "~/stores/BaseStore"; +import { PAGINATION_SYMBOL } from "~/stores/base/Store"; import Collection from "~/models/Collection"; import User from "~/models/User"; import Avatar from "~/components/Avatar"; diff --git a/app/scenes/GroupNew.tsx b/app/scenes/GroupNew.tsx index ff84b9ab3..de9b044a3 100644 --- a/app/scenes/GroupNew.tsx +++ b/app/scenes/GroupNew.tsx @@ -21,7 +21,7 @@ function GroupNew({ onSubmit }: Props) { const { showToast } = useToasts(); const [name, setName] = React.useState(); const [isSaving, setIsSaving] = React.useState(false); - const [group, setGroup] = React.useState(); + const [group, setGroup] = React.useState(); const handleSubmit = async (ev: React.SyntheticEvent) => { ev.preventDefault(); @@ -35,7 +35,8 @@ function GroupNew({ onSubmit }: Props) { ); try { - setGroup(await group.save()); + await group.save(); + setGroup(group); } catch (err) { showToast(err.message, { type: "error", diff --git a/app/scenes/Search/Search.tsx b/app/scenes/Search/Search.tsx index 9b76f7b27..f4f6ce267 100644 --- a/app/scenes/Search/Search.tsx +++ b/app/scenes/Search/Search.tsx @@ -10,9 +10,9 @@ import styled from "styled-components"; import breakpoint from "styled-components-breakpoint"; import { v4 as uuidv4 } from "uuid"; import { DateFilter as TDateFilter } from "@shared/types"; -import { DEFAULT_PAGINATION_LIMIT } from "~/stores/BaseStore"; import { SearchParams } from "~/stores/DocumentsStore"; import RootStore from "~/stores/RootStore"; +import { DEFAULT_PAGINATION_LIMIT } from "~/stores/base/Store"; import ArrowKeyNavigation from "~/components/ArrowKeyNavigation"; import DocumentListItem from "~/components/DocumentListItem"; import Empty from "~/components/Empty"; diff --git a/app/scenes/Settings/Members.tsx b/app/scenes/Settings/Members.tsx index 16a343aa7..414839152 100644 --- a/app/scenes/Settings/Members.tsx +++ b/app/scenes/Settings/Members.tsx @@ -5,7 +5,7 @@ import * as React from "react"; import { Trans, useTranslation } from "react-i18next"; import { useHistory, useLocation } from "react-router-dom"; import styled from "styled-components"; -import { PAGINATION_SYMBOL } from "~/stores/BaseStore"; +import { PAGINATION_SYMBOL } from "~/stores/base/Store"; import User from "~/models/User"; import Invite from "~/scenes/Invite"; import { Action } from "~/components/Actions"; diff --git a/app/scenes/Settings/Shares.tsx b/app/scenes/Settings/Shares.tsx index 1622fcef0..0a69eb636 100644 --- a/app/scenes/Settings/Shares.tsx +++ b/app/scenes/Settings/Shares.tsx @@ -4,7 +4,7 @@ import { LinkIcon, WarningIcon } from "outline-icons"; import * as React from "react"; import { useTranslation, Trans } from "react-i18next"; import { Link } from "react-router-dom"; -import { PAGINATION_SYMBOL } from "~/stores/BaseStore"; +import { PAGINATION_SYMBOL } from "~/stores/base/Store"; import Share from "~/models/Share"; import Heading from "~/components/Heading"; import Notice from "~/components/Notice"; diff --git a/app/stores/ApiKeysStore.ts b/app/stores/ApiKeysStore.ts index b879793c0..f479ca299 100644 --- a/app/stores/ApiKeysStore.ts +++ b/app/stores/ApiKeysStore.ts @@ -1,8 +1,8 @@ import ApiKey from "~/models/ApiKey"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class ApiKeysStore extends BaseStore { +export default class ApiKeysStore extends Store { actions = [RPCAction.List, RPCAction.Create, RPCAction.Delete]; constructor(rootStore: RootStore) { diff --git a/app/stores/AuthStore.ts b/app/stores/AuthStore.ts index c681f4ae4..eb767e2c9 100644 --- a/app/stores/AuthStore.ts +++ b/app/stores/AuthStore.ts @@ -138,8 +138,8 @@ export default class AuthStore { @action rehydrate(data: PersistedData) { - this.user = data.user ? new User(data.user, this) : undefined; - this.team = data.team ? new Team(data.team, this) : undefined; + this.user = data.user ? new User(data.user, this as any) : undefined; + this.team = data.team ? new Team(data.team, this as any) : undefined; this.collaborationToken = data.collaborationToken; this.lastSignedIn = getCookie("lastSignedIn"); this.addPolicies(data.policies); @@ -188,8 +188,8 @@ export default class AuthStore { runInAction("AuthStore#fetch", () => { this.addPolicies(res.policies); const { user, team } = res.data; - this.user = new User(user, this); - this.team = new Team(team, this); + this.user = new User(user, this as any); + this.team = new Team(team, this as any); this.availableTeams = res.data.availableTeams; this.collaborationToken = res.data.collaborationToken; @@ -283,13 +283,13 @@ export default class AuthStore { const previousData = this.user?.toAPI(); try { - this.user?.updateFromJson(params); + this.user?.updateData(params); const res = await client.post(`/users.update`, params); invariant(res?.data, "User response not available"); - this.user?.updateFromJson(res.data); + this.user?.updateData(res.data); this.addPolicies(res.policies); } catch (err) { - this.user?.updateFromJson(previousData); + this.user?.updateData(previousData); throw err; } finally { this.isSaving = false; @@ -310,13 +310,13 @@ export default class AuthStore { const previousData = this.team?.toAPI(); try { - this.team?.updateFromJson(params); + this.team?.updateData(params); const res = await client.post(`/team.update`, params); invariant(res?.data, "Team response not available"); - this.team?.updateFromJson(res.data); + this.team?.updateData(res.data); this.addPolicies(res.policies); } catch (err) { - this.team?.updateFromJson(previousData); + this.team?.updateData(previousData); throw err; } finally { this.isSaving = false; diff --git a/app/stores/AuthenticationProvidersStore.ts b/app/stores/AuthenticationProvidersStore.ts index af5b1fdde..70bbdefcf 100644 --- a/app/stores/AuthenticationProvidersStore.ts +++ b/app/stores/AuthenticationProvidersStore.ts @@ -1,8 +1,8 @@ import AuthenticationProvider from "~/models/AuthenticationProvider"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class AuthenticationProvidersStore extends BaseStore { +export default class AuthenticationProvidersStore extends Store { actions = [RPCAction.List, RPCAction.Update]; constructor(rootStore: RootStore) { diff --git a/app/stores/CollectionGroupMembershipsStore.ts b/app/stores/CollectionGroupMembershipsStore.ts index 2dabf6ccd..bf1fcc6ad 100644 --- a/app/stores/CollectionGroupMembershipsStore.ts +++ b/app/stores/CollectionGroupMembershipsStore.ts @@ -4,10 +4,10 @@ import { CollectionPermission } from "@shared/types"; import CollectionGroupMembership from "~/models/CollectionGroupMembership"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore, { PAGINATION_SYMBOL, RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { PAGINATION_SYMBOL, RPCAction } from "./base/Store"; -export default class CollectionGroupMembershipsStore extends BaseStore { +export default class CollectionGroupMembershipsStore extends Store { actions = [RPCAction.Create, RPCAction.Delete]; constructor(rootStore: RootStore) { diff --git a/app/stores/CollectionsStore.ts b/app/stores/CollectionsStore.ts index 908d633f2..009c55174 100644 --- a/app/stores/CollectionsStore.ts +++ b/app/stores/CollectionsStore.ts @@ -12,8 +12,8 @@ import { import Collection from "~/models/Collection"; import { client } from "~/utils/ApiClient"; import { AuthorizationError, NotFoundError } from "~/utils/errors"; -import BaseStore from "./BaseStore"; import RootStore from "./RootStore"; +import Store from "./base/Store"; enum DocumentPathItemType { Collection = "collection", @@ -32,7 +32,7 @@ export type DocumentPath = DocumentPathItem & { path: DocumentPathItem[]; }; -export default class CollectionsStore extends BaseStore { +export default class CollectionsStore extends Store { constructor(rootStore: RootStore) { super(rootStore, Collection); } diff --git a/app/stores/CommentsStore.ts b/app/stores/CommentsStore.ts index fd88d508b..9f1603375 100644 --- a/app/stores/CommentsStore.ts +++ b/app/stores/CommentsStore.ts @@ -6,10 +6,10 @@ import Comment from "~/models/Comment"; import Document from "~/models/Document"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore from "./BaseStore"; import RootStore from "./RootStore"; +import Store from "./base/Store"; -export default class CommentsStore extends BaseStore { +export default class CommentsStore extends Store { apiEndpoint = "comments"; constructor(rootStore: RootStore) { diff --git a/app/stores/DocumentsStore.ts b/app/stores/DocumentsStore.ts index 424692e6d..2eb51f8bb 100644 --- a/app/stores/DocumentsStore.ts +++ b/app/stores/DocumentsStore.ts @@ -10,8 +10,8 @@ import { subtractDate } from "@shared/utils/date"; import { bytesToHumanReadable } from "@shared/utils/files"; import naturalSort from "@shared/utils/naturalSort"; import { DocumentValidation } from "@shared/validations"; -import BaseStore from "~/stores/BaseStore"; import RootStore from "~/stores/RootStore"; +import Store from "~/stores/base/Store"; import Document from "~/models/Document"; import env from "~/env"; import { FetchOptions, PaginationParams, SearchResult } from "~/types"; @@ -38,7 +38,7 @@ type ImportOptions = { publish?: boolean; }; -export default class DocumentsStore extends BaseStore { +export default class DocumentsStore extends Store { sharedCache: Map< string, { sharedTree: NavigationNode; team: PublicTeam } | undefined @@ -574,7 +574,7 @@ export default class DocumentsStore extends BaseStore { invariant(res?.data, "Data should be available"); const collection = this.getCollectionForDocument(document); if (collection) { - collection.refresh(); + await collection.refresh(); } this.addPolicies(res.policies); return this.add(res.data); @@ -686,7 +686,7 @@ export default class DocumentsStore extends BaseStore { this.addPolicies(res.policies); const document = this.add(res.data.document); const collection = this.getCollectionForDocument(document); - collection?.updateFromJson(res.data.collection); + collection?.updateData(res.data.collection); return document; } finally { this.isSaving = false; @@ -711,7 +711,7 @@ export default class DocumentsStore extends BaseStore { const collection = this.getCollectionForDocument(document); if (collection) { - collection.refresh(); + await collection.refresh(); } } @@ -722,12 +722,12 @@ export default class DocumentsStore extends BaseStore { }); runInAction("Document#archive", () => { invariant(res?.data, "Data should be available"); - document.updateFromJson(res.data); + document.updateData(res.data); this.addPolicies(res.policies); }); const collection = this.getCollectionForDocument(document); if (collection) { - collection.refresh(); + await collection.refresh(); } }; @@ -746,12 +746,12 @@ export default class DocumentsStore extends BaseStore { }); runInAction("Document#restore", () => { invariant(res?.data, "Data should be available"); - document.updateFromJson(res.data); + document.updateData(res.data); this.addPolicies(res.policies); }); const collection = this.getCollectionForDocument(document); if (collection) { - collection.refresh(); + await collection.refresh(); } }; @@ -764,9 +764,9 @@ export default class DocumentsStore extends BaseStore { runInAction("Document#unpublish", () => { invariant(res?.data, "Data should be available"); - document.updateFromJson(res.data.document); + document.updateData(res.data.document); const collection = this.getCollectionForDocument(document); - collection?.updateFromJson(res.data.collection); + collection?.updateData(res.data.collection); this.addPolicies(res.policies); }); }; diff --git a/app/stores/EventsStore.ts b/app/stores/EventsStore.ts index 5c9f1c356..e3109d736 100644 --- a/app/stores/EventsStore.ts +++ b/app/stores/EventsStore.ts @@ -2,10 +2,10 @@ import filter from "lodash/filter"; import sortBy from "lodash/sortBy"; import { computed } from "mobx"; import Event from "~/models/Event"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class EventsStore extends BaseStore { +export default class EventsStore extends Store { actions = [RPCAction.List]; constructor(rootStore: RootStore) { diff --git a/app/stores/FileOperationsStore.ts b/app/stores/FileOperationsStore.ts index d4ec7feb3..62a29504b 100644 --- a/app/stores/FileOperationsStore.ts +++ b/app/stores/FileOperationsStore.ts @@ -2,10 +2,10 @@ import orderBy from "lodash/orderBy"; import { computed } from "mobx"; import { FileOperationType } from "@shared/types"; import FileOperation from "~/models/FileOperation"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class FileOperationsStore extends BaseStore { +export default class FileOperationsStore extends Store { actions = [RPCAction.List, RPCAction.Info, RPCAction.Delete]; constructor(rootStore: RootStore) { diff --git a/app/stores/GroupMembershipsStore.ts b/app/stores/GroupMembershipsStore.ts index 992e3163a..26adf26f0 100644 --- a/app/stores/GroupMembershipsStore.ts +++ b/app/stores/GroupMembershipsStore.ts @@ -4,10 +4,10 @@ import { action, runInAction } from "mobx"; import GroupMembership from "~/models/GroupMembership"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class GroupMembershipsStore extends BaseStore { +export default class GroupMembershipsStore extends Store { actions = [RPCAction.Create, RPCAction.Delete]; constructor(rootStore: RootStore) { diff --git a/app/stores/GroupsStore.ts b/app/stores/GroupsStore.ts index 8dc9e7b28..113ac7531 100644 --- a/app/stores/GroupsStore.ts +++ b/app/stores/GroupsStore.ts @@ -5,12 +5,12 @@ import naturalSort from "@shared/utils/naturalSort"; import Group from "~/models/Group"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore from "./BaseStore"; import RootStore from "./RootStore"; +import Store from "./base/Store"; type FetchPageParams = PaginationParams & { query?: string }; -export default class GroupsStore extends BaseStore { +export default class GroupsStore extends Store { constructor(rootStore: RootStore) { super(rootStore, Group); } diff --git a/app/stores/IntegrationsStore.ts b/app/stores/IntegrationsStore.ts index 2460bcda5..e1218192a 100644 --- a/app/stores/IntegrationsStore.ts +++ b/app/stores/IntegrationsStore.ts @@ -2,11 +2,11 @@ import filter from "lodash/filter"; import { computed } from "mobx"; import { IntegrationService } from "@shared/types"; import naturalSort from "@shared/utils/naturalSort"; -import BaseStore from "~/stores/BaseStore"; import RootStore from "~/stores/RootStore"; +import Store from "~/stores/base/Store"; import Integration from "~/models/Integration"; -class IntegrationsStore extends BaseStore { +class IntegrationsStore extends Store { constructor(rootStore: RootStore) { super(rootStore, Integration); } diff --git a/app/stores/MembershipsStore.ts b/app/stores/MembershipsStore.ts index 679dff41e..6e361fb68 100644 --- a/app/stores/MembershipsStore.ts +++ b/app/stores/MembershipsStore.ts @@ -4,10 +4,10 @@ import { CollectionPermission } from "@shared/types"; import Membership from "~/models/Membership"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore, { PAGINATION_SYMBOL, RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { PAGINATION_SYMBOL, RPCAction } from "./base/Store"; -export default class MembershipsStore extends BaseStore { +export default class MembershipsStore extends Store { actions = [RPCAction.Create, RPCAction.Delete]; constructor(rootStore: RootStore) { diff --git a/app/stores/NotificationsStore.ts b/app/stores/NotificationsStore.ts index 7470f0df0..f20abf068 100644 --- a/app/stores/NotificationsStore.ts +++ b/app/stores/NotificationsStore.ts @@ -5,10 +5,10 @@ import { action, computed, runInAction } from "mobx"; import Notification from "~/models/Notification"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class NotificationsStore extends BaseStore { +export default class NotificationsStore extends Store { actions = [RPCAction.List, RPCAction.Update]; constructor(rootStore: RootStore) { diff --git a/app/stores/PinsStore.ts b/app/stores/PinsStore.ts index 956e705ba..b82a3d592 100644 --- a/app/stores/PinsStore.ts +++ b/app/stores/PinsStore.ts @@ -3,12 +3,12 @@ import { action, runInAction, computed } from "mobx"; import Pin from "~/models/Pin"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore from "./BaseStore"; import RootStore from "./RootStore"; +import Store from "./base/Store"; type FetchParams = PaginationParams & { collectionId?: string }; -export default class PinsStore extends BaseStore { +export default class PinsStore extends Store { constructor(rootStore: RootStore) { super(rootStore, Pin); } diff --git a/app/stores/PoliciesStore.ts b/app/stores/PoliciesStore.ts index 3534b1d9f..a0e112e4c 100644 --- a/app/stores/PoliciesStore.ts +++ b/app/stores/PoliciesStore.ts @@ -1,8 +1,8 @@ import Policy from "~/models/Policy"; -import BaseStore from "./BaseStore"; import RootStore from "./RootStore"; +import Store from "./base/Store"; -export default class PoliciesStore extends BaseStore { +export default class PoliciesStore extends Store { actions = []; constructor(rootStore: RootStore) { diff --git a/app/stores/RevisionsStore.ts b/app/stores/RevisionsStore.ts index dcac76b66..b859bbe04 100644 --- a/app/stores/RevisionsStore.ts +++ b/app/stores/RevisionsStore.ts @@ -1,13 +1,13 @@ import invariant from "invariant"; import filter from "lodash/filter"; import { action, runInAction } from "mobx"; -import BaseStore, { RPCAction } from "~/stores/BaseStore"; import RootStore from "~/stores/RootStore"; +import Store, { RPCAction } from "~/stores/base/Store"; import Revision from "~/models/Revision"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -export default class RevisionsStore extends BaseStore { +export default class RevisionsStore extends Store { actions = [RPCAction.List, RPCAction.Info]; constructor(rootStore: RootStore) { diff --git a/app/stores/SearchesStore.ts b/app/stores/SearchesStore.ts index 5498db2a3..6cc6c89f3 100644 --- a/app/stores/SearchesStore.ts +++ b/app/stores/SearchesStore.ts @@ -1,10 +1,10 @@ import uniqBy from "lodash/uniqBy"; import { computed } from "mobx"; import SearchQuery from "~/models/SearchQuery"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class SearchesStore extends BaseStore { +export default class SearchesStore extends Store { actions = [RPCAction.List, RPCAction.Delete]; apiEndpoint = "searches"; diff --git a/app/stores/SharesStore.ts b/app/stores/SharesStore.ts index 23ee81b8c..da63a95f2 100644 --- a/app/stores/SharesStore.ts +++ b/app/stores/SharesStore.ts @@ -6,10 +6,10 @@ import sortBy from "lodash/sortBy"; import { action, computed } from "mobx"; import Share from "~/models/Share"; import { client } from "~/utils/ApiClient"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class SharesStore extends BaseStore { +export default class SharesStore extends Store { actions = [ RPCAction.Info, RPCAction.List, diff --git a/app/stores/StarsStore.ts b/app/stores/StarsStore.ts index fa01d7730..6c9cfaa65 100644 --- a/app/stores/StarsStore.ts +++ b/app/stores/StarsStore.ts @@ -3,10 +3,10 @@ import { action, runInAction, computed } from "mobx"; import Star from "~/models/Star"; import { PaginationParams } from "~/types"; import { client } from "~/utils/ApiClient"; -import BaseStore from "./BaseStore"; import RootStore from "./RootStore"; +import Store from "./base/Store"; -export default class StarsStore extends BaseStore { +export default class StarsStore extends Store { constructor(rootStore: RootStore) { super(rootStore, Star); } diff --git a/app/stores/SubscriptionsStore.ts b/app/stores/SubscriptionsStore.ts index e2a2b4c04..fd9a70077 100644 --- a/app/stores/SubscriptionsStore.ts +++ b/app/stores/SubscriptionsStore.ts @@ -1,8 +1,8 @@ import Subscription from "~/models/Subscription"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class SubscriptionsStore extends BaseStore { +export default class SubscriptionsStore extends Store { actions = [RPCAction.List, RPCAction.Create, RPCAction.Delete]; constructor(rootStore: RootStore) { diff --git a/app/stores/UsersStore.ts b/app/stores/UsersStore.ts index 8749e4d4f..62887b37e 100644 --- a/app/stores/UsersStore.ts +++ b/app/stores/UsersStore.ts @@ -5,10 +5,10 @@ import { observable, computed, action, runInAction } from "mobx"; import { UserRole } from "@shared/types"; import User from "~/models/User"; import { client } from "~/utils/ApiClient"; -import BaseStore from "./BaseStore"; import RootStore from "./RootStore"; +import Store from "./base/Store"; -export default class UsersStore extends BaseStore { +export default class UsersStore extends Store { @observable counts: { active: number; diff --git a/app/stores/ViewsStore.ts b/app/stores/ViewsStore.ts index 7c098db19..a11b84f41 100644 --- a/app/stores/ViewsStore.ts +++ b/app/stores/ViewsStore.ts @@ -3,10 +3,10 @@ import find from "lodash/find"; import orderBy from "lodash/orderBy"; import reduce from "lodash/reduce"; import View from "~/models/View"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class ViewsStore extends BaseStore { +export default class ViewsStore extends Store { actions = [RPCAction.List, RPCAction.Create]; constructor(rootStore: RootStore) { diff --git a/app/stores/WebhookSubscriptionStore.ts b/app/stores/WebhookSubscriptionStore.ts index dc318ca95..c7ed34492 100644 --- a/app/stores/WebhookSubscriptionStore.ts +++ b/app/stores/WebhookSubscriptionStore.ts @@ -1,9 +1,9 @@ import { computed } from "mobx"; import WebhookSubscription from "~/models/WebhookSubscription"; -import BaseStore, { RPCAction } from "./BaseStore"; import RootStore from "./RootStore"; +import Store, { RPCAction } from "./base/Store"; -export default class WebhookSubscriptionsStore extends BaseStore { +export default class WebhookSubscriptionsStore extends Store { actions = [ RPCAction.List, RPCAction.Create, diff --git a/app/stores/BaseStore.ts b/app/stores/base/Store.ts similarity index 97% rename from app/stores/BaseStore.ts rename to app/stores/base/Store.ts index 96c96f5a2..dcc305b53 100644 --- a/app/stores/BaseStore.ts +++ b/app/stores/base/Store.ts @@ -4,8 +4,8 @@ import orderBy from "lodash/orderBy"; import { observable, action, computed, runInAction } from "mobx"; import { Class } from "utility-types"; import RootStore from "~/stores/RootStore"; -import BaseModel from "~/models/BaseModel"; import Policy from "~/models/Policy"; +import Model from "~/models/base/Model"; import { PaginationParams, PartialWithId } from "~/types"; import { client } from "~/utils/ApiClient"; import { AuthorizationError, NotFoundError } from "~/utils/errors"; @@ -25,7 +25,7 @@ export const DEFAULT_PAGINATION_LIMIT = 25; export const PAGINATION_SYMBOL = Symbol.for("pagination"); -export default abstract class BaseStore { +export default abstract class Store { @observable data: Map = new Map(); @@ -84,7 +84,7 @@ export default abstract class BaseStore { const existingModel = this.data.get(item.id); if (existingModel) { - existingModel.updateFromJson(item); + existingModel.updateData(item); return existingModel; } diff --git a/app/stores/index.ts b/app/stores/index.ts index e692e7451..d0189176a 100644 --- a/app/stores/index.ts +++ b/app/stores/index.ts @@ -1,5 +1,11 @@ import RootStore from "~/stores/RootStore"; +import env from "~/env"; const stores = new RootStore(); +// Expose stores on window in development for easier debugging +if (env.ENVIRONMENT === "development") { + window.stores = stores; +} + export default stores; diff --git a/app/typings/window.d.ts b/app/typings/window.d.ts index 0a5a11a91..2517af21e 100644 --- a/app/typings/window.d.ts +++ b/app/typings/window.d.ts @@ -1,3 +1,5 @@ +import type RootStore from "~/stores/RootStore"; + declare global { interface ImportMeta { /** @@ -10,6 +12,8 @@ declare global { dataLayer: any[]; gtag: (...args: any[]) => void; + stores: RootStore; + DesktopBridge: { /** * The name of the platform running on.