fix: Initials do not display on notification avatars (#5803)

This commit is contained in:
Tom Moor
2023-09-09 21:01:14 -04:00
committed by GitHub
parent 80ef0a38d6
commit 5c839998c1
65 changed files with 238 additions and 165 deletions

View File

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

View File

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

View File

@ -317,7 +317,7 @@ class WebsocketProvider extends React.Component<Props> {
});
}
auth.team?.updateFromJson(event);
auth.team?.updateData(event);
});
this.socket.on(

View File

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

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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[];
}
}

View File

@ -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 = {

View File

@ -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: {

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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<T = unknown> extends BaseModel {
class Integration<T = unknown> extends Model {
id: string;
type: IntegrationType;

View File

@ -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;

View File

@ -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: {

View File

@ -1,5 +0,0 @@
import BaseModel from "./BaseModel";
export default abstract class ParanoidModel extends BaseModel {
deletedAt: string | undefined;
}

View File

@ -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;

View File

@ -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<string, boolean>;

View File

@ -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;
/**

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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
)
);
}

View File

@ -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;

View File

@ -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;

View File

@ -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<Model>;
constructor(fields: Record<string, any>, store: any) {
this.updateFromJson(fields);
this.isNew = !this.id;
constructor(fields: Record<string, any>, store: Store<Model>) {
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<BaseModel> = {};
protected persistedAttributes: Partial<Model> = {};
}

View File

@ -0,0 +1,5 @@
import Model from "./Model";
export default abstract class ParanoidModel extends Model {
deletedAt: string | undefined;
}

View File

@ -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);
/**

View File

@ -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<T extends typeof Model>(
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<Model> | 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,
});
};
}

View File

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

View File

@ -21,7 +21,7 @@ function GroupNew({ onSubmit }: Props) {
const { showToast } = useToasts();
const [name, setName] = React.useState<string | undefined>();
const [isSaving, setIsSaving] = React.useState(false);
const [group, setGroup] = React.useState();
const [group, setGroup] = React.useState<Group | undefined>();
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",

View File

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

View File

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

View File

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

View File

@ -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<ApiKey> {
export default class ApiKeysStore extends Store<ApiKey> {
actions = [RPCAction.List, RPCAction.Create, RPCAction.Delete];
constructor(rootStore: RootStore) {

View File

@ -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;

View File

@ -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<AuthenticationProvider> {
export default class AuthenticationProvidersStore extends Store<AuthenticationProvider> {
actions = [RPCAction.List, RPCAction.Update];
constructor(rootStore: RootStore) {

View File

@ -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<CollectionGroupMembership> {
export default class CollectionGroupMembershipsStore extends Store<CollectionGroupMembership> {
actions = [RPCAction.Create, RPCAction.Delete];
constructor(rootStore: RootStore) {

View File

@ -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<Collection> {
export default class CollectionsStore extends Store<Collection> {
constructor(rootStore: RootStore) {
super(rootStore, Collection);
}

View File

@ -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<Comment> {
export default class CommentsStore extends Store<Comment> {
apiEndpoint = "comments";
constructor(rootStore: RootStore) {

View File

@ -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<Document> {
export default class DocumentsStore extends Store<Document> {
sharedCache: Map<
string,
{ sharedTree: NavigationNode; team: PublicTeam } | undefined
@ -574,7 +574,7 @@ export default class DocumentsStore extends BaseStore<Document> {
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<Document> {
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<Document> {
const collection = this.getCollectionForDocument(document);
if (collection) {
collection.refresh();
await collection.refresh();
}
}
@ -722,12 +722,12 @@ export default class DocumentsStore extends BaseStore<Document> {
});
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<Document> {
});
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<Document> {
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);
});
};

View File

@ -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<Event> {
export default class EventsStore extends Store<Event> {
actions = [RPCAction.List];
constructor(rootStore: RootStore) {

View File

@ -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<FileOperation> {
export default class FileOperationsStore extends Store<FileOperation> {
actions = [RPCAction.List, RPCAction.Info, RPCAction.Delete];
constructor(rootStore: RootStore) {

View File

@ -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<GroupMembership> {
export default class GroupMembershipsStore extends Store<GroupMembership> {
actions = [RPCAction.Create, RPCAction.Delete];
constructor(rootStore: RootStore) {

View File

@ -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<Group> {
export default class GroupsStore extends Store<Group> {
constructor(rootStore: RootStore) {
super(rootStore, Group);
}

View File

@ -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<Integration> {
class IntegrationsStore extends Store<Integration> {
constructor(rootStore: RootStore) {
super(rootStore, Integration);
}

View File

@ -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<Membership> {
export default class MembershipsStore extends Store<Membership> {
actions = [RPCAction.Create, RPCAction.Delete];
constructor(rootStore: RootStore) {

View File

@ -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<Notification> {
export default class NotificationsStore extends Store<Notification> {
actions = [RPCAction.List, RPCAction.Update];
constructor(rootStore: RootStore) {

View File

@ -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<Pin> {
export default class PinsStore extends Store<Pin> {
constructor(rootStore: RootStore) {
super(rootStore, Pin);
}

View File

@ -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<Policy> {
export default class PoliciesStore extends Store<Policy> {
actions = [];
constructor(rootStore: RootStore) {

View File

@ -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<Revision> {
export default class RevisionsStore extends Store<Revision> {
actions = [RPCAction.List, RPCAction.Info];
constructor(rootStore: RootStore) {

View File

@ -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<SearchQuery> {
export default class SearchesStore extends Store<SearchQuery> {
actions = [RPCAction.List, RPCAction.Delete];
apiEndpoint = "searches";

View File

@ -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<Share> {
export default class SharesStore extends Store<Share> {
actions = [
RPCAction.Info,
RPCAction.List,

View File

@ -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<Star> {
export default class StarsStore extends Store<Star> {
constructor(rootStore: RootStore) {
super(rootStore, Star);
}

View File

@ -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<Subscription> {
export default class SubscriptionsStore extends Store<Subscription> {
actions = [RPCAction.List, RPCAction.Create, RPCAction.Delete];
constructor(rootStore: RootStore) {

View File

@ -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<User> {
export default class UsersStore extends Store<User> {
@observable
counts: {
active: number;

View File

@ -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<View> {
export default class ViewsStore extends Store<View> {
actions = [RPCAction.List, RPCAction.Create];
constructor(rootStore: RootStore) {

View File

@ -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<WebhookSubscription> {
export default class WebhookSubscriptionsStore extends Store<WebhookSubscription> {
actions = [
RPCAction.List,
RPCAction.Create,

View File

@ -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<T extends BaseModel> {
export default abstract class Store<T extends Model> {
@observable
data: Map<string, T> = new Map();
@ -84,7 +84,7 @@ export default abstract class BaseStore<T extends BaseModel> {
const existingModel = this.data.get(item.id);
if (existingModel) {
existingModel.updateFromJson(item);
existingModel.updateData(item);
return existingModel;
}

View File

@ -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;

View File

@ -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.