mirror of
https://github.com/flarum/framework.git
synced 2024-11-25 09:41:49 +08:00
Clean up model nullability
This commit is contained in:
parent
bbc9143404
commit
3a8d640dab
|
@ -310,6 +310,8 @@ export default abstract class Model {
|
|||
* relationship exists; undefined if the relationship exists but the model
|
||||
* has not been loaded; or the model if it has been loaded.
|
||||
*/
|
||||
static hasOne<M extends Model>(name: string): () => M | false;
|
||||
static hasOne<M extends Model | null>(name: string): () => M | null | false;
|
||||
static hasOne<M extends Model>(name: string): () => M | false {
|
||||
return function (this: Model) {
|
||||
if (this.data.relationships) {
|
||||
|
@ -358,8 +360,12 @@ export default abstract class Model {
|
|||
/**
|
||||
* Transform the given value into a Date object.
|
||||
*/
|
||||
static transformDate(value: string | null): Date | null {
|
||||
return value ? new Date(value) : null;
|
||||
static transformDate(value: string): Date;
|
||||
static transformDate(value: string | null): Date | null;
|
||||
static transformDate(value: string | undefined): Date | undefined;
|
||||
static transformDate(value: string | null | undefined): Date | null | undefined;
|
||||
static transformDate(value: string | null | undefined): Date | null | undefined {
|
||||
return value != null ? new Date(value) : value;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,8 +24,8 @@ export default function avatar(user: User, attrs: ComponentAttrs = {}): Mithril.
|
|||
// uploaded image, or the first letter of their username if they haven't
|
||||
// uploaded one.
|
||||
if (user) {
|
||||
const username: string = user.displayName() || '?';
|
||||
const avatarUrl: string = user.avatarUrl();
|
||||
const username = user.displayName() || '?';
|
||||
const avatarUrl = user.avatarUrl();
|
||||
|
||||
if (hasTitle) attrs.title = attrs.title || username;
|
||||
|
||||
|
|
|
@ -16,46 +16,46 @@ export default class Discussion extends Model {
|
|||
}
|
||||
|
||||
createdAt() {
|
||||
return Model.attribute<Date | null, string | null>('createdAt', Model.transformDate).call(this);
|
||||
return Model.attribute<Date | undefined, string | undefined>('createdAt', Model.transformDate).call(this);
|
||||
}
|
||||
user() {
|
||||
return Model.hasOne<User>('user').call(this);
|
||||
return Model.hasOne<User | null>('user').call(this);
|
||||
}
|
||||
firstPost() {
|
||||
return Model.hasOne<Post>('firstPost').call(this);
|
||||
return Model.hasOne<Post | null>('firstPost').call(this);
|
||||
}
|
||||
|
||||
lastPostedAt() {
|
||||
return Model.attribute<Date | null, string | null>('lastPostedAt', Model.transformDate).call(this);
|
||||
return Model.attribute('lastPostedAt', Model.transformDate).call(this);
|
||||
}
|
||||
lastPostedUser() {
|
||||
return Model.hasOne<User>('lastPostedUser').call(this);
|
||||
return Model.hasOne<User | null>('lastPostedUser').call(this);
|
||||
}
|
||||
lastPost() {
|
||||
return Model.hasOne<Post>('lastPost').call(this);
|
||||
return Model.hasOne<Post | null>('lastPost').call(this);
|
||||
}
|
||||
lastPostNumber() {
|
||||
return Model.attribute<number | null>('lastPostNumber').call(this);
|
||||
return Model.attribute<number | null | undefined>('lastPostNumber').call(this);
|
||||
}
|
||||
|
||||
commentCount() {
|
||||
return Model.attribute<number | null>('commentCount').call(this);
|
||||
return Model.attribute<number | undefined>('commentCount').call(this);
|
||||
}
|
||||
replyCount() {
|
||||
return computed<number, this>('commentCount', (commentCount) => Math.max(0, (commentCount as number) - 1)).call(this);
|
||||
return computed<Number, this>('commentCount', (commentCount) => Math.max(0, (commentCount as number ?? 0) - 1)).call(this);
|
||||
}
|
||||
posts() {
|
||||
return Model.hasMany<Post>('posts').call(this);
|
||||
}
|
||||
mostRelevantPost() {
|
||||
return Model.hasOne<Post>('mostRelevantPost').call(this);
|
||||
return Model.hasOne<Post | null>('mostRelevantPost').call(this);
|
||||
}
|
||||
|
||||
lastReadAt() {
|
||||
return Model.attribute<Date | null, string | null>('lastReadAt', Model.transformDate).call(this);
|
||||
return Model.attribute('lastReadAt', Model.transformDate).call(this);
|
||||
}
|
||||
lastReadPostNumber() {
|
||||
return Model.attribute<number | null>('lastReadPostNumber').call(this);
|
||||
return Model.attribute<number | null | undefined>('lastReadPostNumber').call(this);
|
||||
}
|
||||
isUnread() {
|
||||
return computed<boolean, this>('unreadCount', (unreadCount) => !!unreadCount).call(this);
|
||||
|
@ -65,26 +65,26 @@ export default class Discussion extends Model {
|
|||
}
|
||||
|
||||
hiddenAt() {
|
||||
return Model.attribute<Date | null, string | null>('hiddenAt', Model.transformDate).call(this);
|
||||
return Model.attribute('hiddenAt', Model.transformDate).call(this);
|
||||
}
|
||||
hiddenUser() {
|
||||
return Model.hasOne<User>('hiddenUser').call(this);
|
||||
return Model.hasOne<User | null>('hiddenUser').call(this);
|
||||
}
|
||||
isHidden() {
|
||||
return computed<boolean, this>('hiddenAt', (hiddenAt) => !!hiddenAt).call(this);
|
||||
}
|
||||
|
||||
canReply() {
|
||||
return Model.attribute<boolean | null>('canReply').call(this);
|
||||
return Model.attribute<boolean | undefined>('canReply').call(this);
|
||||
}
|
||||
canRename() {
|
||||
return Model.attribute<boolean | null>('canRename').call(this);
|
||||
return Model.attribute<boolean | undefined>('canRename').call(this);
|
||||
}
|
||||
canHide() {
|
||||
return Model.attribute<boolean | null>('canHide').call(this);
|
||||
return Model.attribute<boolean | undefined>('canHide').call(this);
|
||||
}
|
||||
canDelete() {
|
||||
return Model.attribute<boolean | null>('canDelete').call(this);
|
||||
return Model.attribute<boolean | undefined>('canDelete').call(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,10 +13,10 @@ export default class Group extends Model {
|
|||
}
|
||||
|
||||
color() {
|
||||
return Model.attribute<string>('color').call(this);
|
||||
return Model.attribute<string | null>('color').call(this);
|
||||
}
|
||||
icon() {
|
||||
return Model.attribute<string>('icon').call(this);
|
||||
return Model.attribute<string | null>('icon').call(this);
|
||||
}
|
||||
|
||||
isHidden() {
|
||||
|
|
|
@ -9,7 +9,7 @@ export default class Notification extends Model {
|
|||
return Model.attribute<string>('content').call(this);
|
||||
}
|
||||
createdAt() {
|
||||
return Model.attribute<Date | null, string | null>('createdAt', Model.transformDate).call(this);
|
||||
return Model.attribute<Date, string>('createdAt', Model.transformDate).call(this);
|
||||
}
|
||||
|
||||
isRead() {
|
||||
|
@ -20,9 +20,9 @@ export default class Notification extends Model {
|
|||
return Model.hasOne<User>('user').call(this);
|
||||
}
|
||||
fromUser() {
|
||||
return Model.hasOne<User>('fromUser').call(this);
|
||||
return Model.hasOne<User | null>('fromUser').call(this);
|
||||
}
|
||||
subject() {
|
||||
return Model.hasOne('subject').call(this);
|
||||
return Model.hasOne<Model | null>('subject').call(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,55 +13,61 @@ export default class Post extends Model {
|
|||
}
|
||||
|
||||
createdAt() {
|
||||
return Model.attribute<Date | null, string>('createdAt', Model.transformDate).call(this);
|
||||
return Model.attribute<Date, string>('createdAt', Model.transformDate).call(this);
|
||||
}
|
||||
user() {
|
||||
return Model.hasOne<User>('user').call(this);
|
||||
}
|
||||
|
||||
contentType() {
|
||||
return Model.attribute<string>('contentType').call(this);
|
||||
return Model.attribute<string | null>('contentType').call(this);
|
||||
}
|
||||
content() {
|
||||
return Model.attribute<string>('content').call(this);
|
||||
return Model.attribute<string | null | undefined>('content').call(this);
|
||||
}
|
||||
contentHtml() {
|
||||
return Model.attribute<string>('contentHtml').call(this);
|
||||
return Model.attribute<string | null | undefined>('contentHtml').call(this);
|
||||
}
|
||||
renderFailed() {
|
||||
return Model.attribute<boolean>('renderFailed').call(this);
|
||||
return Model.attribute<boolean | undefined>('renderFailed').call(this);
|
||||
}
|
||||
contentPlain() {
|
||||
return computed<string>('contentHtml', getPlainContent as (content: unknown) => string).call(this);
|
||||
return computed<string | null | undefined>('contentHtml', (content) => {
|
||||
if (typeof content === 'string') {
|
||||
return getPlainContent(content);
|
||||
}
|
||||
|
||||
return content as (null | undefined);
|
||||
}).call(this);
|
||||
}
|
||||
|
||||
editedAt() {
|
||||
return Model.attribute<Date | null, string>('editedAt', Model.transformDate).call(this);
|
||||
return Model.attribute('editedAt', Model.transformDate).call(this);
|
||||
}
|
||||
editedUser() {
|
||||
return Model.hasOne<User>('editedUser').call(this);
|
||||
return Model.hasOne<User | null>('editedUser').call(this);
|
||||
}
|
||||
isEdited() {
|
||||
return computed<boolean>('editedAt', (editedAt) => !!editedAt).call(this);
|
||||
}
|
||||
|
||||
hiddenAt() {
|
||||
return Model.attribute<Date | null, string>('hiddenAt', Model.transformDate).call(this);
|
||||
return Model.attribute('hiddenAt', Model.transformDate).call(this);
|
||||
}
|
||||
hiddenUser() {
|
||||
return Model.hasOne<User>('hiddenUser').call(this);
|
||||
return Model.hasOne<User | null>('hiddenUser').call(this);
|
||||
}
|
||||
isHidden() {
|
||||
return computed<boolean>('hiddenAt', (hiddenAt) => !!hiddenAt).call(this);
|
||||
}
|
||||
|
||||
canEdit() {
|
||||
return Model.attribute<boolean>('canEdit').call(this);
|
||||
return Model.attribute<boolean | undefined>('canEdit').call(this);
|
||||
}
|
||||
canHide() {
|
||||
return Model.attribute<boolean>('canHide').call(this);
|
||||
return Model.attribute<boolean | undefined>('canHide').call(this);
|
||||
}
|
||||
canDelete() {
|
||||
return Model.attribute<boolean>('canDelete').call(this);
|
||||
return Model.attribute<boolean | undefined>('canDelete').call(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import ItemList from '../utils/ItemList';
|
|||
import computed from '../utils/computed';
|
||||
import GroupBadge from '../components/GroupBadge';
|
||||
import Mithril from 'mithril';
|
||||
import Group from './Group';
|
||||
|
||||
export default class User extends Model {
|
||||
username() {
|
||||
|
@ -19,65 +20,65 @@ export default class User extends Model {
|
|||
}
|
||||
|
||||
email() {
|
||||
return Model.attribute<string | null>('email').call(this);
|
||||
return Model.attribute<string | undefined>('email').call(this);
|
||||
}
|
||||
isEmailConfirmed() {
|
||||
return Model.attribute<boolean | null>('isEmailConfirmed').call(this);
|
||||
return Model.attribute<boolean | undefined>('isEmailConfirmed').call(this);
|
||||
}
|
||||
|
||||
password() {
|
||||
return Model.attribute<string | null>('password').call(this);
|
||||
return Model.attribute<string | undefined>('password').call(this);
|
||||
}
|
||||
|
||||
avatarUrl() {
|
||||
return Model.attribute<string>('avatarUrl').call(this);
|
||||
return Model.attribute<string | null>('avatarUrl').call(this);
|
||||
}
|
||||
|
||||
preferences() {
|
||||
return Model.attribute<Record<string, any> | null>('preferences').call(this);
|
||||
return Model.attribute<Record<string, any> | null | undefined>('preferences').call(this);
|
||||
}
|
||||
|
||||
groups() {
|
||||
return Model.hasMany('groups').call(this);
|
||||
return Model.hasMany<Group>('groups').call(this);
|
||||
}
|
||||
|
||||
joinTime() {
|
||||
return Model.attribute<Date | null, string | null>('joinTime', Model.transformDate).call(this);
|
||||
return Model.attribute('joinTime', Model.transformDate).call(this);
|
||||
}
|
||||
|
||||
lastSeenAt() {
|
||||
return Model.attribute<Date | null, string | null>('lastSeenAt', Model.transformDate).call(this);
|
||||
return Model.attribute('lastSeenAt', Model.transformDate).call(this);
|
||||
}
|
||||
|
||||
markedAllAsReadAt() {
|
||||
return Model.attribute<Date | null, string | null>('markedAllAsReadAt', Model.transformDate).call(this);
|
||||
return Model.attribute('markedAllAsReadAt', Model.transformDate).call(this);
|
||||
}
|
||||
|
||||
unreadNotificationCount() {
|
||||
return Model.attribute<number | null>('unreadNotificationCount').call(this);
|
||||
return Model.attribute<number | undefined>('unreadNotificationCount').call(this);
|
||||
}
|
||||
newNotificationCount() {
|
||||
return Model.attribute<number | null>('newNotificationCount').call(this);
|
||||
return Model.attribute<number | undefined>('newNotificationCount').call(this);
|
||||
}
|
||||
|
||||
discussionCount() {
|
||||
return Model.attribute<number | null>('discussionCount').call(this);
|
||||
return Model.attribute<number | undefined>('discussionCount').call(this);
|
||||
}
|
||||
commentCount() {
|
||||
return Model.attribute<number | null>('commentCount').call(this);
|
||||
return Model.attribute<number | undefined>('commentCount').call(this);
|
||||
}
|
||||
|
||||
canEdit() {
|
||||
return Model.attribute<boolean | null>('canEdit').call(this);
|
||||
return Model.attribute<boolean | undefined>('canEdit').call(this);
|
||||
}
|
||||
canEditCredentials() {
|
||||
return Model.attribute<boolean | null>('canEditCredentials').call(this);
|
||||
return Model.attribute<boolean | undefined>('canEditCredentials').call(this);
|
||||
}
|
||||
canEditGroups() {
|
||||
return Model.attribute<boolean | null>('canEditGroups').call(this);
|
||||
return Model.attribute<boolean | undefined>('canEditGroups').call(this);
|
||||
}
|
||||
canDelete() {
|
||||
return Model.attribute<boolean | null>('canDelete').call(this);
|
||||
return Model.attribute<boolean | undefined>('canDelete').call(this);
|
||||
}
|
||||
|
||||
color() {
|
||||
|
@ -148,7 +149,7 @@ export default class User extends Model {
|
|||
m.redraw();
|
||||
};
|
||||
image.crossOrigin = 'anonymous';
|
||||
image.src = this.avatarUrl();
|
||||
image.src = this.avatarUrl() ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,7 +40,7 @@ export default class DiscussionsSearchSource implements SearchSource {
|
|||
<li className="DiscussionSearchResult" data-index={'discussions' + discussion.id()}>
|
||||
<Link href={app.route.discussion(discussion, mostRelevantPost && mostRelevantPost.number())}>
|
||||
<div className="DiscussionSearchResult-title">{highlight(discussion.title(), query)}</div>
|
||||
{mostRelevantPost ? <div className="DiscussionSearchResult-excerpt">{highlight(mostRelevantPost.contentPlain(), query, 100)}</div> : ''}
|
||||
{mostRelevantPost ? <div className="DiscussionSearchResult-excerpt">{highlight(mostRelevantPost.contentPlain() ?? '', query, 100)}</div> : ''}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue
Block a user