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

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