mirror of
https://github.com/flarum/core.git
synced 2025-10-14 00:15:51 +02:00
Webpack (#1367)
* Replace gulp with webpack and npm scripts for JS compilation * Set up Travis CI to commit compiled JS * Restructure `js` directory; only one instance of npm, forum/admin are "submodules" * Refactor JS initializers into Application subclasses * Maintain partial compatibility API (importing from absolute paths) for extensions * Remove minification responsibility from PHP asset compiler * Restructure `less` directory
This commit is contained in:
104
js/src/common/models/Discussion.js
Normal file
104
js/src/common/models/Discussion.js
Normal file
@@ -0,0 +1,104 @@
|
||||
import Model from '../Model';
|
||||
import computed from '../utils/computed';
|
||||
import ItemList from '../utils/ItemList';
|
||||
import Badge from '../components/Badge';
|
||||
|
||||
export default class Discussion extends Model {}
|
||||
|
||||
Object.assign(Discussion.prototype, {
|
||||
title: Model.attribute('title'),
|
||||
slug: Model.attribute('slug'),
|
||||
|
||||
startTime: Model.attribute('startTime', Model.transformDate),
|
||||
startUser: Model.hasOne('startUser'),
|
||||
startPost: Model.hasOne('startPost'),
|
||||
|
||||
lastTime: Model.attribute('lastTime', Model.transformDate),
|
||||
lastUser: Model.hasOne('lastUser'),
|
||||
lastPost: Model.hasOne('lastPost'),
|
||||
lastPostNumber: Model.attribute('lastPostNumber'),
|
||||
|
||||
commentsCount: Model.attribute('commentsCount'),
|
||||
repliesCount: computed('commentsCount', commentsCount => Math.max(0, commentsCount - 1)),
|
||||
posts: Model.hasMany('posts'),
|
||||
mostRelevantPost: Model.hasOne('mostRelevantPost'),
|
||||
|
||||
readTime: Model.attribute('readTime', Model.transformDate),
|
||||
readNumber: Model.attribute('readNumber'),
|
||||
isUnread: computed('unreadCount', unreadCount => !!unreadCount),
|
||||
isRead: computed('unreadCount', unreadCount => app.session.user && !unreadCount),
|
||||
|
||||
hideTime: Model.attribute('hideTime', Model.transformDate),
|
||||
hideUser: Model.hasOne('hideUser'),
|
||||
isHidden: computed('hideTime', hideTime => !!hideTime),
|
||||
|
||||
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.
|
||||
*
|
||||
* @param {Integer} id The ID of the post to remove.
|
||||
* @public
|
||||
*/
|
||||
removePost(id) {
|
||||
const relationships = this.data.relationships;
|
||||
const posts = relationships && relationships.posts;
|
||||
|
||||
if (posts) {
|
||||
posts.data.some((data, i) => {
|
||||
if (id === data.id) {
|
||||
posts.data.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the estimated number of unread posts in this discussion for the current
|
||||
* user.
|
||||
*
|
||||
* @return {Integer}
|
||||
* @public
|
||||
*/
|
||||
unreadCount() {
|
||||
const user = app.session.user;
|
||||
|
||||
if (user && user.readTime() < this.lastTime()) {
|
||||
return Math.max(0, this.lastPostNumber() - (this.readNumber() || 0));
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Badge components that apply to this discussion.
|
||||
*
|
||||
* @return {ItemList}
|
||||
* @public
|
||||
*/
|
||||
badges() {
|
||||
const items = new ItemList();
|
||||
|
||||
if (this.isHidden()) {
|
||||
items.add('hidden', <Badge type="hidden" icon="fas fa-trash" label={app.translator.trans('core.lib.badge.hidden_tooltip')}/>);
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of all of the post IDs in this discussion.
|
||||
*
|
||||
* @return {Array}
|
||||
* @public
|
||||
*/
|
||||
postIds() {
|
||||
const posts = this.data.relationships.posts;
|
||||
|
||||
return posts ? posts.data.map(link => link.id) : [];
|
||||
}
|
||||
});
|
7
js/src/common/models/Forum.js
Normal file
7
js/src/common/models/Forum.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Model from '../Model';
|
||||
|
||||
export default class Forum extends Model {
|
||||
apiEndpoint() {
|
||||
return '/';
|
||||
}
|
||||
}
|
16
js/src/common/models/Group.js
Normal file
16
js/src/common/models/Group.js
Normal file
@@ -0,0 +1,16 @@
|
||||
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')
|
||||
});
|
||||
|
||||
Group.ADMINISTRATOR_ID = '1';
|
||||
Group.GUEST_ID = '2';
|
||||
Group.MEMBER_ID = '3';
|
||||
|
||||
export default Group;
|
19
js/src/common/models/Notification.js
Normal file
19
js/src/common/models/Notification.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import Model from '../Model';
|
||||
import computed from '../utils/computed';
|
||||
|
||||
export default class Notification extends Model {}
|
||||
|
||||
Object.assign(Notification.prototype, {
|
||||
contentType: Model.attribute('contentType'),
|
||||
subjectId: Model.attribute('subjectId'),
|
||||
content: Model.attribute('content'),
|
||||
time: Model.attribute('time', Model.date),
|
||||
|
||||
isRead: Model.attribute('isRead'),
|
||||
unreadCount: Model.attribute('unreadCount'),
|
||||
additionalUnreadCount: computed('unreadCount', unreadCount => Math.max(0, unreadCount - 1)),
|
||||
|
||||
user: Model.hasOne('user'),
|
||||
sender: Model.hasOne('sender'),
|
||||
subject: Model.hasOne('subject')
|
||||
});
|
28
js/src/common/models/Post.js
Normal file
28
js/src/common/models/Post.js
Normal file
@@ -0,0 +1,28 @@
|
||||
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'),
|
||||
|
||||
time: Model.attribute('time', Model.transformDate),
|
||||
user: Model.hasOne('user'),
|
||||
contentType: Model.attribute('contentType'),
|
||||
content: Model.attribute('content'),
|
||||
contentHtml: Model.attribute('contentHtml'),
|
||||
contentPlain: computed('contentHtml', getPlainContent),
|
||||
|
||||
editTime: Model.attribute('editTime', Model.transformDate),
|
||||
editUser: Model.hasOne('editUser'),
|
||||
isEdited: computed('editTime', editTime => !!editTime),
|
||||
|
||||
hideTime: Model.attribute('hideTime', Model.transformDate),
|
||||
hideUser: Model.hasOne('hideUser'),
|
||||
isHidden: computed('hideTime', hideTime => !!hideTime),
|
||||
|
||||
canEdit: Model.attribute('canEdit'),
|
||||
canDelete: Model.attribute('canDelete')
|
||||
});
|
110
js/src/common/models/User.js
Normal file
110
js/src/common/models/User.js
Normal file
@@ -0,0 +1,110 @@
|
||||
/*global ColorThief*/
|
||||
|
||||
import Model from '../Model';
|
||||
import stringToColor from '../utils/stringToColor';
|
||||
import ItemList from '../utils/ItemList';
|
||||
import computed from '../utils/computed';
|
||||
import GroupBadge from '../components/GroupBadge';
|
||||
|
||||
export default class User extends Model {}
|
||||
|
||||
Object.assign(User.prototype, {
|
||||
username: Model.attribute('username'),
|
||||
displayName: Model.attribute('displayName'),
|
||||
email: Model.attribute('email'),
|
||||
isActivated: Model.attribute('isActivated'),
|
||||
password: Model.attribute('password'),
|
||||
|
||||
avatarUrl: Model.attribute('avatarUrl'),
|
||||
preferences: Model.attribute('preferences'),
|
||||
groups: Model.hasMany('groups'),
|
||||
|
||||
joinTime: Model.attribute('joinTime', Model.transformDate),
|
||||
lastSeenTime: Model.attribute('lastSeenTime', Model.transformDate),
|
||||
readTime: Model.attribute('readTime', Model.transformDate),
|
||||
unreadNotificationsCount: Model.attribute('unreadNotificationsCount'),
|
||||
newNotificationsCount: Model.attribute('newNotificationsCount'),
|
||||
|
||||
discussionsCount: Model.attribute('discussionsCount'),
|
||||
commentsCount: Model.attribute('commentsCount'),
|
||||
|
||||
canEdit: Model.attribute('canEdit'),
|
||||
canDelete: Model.attribute('canDelete'),
|
||||
|
||||
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
|
||||
// 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
|
||||
// we generate a color from their username.
|
||||
if (avatarColor) {
|
||||
return 'rgb(' + avatarColor.join(', ') + ')';
|
||||
} else if (avatarUrl) {
|
||||
this.calculateAvatarColor();
|
||||
return '';
|
||||
}
|
||||
|
||||
return '#' + stringToColor(username);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Check whether or not the user has been seen in the last 5 minutes.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @public
|
||||
*/
|
||||
isOnline() {
|
||||
return this.lastSeenTime() > moment().subtract(5, 'minutes').toDate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Badge components that apply to this user.
|
||||
*
|
||||
* @return {ItemList}
|
||||
*/
|
||||
badges() {
|
||||
const items = new ItemList();
|
||||
const groups = this.groups();
|
||||
|
||||
if (groups) {
|
||||
groups.forEach(group => {
|
||||
items.add('group' + group.id(), GroupBadge.component({group}));
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculate the dominant color of the user's avatar. The dominant color will
|
||||
* be set to the `avatarColor` property once it has been calculated.
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
calculateAvatarColor() {
|
||||
const image = new Image();
|
||||
const user = this;
|
||||
|
||||
image.onload = function() {
|
||||
const colorThief = new ColorThief();
|
||||
user.avatarColor = colorThief.getColor(this);
|
||||
user.freshness = new Date();
|
||||
m.redraw();
|
||||
};
|
||||
image.src = this.avatarUrl();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the user's preferences.
|
||||
*
|
||||
* @param {Object} newPreferences
|
||||
* @return {Promise}
|
||||
*/
|
||||
savePreferences(newPreferences) {
|
||||
const preferences = this.preferences();
|
||||
|
||||
Object.assign(preferences, newPreferences);
|
||||
|
||||
return this.save({preferences});
|
||||
}
|
||||
});
|
Reference in New Issue
Block a user