mirror of
https://github.com/flarum/core.git
synced 2025-08-04 23:47:32 +02:00
Drop mixin-like attributes and convert to typescript
This commit is contained in:
2
js/shims.d.ts
vendored
2
js/shims.d.ts
vendored
@@ -4,6 +4,7 @@ import Mithril from 'mithril';
|
|||||||
// Other third-party libs
|
// Other third-party libs
|
||||||
import * as _dayjs from 'dayjs';
|
import * as _dayjs from 'dayjs';
|
||||||
import * as _$ from 'jquery';
|
import * as _$ from 'jquery';
|
||||||
|
import * as _ColorThief from 'color-thief-browser';
|
||||||
|
|
||||||
// Globals from flarum/core
|
// Globals from flarum/core
|
||||||
import Application from './src/common/Application';
|
import Application from './src/common/Application';
|
||||||
@@ -22,6 +23,7 @@ declare global {
|
|||||||
const $: typeof _$;
|
const $: typeof _$;
|
||||||
const m: Mithril.Static;
|
const m: Mithril.Static;
|
||||||
const dayjs: typeof _dayjs;
|
const dayjs: typeof _dayjs;
|
||||||
|
const ColorThief: _ColorThief;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,3 +1,17 @@
|
|||||||
|
import Store from './Store';
|
||||||
|
import Mithril from 'mithril';
|
||||||
|
|
||||||
|
interface ModelData {
|
||||||
|
type?: string;
|
||||||
|
id?: string;
|
||||||
|
attributes?: any;
|
||||||
|
relationships?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SaveOptions extends Mithril.RequestOptions<any> {
|
||||||
|
meta?: any;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `Model` class represents a local data resource. It provides methods to
|
* The `Model` class represents a local data resource. It provides methods to
|
||||||
* persist changes via the API.
|
* persist changes via the API.
|
||||||
@@ -5,19 +19,13 @@
|
|||||||
* @abstract
|
* @abstract
|
||||||
*/
|
*/
|
||||||
export default class Model {
|
export default class Model {
|
||||||
/**
|
|
||||||
* @param {Object} data A resource object from the API.
|
|
||||||
* @param {Store} store The data store that this model should be persisted to.
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
constructor(data = {}, store = null) {
|
|
||||||
/**
|
/**
|
||||||
* The resource object from the API.
|
* The resource object from the API.
|
||||||
*
|
*
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
this.data = data;
|
data: ModelData = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The time at which the model's data was last updated. Watching the value
|
* The time at which the model's data was last updated. Watching the value
|
||||||
@@ -27,7 +35,7 @@ export default class Model {
|
|||||||
* @type {Date}
|
* @type {Date}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
this.freshness = new Date();
|
freshness: Date = new Date();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the resource exists on the server.
|
* Whether or not the resource exists on the server.
|
||||||
@@ -35,7 +43,7 @@ export default class Model {
|
|||||||
* @type {Boolean}
|
* @type {Boolean}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
this.exists = false;
|
exists: boolean = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data store that this resource should be persisted to.
|
* The data store that this resource should be persisted to.
|
||||||
@@ -43,17 +51,26 @@ export default class Model {
|
|||||||
* @type {Store}
|
* @type {Store}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
|
store?: Store = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} data A resource object from the API.
|
||||||
|
* @param {Store} store The data store that this model should be persisted to.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
constructor(data: ModelData = {}, store = null) {
|
||||||
|
this.data = data;
|
||||||
this.store = store;
|
this.store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the model's ID.
|
* Get the model's ID.
|
||||||
*
|
*
|
||||||
* @return {Integer}
|
* @return {String}
|
||||||
* @public
|
* @public
|
||||||
* @final
|
* @final
|
||||||
*/
|
*/
|
||||||
id() {
|
id(): string | undefined {
|
||||||
return this.data.id;
|
return this.data.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,8 +138,8 @@ export default class Model {
|
|||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
save(attributes, options = {}) {
|
save(attributes, options: SaveOptions = {}) {
|
||||||
const data = {
|
const data: ModelData = {
|
||||||
type: this.data.type,
|
type: this.data.type,
|
||||||
id: this.data.id,
|
id: this.data.id,
|
||||||
attributes,
|
attributes,
|
||||||
@@ -152,7 +169,7 @@ export default class Model {
|
|||||||
|
|
||||||
this.pushData(data);
|
this.pushData(data);
|
||||||
|
|
||||||
const request = { data };
|
const request: any = { data };
|
||||||
if (options.meta) request.meta = options.meta;
|
if (options.meta) request.meta = options.meta;
|
||||||
|
|
||||||
return app
|
return app
|
||||||
@@ -220,11 +237,11 @@ export default class Model {
|
|||||||
* @return {String}
|
* @return {String}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
apiEndpoint() {
|
apiEndpoint(): string {
|
||||||
return '/' + this.data.type + (this.exists ? '/' + this.data.id : '');
|
return '/' + this.data.type + (this.exists ? '/' + this.data.id : '');
|
||||||
}
|
}
|
||||||
|
|
||||||
copyData() {
|
copyData(): ModelData {
|
||||||
return JSON.parse(JSON.stringify(this.data));
|
return JSON.parse(JSON.stringify(this.data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,8 +253,8 @@ export default class Model {
|
|||||||
* @return {*}
|
* @return {*}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
static attribute(name, transform) {
|
static attribute<T>(name: string, transform?: Function) {
|
||||||
return function () {
|
return function (this: Model): T | null | undefined {
|
||||||
const value = this.data.attributes && this.data.attributes[name];
|
const value = this.data.attributes && this.data.attributes[name];
|
||||||
|
|
||||||
return transform ? transform(value) : value;
|
return transform ? transform(value) : value;
|
||||||
@@ -254,8 +271,8 @@ export default class Model {
|
|||||||
* has not been loaded; or the model if it has been loaded.
|
* has not been loaded; or the model if it has been loaded.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
static hasOne(name) {
|
static hasOne<T>(name: string) {
|
||||||
return function () {
|
return function (this: Model): T | null | false {
|
||||||
if (this.data.relationships) {
|
if (this.data.relationships) {
|
||||||
const relationship = this.data.relationships[name];
|
const relationship = this.data.relationships[name];
|
||||||
|
|
||||||
@@ -278,8 +295,8 @@ export default class Model {
|
|||||||
* loaded, and undefined for those that have not.
|
* loaded, and undefined for those that have not.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
static hasMany(name) {
|
static hasMany<T>(name: string) {
|
||||||
return function () {
|
return function (this: Model): T[] | false {
|
||||||
if (this.data.relationships) {
|
if (this.data.relationships) {
|
||||||
const relationship = this.data.relationships[name];
|
const relationship = this.data.relationships[name];
|
||||||
|
|
||||||
@@ -299,7 +316,7 @@ export default class Model {
|
|||||||
* @return {Date|null}
|
* @return {Date|null}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
static transformDate(value) {
|
static transformDate(value: string): Date | null {
|
||||||
return value ? new Date(value) : null;
|
return value ? new Date(value) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +327,7 @@ export default class Model {
|
|||||||
* @return {Object}
|
* @return {Object}
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
static getIdentifier(model) {
|
static getIdentifier(model: Model) {
|
||||||
return {
|
return {
|
||||||
type: model.data.type,
|
type: model.data.type,
|
||||||
id: model.data.id,
|
id: model.data.id,
|
@@ -2,40 +2,40 @@ import Model from '../Model';
|
|||||||
import computed from '../utils/computed';
|
import computed from '../utils/computed';
|
||||||
import ItemList from '../utils/ItemList';
|
import ItemList from '../utils/ItemList';
|
||||||
import Badge from '../components/Badge';
|
import Badge from '../components/Badge';
|
||||||
|
import User from './User';
|
||||||
|
import Post from './Post';
|
||||||
|
|
||||||
export default class Discussion extends Model {}
|
export default class Discussion extends Model {
|
||||||
|
title = Model.attribute<string>('title');
|
||||||
|
slug = Model.attribute<string>('slug');
|
||||||
|
|
||||||
Object.assign(Discussion.prototype, {
|
createdAt = Model.attribute<Date>('createdAt', Model.transformDate);
|
||||||
title: Model.attribute('title'),
|
user = Model.hasOne<User>('user');
|
||||||
slug: Model.attribute('slug'),
|
firstPost = Model.hasOne<Post>('firstPost');
|
||||||
|
|
||||||
createdAt: Model.attribute('createdAt', Model.transformDate),
|
lastPostedAt = Model.attribute<Date>('lastPostedAt', Model.transformDate);
|
||||||
user: Model.hasOne('user'),
|
lastPostedUser = Model.hasOne<User>('lastPostedUser');
|
||||||
firstPost: Model.hasOne('firstPost'),
|
lastPost = Model.hasOne<Post>('lastPost');
|
||||||
|
lastPostNumber = Model.attribute<number>('lastPostNumber');
|
||||||
|
|
||||||
lastPostedAt: Model.attribute('lastPostedAt', Model.transformDate),
|
commentCount = Model.attribute<number>('commentCount');
|
||||||
lastPostedUser: Model.hasOne('lastPostedUser'),
|
replyCount = computed<number>('commentCount', (commentCount) => Math.max(0, commentCount - 1));
|
||||||
lastPost: Model.hasOne('lastPost'),
|
posts = Model.hasMany<Post>('posts');
|
||||||
lastPostNumber: Model.attribute('lastPostNumber'),
|
mostRelevantPost = Model.hasOne<Post>('mostRelevantPost');
|
||||||
|
|
||||||
commentCount: Model.attribute('commentCount'),
|
lastReadAt = Model.attribute<Date>('lastReadAt', Model.transformDate);
|
||||||
replyCount: computed('commentCount', (commentCount) => Math.max(0, commentCount - 1)),
|
lastReadPostNumber = Model.attribute<number>('lastReadPostNumber');
|
||||||
posts: Model.hasMany('posts'),
|
isUnread = computed<boolean>('unreadCount', (unreadCount) => !!unreadCount);
|
||||||
mostRelevantPost: Model.hasOne('mostRelevantPost'),
|
isRead = computed<boolean>('unreadCount', (unreadCount) => app.session.user && !unreadCount);
|
||||||
|
|
||||||
lastReadAt: Model.attribute('lastReadAt', Model.transformDate),
|
hiddenAt = Model.attribute<Date>('hiddenAt', Model.transformDate);
|
||||||
lastReadPostNumber: Model.attribute('lastReadPostNumber'),
|
hiddenUser = Model.hasOne<User>('hiddenUser');
|
||||||
isUnread: computed('unreadCount', (unreadCount) => !!unreadCount),
|
isHidden = computed<boolean>('hiddenAt', (hiddenAt) => !!hiddenAt);
|
||||||
isRead: computed('unreadCount', (unreadCount) => app.session.user && !unreadCount),
|
|
||||||
|
|
||||||
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
canReply = Model.attribute<boolean>('canReply');
|
||||||
hiddenUser: Model.hasOne('hiddenUser'),
|
canRename = Model.attribute<boolean>('canRename');
|
||||||
isHidden: computed('hiddenAt', (hiddenAt) => !!hiddenAt),
|
canHide = Model.attribute<boolean>('canHide');
|
||||||
|
canDelete = Model.attribute<boolean>('canDelete');
|
||||||
canReply: Model.attribute('canReply'),
|
|
||||||
canRename: Model.attribute('canRename'),
|
|
||||||
canHide: Model.attribute('canHide'),
|
|
||||||
canDelete: Model.attribute('canDelete'),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a post from the discussion's posts relationship.
|
* Remove a post from the discussion's posts relationship.
|
||||||
@@ -55,7 +55,7 @@ Object.assign(Discussion.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the estimated number of unread posts in this discussion for the current
|
* Get the estimated number of unread posts in this discussion for the current
|
||||||
@@ -64,7 +64,7 @@ Object.assign(Discussion.prototype, {
|
|||||||
* @return {Integer}
|
* @return {Integer}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
unreadCount() {
|
unreadCount(): number {
|
||||||
const user = app.session.user;
|
const user = app.session.user;
|
||||||
|
|
||||||
if (user && user.markedAllAsReadAt() < this.lastPostedAt()) {
|
if (user && user.markedAllAsReadAt() < this.lastPostedAt()) {
|
||||||
@@ -75,7 +75,7 @@ Object.assign(Discussion.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Badge components that apply to this discussion.
|
* Get the Badge components that apply to this discussion.
|
||||||
@@ -83,7 +83,7 @@ Object.assign(Discussion.prototype, {
|
|||||||
* @return {ItemList}
|
* @return {ItemList}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
badges() {
|
badges(): ItemList {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
|
|
||||||
if (this.isHidden()) {
|
if (this.isHidden()) {
|
||||||
@@ -91,7 +91,7 @@ Object.assign(Discussion.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of all of the post IDs in this discussion.
|
* Get a list of all of the post IDs in this discussion.
|
||||||
@@ -99,9 +99,9 @@ Object.assign(Discussion.prototype, {
|
|||||||
* @return {Array}
|
* @return {Array}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
postIds() {
|
postIds(): string[] {
|
||||||
const posts = this.data.relationships.posts;
|
const posts = this.data.relationships.posts;
|
||||||
|
|
||||||
return posts ? posts.data.map((link) => link.id) : [];
|
return posts ? posts.data.map((link) => link.id) : [];
|
||||||
},
|
}
|
||||||
});
|
}
|
@@ -1,17 +0,0 @@
|
|||||||
import Model from '../Model';
|
|
||||||
|
|
||||||
class Group extends Model {}
|
|
||||||
|
|
||||||
Object.assign(Group.prototype, {
|
|
||||||
nameSingular: Model.attribute('nameSingular'),
|
|
||||||
namePlural: Model.attribute('namePlural'),
|
|
||||||
color: Model.attribute('color'),
|
|
||||||
icon: Model.attribute('icon'),
|
|
||||||
isHidden: Model.attribute('isHidden'),
|
|
||||||
});
|
|
||||||
|
|
||||||
Group.ADMINISTRATOR_ID = '1';
|
|
||||||
Group.GUEST_ID = '2';
|
|
||||||
Group.MEMBER_ID = '3';
|
|
||||||
|
|
||||||
export default Group;
|
|
13
js/src/common/models/Group.ts
Normal file
13
js/src/common/models/Group.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import Model from '../Model';
|
||||||
|
|
||||||
|
export default class Group extends Model {
|
||||||
|
static ADMINISTRATOR_ID = '1';
|
||||||
|
static GUEST_ID = '2';
|
||||||
|
static MEMBER_ID = '3';
|
||||||
|
|
||||||
|
nameSingular = Model.attribute<string>('nameSingular');
|
||||||
|
namePlural = Model.attribute<string>('namePlural');
|
||||||
|
color = Model.attribute<string>('color');
|
||||||
|
icon = Model.attribute<string>('icon');
|
||||||
|
isHidden = Model.attribute<boolean>('isHidden');
|
||||||
|
}
|
@@ -1,15 +0,0 @@
|
|||||||
import Model from '../Model';
|
|
||||||
|
|
||||||
export default class Notification extends Model {}
|
|
||||||
|
|
||||||
Object.assign(Notification.prototype, {
|
|
||||||
contentType: Model.attribute('contentType'),
|
|
||||||
content: Model.attribute('content'),
|
|
||||||
createdAt: Model.attribute('createdAt', Model.transformDate),
|
|
||||||
|
|
||||||
isRead: Model.attribute('isRead'),
|
|
||||||
|
|
||||||
user: Model.hasOne('user'),
|
|
||||||
fromUser: Model.hasOne('fromUser'),
|
|
||||||
subject: Model.hasOne('subject'),
|
|
||||||
});
|
|
14
js/src/common/models/Notification.ts
Normal file
14
js/src/common/models/Notification.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Model from '../Model';
|
||||||
|
import User from './User';
|
||||||
|
|
||||||
|
export default class Notification extends Model {
|
||||||
|
contentType = Model.attribute<string>('contentType');
|
||||||
|
content = Model.attribute<any>('content');
|
||||||
|
createdAt = Model.attribute<Date>('createdAt', Model.transformDate);
|
||||||
|
|
||||||
|
isRead = Model.attribute<boolean>('isRead');
|
||||||
|
|
||||||
|
user = Model.hasOne<User>('user');
|
||||||
|
fromUser = Model.hasOne<User>('fromUser');
|
||||||
|
subject = Model.hasOne<any>('subject');
|
||||||
|
}
|
@@ -1,29 +0,0 @@
|
|||||||
import Model from '../Model';
|
|
||||||
import computed from '../utils/computed';
|
|
||||||
import { getPlainContent } from '../utils/string';
|
|
||||||
|
|
||||||
export default class Post extends Model {}
|
|
||||||
|
|
||||||
Object.assign(Post.prototype, {
|
|
||||||
number: Model.attribute('number'),
|
|
||||||
discussion: Model.hasOne('discussion'),
|
|
||||||
|
|
||||||
createdAt: Model.attribute('createdAt', Model.transformDate),
|
|
||||||
user: Model.hasOne('user'),
|
|
||||||
contentType: Model.attribute('contentType'),
|
|
||||||
content: Model.attribute('content'),
|
|
||||||
contentHtml: Model.attribute('contentHtml'),
|
|
||||||
contentPlain: computed('contentHtml', getPlainContent),
|
|
||||||
|
|
||||||
editedAt: Model.attribute('editedAt', Model.transformDate),
|
|
||||||
editedUser: Model.hasOne('editedUser'),
|
|
||||||
isEdited: computed('editedAt', (editedAt) => !!editedAt),
|
|
||||||
|
|
||||||
hiddenAt: Model.attribute('hiddenAt', Model.transformDate),
|
|
||||||
hiddenUser: Model.hasOne('hiddenUser'),
|
|
||||||
isHidden: computed('hiddenAt', (hiddenAt) => !!hiddenAt),
|
|
||||||
|
|
||||||
canEdit: Model.attribute('canEdit'),
|
|
||||||
canHide: Model.attribute('canHide'),
|
|
||||||
canDelete: Model.attribute('canDelete'),
|
|
||||||
});
|
|
29
js/src/common/models/Post.ts
Normal file
29
js/src/common/models/Post.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import Model from '../Model';
|
||||||
|
import computed from '../utils/computed';
|
||||||
|
import { getPlainContent } from '../utils/string';
|
||||||
|
import Discussion from './Discussion';
|
||||||
|
import User from './User';
|
||||||
|
|
||||||
|
export default class Post extends Model {
|
||||||
|
number = Model.attribute<number>('number');
|
||||||
|
discussion = Model.hasOne<Discussion>('discussion');
|
||||||
|
|
||||||
|
createdAt = Model.attribute<Date>('createdAt', Model.transformDate);
|
||||||
|
user = Model.hasOne<User>('user');
|
||||||
|
contentType = Model.attribute<string>('contentType');
|
||||||
|
content = Model.attribute<string>('content');
|
||||||
|
contentHtml = Model.attribute<string>('contentHtml');
|
||||||
|
contentPlain = computed<string>('contentHtml', getPlainContent);
|
||||||
|
|
||||||
|
editedAt = Model.attribute<Date>('editedAt', Model.transformDate);
|
||||||
|
editedUser = Model.hasOne<User>('editedUser');
|
||||||
|
isEdited = computed<boolean>('editedAt', (editedAt) => !!editedAt);
|
||||||
|
|
||||||
|
hiddenAt = Model.attribute<Date>('hiddenAt', Model.transformDate);
|
||||||
|
hiddenUser = Model.hasOne<User>('hiddenUser');
|
||||||
|
isHidden = computed<boolean>('hiddenAt', (hiddenAt) => !!hiddenAt);
|
||||||
|
|
||||||
|
canEdit = Model.attribute<boolean>('canEdit');
|
||||||
|
canHide = Model.attribute<boolean>('canHide');
|
||||||
|
canDelete = Model.attribute<boolean>('canDelete');
|
||||||
|
}
|
@@ -5,34 +5,33 @@ import stringToColor from '../utils/stringToColor';
|
|||||||
import ItemList from '../utils/ItemList';
|
import ItemList from '../utils/ItemList';
|
||||||
import computed from '../utils/computed';
|
import computed from '../utils/computed';
|
||||||
import GroupBadge from '../components/GroupBadge';
|
import GroupBadge from '../components/GroupBadge';
|
||||||
|
import Group from './Group';
|
||||||
|
|
||||||
export default class User extends Model {}
|
export default class User extends Model {
|
||||||
|
username = Model.attribute<string>('username');
|
||||||
|
displayName = Model.attribute<string>('displayName');
|
||||||
|
email = Model.attribute<string>('email');
|
||||||
|
isEmailConfirmed = Model.attribute<boolean>('isEmailConfirmed');
|
||||||
|
password = Model.attribute<string>('password');
|
||||||
|
|
||||||
Object.assign(User.prototype, {
|
avatarUrl = Model.attribute<string>('avatarUrl');
|
||||||
username: Model.attribute('username'),
|
preferences = Model.attribute<any>('preferences');
|
||||||
displayName: Model.attribute('displayName'),
|
groups = Model.hasMany<Group>('groups');
|
||||||
email: Model.attribute('email'),
|
|
||||||
isEmailConfirmed: Model.attribute('isEmailConfirmed'),
|
|
||||||
password: Model.attribute('password'),
|
|
||||||
|
|
||||||
avatarUrl: Model.attribute('avatarUrl'),
|
joinTime = Model.attribute<Date>('joinTime', Model.transformDate);
|
||||||
preferences: Model.attribute('preferences'),
|
lastSeenAt = Model.attribute<Date>('lastSeenAt', Model.transformDate);
|
||||||
groups: Model.hasMany('groups'),
|
markedAllAsReadAt = Model.attribute<Date>('markedAllAsReadAt', Model.transformDate);
|
||||||
|
unreadNotificationCount = Model.attribute<number>('unreadNotificationCount');
|
||||||
|
newNotificationCount = Model.attribute<number>('newNotificationCount');
|
||||||
|
|
||||||
joinTime: Model.attribute('joinTime', Model.transformDate),
|
discussionCount = Model.attribute<number>('discussionCount');
|
||||||
lastSeenAt: Model.attribute('lastSeenAt', Model.transformDate),
|
commentCount = Model.attribute<number>('commentCount');
|
||||||
markedAllAsReadAt: Model.attribute('markedAllAsReadAt', Model.transformDate),
|
|
||||||
unreadNotificationCount: Model.attribute('unreadNotificationCount'),
|
|
||||||
newNotificationCount: Model.attribute('newNotificationCount'),
|
|
||||||
|
|
||||||
discussionCount: Model.attribute('discussionCount'),
|
canEdit = Model.attribute<boolean>('canEdit');
|
||||||
commentCount: Model.attribute('commentCount'),
|
canDelete = Model.attribute<boolean>('canDelete');
|
||||||
|
|
||||||
canEdit: Model.attribute('canEdit'),
|
avatarColor = null;
|
||||||
canDelete: Model.attribute('canDelete'),
|
color = computed<string>('username', 'avatarUrl', 'avatarColor', (username, avatarUrl, avatarColor) => {
|
||||||
|
|
||||||
avatarColor: null,
|
|
||||||
color: computed('username', 'avatarUrl', 'avatarColor', function (username, avatarUrl, avatarColor) {
|
|
||||||
// If we've already calculated and cached the dominant color of the user's
|
// If we've already calculated and cached the dominant color of the user's
|
||||||
// avatar, then we can return that in RGB format. If we haven't, we'll want
|
// avatar, then we can return that in RGB format. If we haven't, we'll want
|
||||||
// to calculate it. Unless the user doesn't have an avatar, in which case
|
// to calculate it. Unless the user doesn't have an avatar, in which case
|
||||||
@@ -45,7 +44,7 @@ Object.assign(User.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return '#' + stringToColor(username);
|
return '#' + stringToColor(username);
|
||||||
}),
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether or not the user has been seen in the last 5 minutes.
|
* Check whether or not the user has been seen in the last 5 minutes.
|
||||||
@@ -53,16 +52,16 @@ Object.assign(User.prototype, {
|
|||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
isOnline() {
|
isOnline(): boolean {
|
||||||
return dayjs().subtract(5, 'minutes').isBefore(this.lastSeenAt());
|
return dayjs().subtract(5, 'minutes').isBefore(this.lastSeenAt());
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Badge components that apply to this user.
|
* Get the Badge components that apply to this user.
|
||||||
*
|
*
|
||||||
* @return {ItemList}
|
* @return {ItemList}
|
||||||
*/
|
*/
|
||||||
badges() {
|
badges(): ItemList {
|
||||||
const items = new ItemList();
|
const items = new ItemList();
|
||||||
const groups = this.groups();
|
const groups = this.groups();
|
||||||
|
|
||||||
@@ -73,7 +72,7 @@ Object.assign(User.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return items;
|
return items;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the dominant color of the user's avatar. The dominant color will
|
* Calculate the dominant color of the user's avatar. The dominant color will
|
||||||
@@ -93,7 +92,7 @@ Object.assign(User.prototype, {
|
|||||||
};
|
};
|
||||||
image.crossOrigin = 'anonymous';
|
image.crossOrigin = 'anonymous';
|
||||||
image.src = this.avatarUrl();
|
image.src = this.avatarUrl();
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the user's preferences.
|
* Update the user's preferences.
|
||||||
@@ -107,5 +106,5 @@ Object.assign(User.prototype, {
|
|||||||
Object.assign(preferences, newPreferences);
|
Object.assign(preferences, newPreferences);
|
||||||
|
|
||||||
return this.save({ preferences });
|
return this.save({ preferences });
|
||||||
},
|
}
|
||||||
});
|
}
|
@@ -1,3 +1,5 @@
|
|||||||
|
import Model from '../Model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `computed` utility creates a function that will cache its output until
|
* The `computed` utility creates a function that will cache its output until
|
||||||
* any of the dependent values are dirty.
|
* any of the dependent values are dirty.
|
||||||
@@ -7,14 +9,14 @@
|
|||||||
* dependent values.
|
* dependent values.
|
||||||
* @return {Function}
|
* @return {Function}
|
||||||
*/
|
*/
|
||||||
export default function computed(...dependentKeys) {
|
export default function computed<T, M = Model>(...dependentKeys: any[]) {
|
||||||
const keys = dependentKeys.slice(0, -1);
|
const keys = dependentKeys.slice(0, -1);
|
||||||
const compute = dependentKeys.slice(-1)[0];
|
const compute = dependentKeys.slice(-1)[0];
|
||||||
|
|
||||||
const dependentValues = {};
|
const dependentValues = {};
|
||||||
let computedValue;
|
let computedValue;
|
||||||
|
|
||||||
return function () {
|
return function (this: M): T {
|
||||||
let recompute = false;
|
let recompute = false;
|
||||||
|
|
||||||
// Read all of the dependent values. If any of them have changed since last
|
// Read all of the dependent values. If any of them have changed since last
|
Reference in New Issue
Block a user