fix: Initials do not display on notification avatars (#5803)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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[];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import BaseModel from "./BaseModel";
|
||||
|
||||
export default abstract class ParanoidModel extends BaseModel {
|
||||
deletedAt: string | undefined;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> = {};
|
||||
}
|
||||
5
app/models/base/ParanoidModel.ts
Normal file
5
app/models/base/ParanoidModel.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import Model from "./Model";
|
||||
|
||||
export default abstract class ParanoidModel extends Model {
|
||||
deletedAt: string | undefined;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
59
app/models/decorators/Relation.ts
Normal file
59
app/models/decorators/Relation.ts
Normal 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,
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user