diff --git a/js/forum/src/components/Activity.js b/js/forum/src/components/Activity.js index 2c397586c..ea5821bdb 100644 --- a/js/forum/src/components/Activity.js +++ b/js/forum/src/components/Activity.js @@ -22,7 +22,7 @@ export default class Activity extends Component {
{this.description()} - {humanTime(activity.time())} + {humanTime(this.time())}
{this.content()} @@ -34,9 +34,18 @@ export default class Activity extends Component { * Get the user whose avatar should be displayed. * * @return {User} + * @abstract */ user() { - return this.props.activity.user(); + } + + /** + * Get the time of the activity. + * + * @return {Date} + * @abstract + */ + time() { } /** diff --git a/js/forum/src/components/JoinedActivity.js b/js/forum/src/components/JoinedActivity.js deleted file mode 100644 index 1b4df8917..000000000 --- a/js/forum/src/components/JoinedActivity.js +++ /dev/null @@ -1,11 +0,0 @@ -import Activity from 'flarum/components/Activity'; - -/** - * The `JoinedActivity` component displays an activity feed item for when a user - * joined the forum. - */ -export default class JoinedActivity extends Activity { - description() { - return app.trans('core.joined_the_forum'); - } -} diff --git a/js/forum/src/components/PostedActivity.js b/js/forum/src/components/PostedActivity.js index 8f3aff34f..69891a1d2 100644 --- a/js/forum/src/components/PostedActivity.js +++ b/js/forum/src/components/PostedActivity.js @@ -12,14 +12,20 @@ import { truncate } from 'flarum/utils/string'; * - All of the props for Activity */ export default class PostedActivity extends Activity { - description() { - const post = this.props.activity.subject(); + user() { + return this.props.post.user(); + } - return app.trans(post.number() === 1 ? 'core.started_a_discussion' : 'core.posted_a_reply'); + time() { + return this.props.post.time(); + } + + description() { + return app.trans(this.props.post.number() === 1 ? 'core.started_a_discussion' : 'core.posted_a_reply'); } content() { - const post = this.props.activity.subject(); + const post = this.props.post; return ( {this.props.activity.subject().discussion().title()}); + items.add('title',

{this.props.post.discussion().title()}

); return items; } diff --git a/js/forum/src/components/UserDiscussionsPage.js b/js/forum/src/components/UserDiscussionsPage.js new file mode 100644 index 000000000..c739615fa --- /dev/null +++ b/js/forum/src/components/UserDiscussionsPage.js @@ -0,0 +1,26 @@ +import UserPage from 'flarum/components/UserPage'; +import DiscussionList from 'flarum/components/DiscussionList'; + +/** + * The `UserDiscussionsPage` component shows a user's activity feed inside of their + * profile. + */ +export default class UserDiscussionsPage extends UserPage { + constructor(...args) { + super(...args); + + this.loadUser(m.route.param('username')); + } + + content() { + return ( +
+ {DiscussionList.component({ + params: { + q: 'author:' + this.user.username() + } + })} +
+ ); + } +} diff --git a/js/forum/src/components/UserPage.js b/js/forum/src/components/UserPage.js index 0554c03fa..0172db42a 100644 --- a/js/forum/src/components/UserPage.js +++ b/js/forum/src/components/UserPage.js @@ -134,11 +134,11 @@ export default class UserPage extends Component { const items = new ItemList(); const user = this.user; - items.add('activity', + items.add('posts', LinkButton.component({ - href: app.route('user.activity', {username: user.username()}), - children: app.trans('core.activity'), - icon: 'user' + href: app.route('user.posts', {username: user.username()}), + children: [app.trans('core.posts'), {user.commentsCount()}], + icon: 'comment-o' }) ); @@ -150,14 +150,6 @@ export default class UserPage extends Component { }) ); - items.add('posts', - LinkButton.component({ - href: app.route('user.posts', {username: user.username()}), - children: [app.trans('core.posts'), {user.commentsCount()}], - icon: 'comment-o' - }) - ); - if (app.session.user === user) { items.add('separator', Separator.component()); items.add('settings', diff --git a/js/forum/src/components/ActivityPage.js b/js/forum/src/components/UserPostsPage.js similarity index 71% rename from js/forum/src/components/ActivityPage.js rename to js/forum/src/components/UserPostsPage.js index 5b2948f37..4209ddfe3 100644 --- a/js/forum/src/components/ActivityPage.js +++ b/js/forum/src/components/UserPostsPage.js @@ -1,12 +1,13 @@ import UserPage from 'flarum/components/UserPage'; import LoadingIndicator from 'flarum/components/LoadingIndicator'; import Button from 'flarum/components/Button'; +import PostedActivity from 'flarum/components/PostedActivity'; /** - * The `ActivityPage` component shows a user's activity feed inside of their + * The `UserPostsPage` component shows a user's activity feed inside of their * profile. */ -export default class ActivityPage extends UserPage { +export default class UserPostsPage extends UserPage { constructor(...args) { super(...args); @@ -25,10 +26,11 @@ export default class ActivityPage extends UserPage { this.moreResults = false; /** - * The Activity models in the feed. - * @type {Activity[]} + * The Post models in the feed. + * + * @type {Post[]} */ - this.activity = []; + this.posts = []; /** * The number of activity items to load per request. @@ -47,7 +49,7 @@ export default class ActivityPage extends UserPage { footer = LoadingIndicator.component(); } else if (this.moreResults) { footer = ( -
+
{Button.component({ children: app.trans('core.load_more'), className: 'Button', @@ -58,11 +60,10 @@ export default class ActivityPage extends UserPage { } return ( -
-
    - {this.activity.map(activity => { - const ActivityComponent = app.activityComponents[activity.contentType()]; - return ActivityComponent ?
  • {ActivityComponent.component({activity})}
  • : ''; +
    +
      + {this.posts.map(post => { + return
    • {PostedActivity.component({post})}
    • ; })}
    {footer} @@ -87,7 +88,7 @@ export default class ActivityPage extends UserPage { */ refresh() { this.loading = true; - this.activity = []; + this.posts = []; // Redraw, but only if we're not in the middle of a route change. m.startComputation(); @@ -104,12 +105,13 @@ export default class ActivityPage extends UserPage { * @protected */ loadResults(offset) { - return app.store.find('activity', { + return app.store.find('posts', { filter: { user: this.user.id(), - type: this.props.filter + type: 'comment' }, - page: {offset, limit: this.loadLimit} + page: {offset, limit: this.loadLimit}, + sort: '-time' }); } @@ -120,19 +122,19 @@ export default class ActivityPage extends UserPage { */ loadMore() { this.loading = true; - this.loadResults(this.activity.length).then(this.parseResults.bind(this)); + this.loadResults(this.posts.length).then(this.parseResults.bind(this)); } /** * Parse results and append them to the activity feed. * - * @param {Activity[]} results - * @return {Activity[]} + * @param {Post[]} results + * @return {Post[]} */ parseResults(results) { this.loading = false; - [].push.apply(this.activity, results); + [].push.apply(this.posts, results); this.moreResults = results.length >= this.loadLimit; m.redraw(); diff --git a/js/forum/src/initializers/components.js b/js/forum/src/initializers/components.js index 896de17dd..d027768df 100644 --- a/js/forum/src/initializers/components.js +++ b/js/forum/src/initializers/components.js @@ -14,9 +14,5 @@ export default function components(app) { app.postComponents.comment = CommentPost; app.postComponents.discussionRenamed = DiscussionRenamedPost; - app.activityComponents.posted = PostedActivity; - app.activityComponents.startedDiscussion = PostedActivity; - app.activityComponents.joined = JoinedActivity; - app.notificationComponents.discussionRenamed = DiscussionRenamedNotification; } diff --git a/js/forum/src/initializers/routes.js b/js/forum/src/initializers/routes.js index 0c5b87e36..517289702 100644 --- a/js/forum/src/initializers/routes.js +++ b/js/forum/src/initializers/routes.js @@ -1,6 +1,7 @@ import IndexPage from 'flarum/components/IndexPage'; import DiscussionPage from 'flarum/components/DiscussionPage'; -import ActivityPage from 'flarum/components/ActivityPage'; +import UserPostsPage from 'flarum/components/UserPostsPage'; +import UserDiscussionsPage from 'flarum/components/UserDiscussionsPage'; import SettingsPage from 'flarum/components/SettingsPage'; import NotificationsPage from 'flarum/components/NotificationsPage'; @@ -18,10 +19,9 @@ export default function(app) { 'discussion': {path: '/d/:id/:slug', component: DiscussionPage.component()}, 'discussion.near': {path: '/d/:id/:slug/:near', component: DiscussionPage.component()}, - 'user': {path: '/u/:username', component: ActivityPage.component()}, - 'user.activity': {path: '/u/:username', component: ActivityPage.component()}, - 'user.discussions': {path: '/u/:username/discussions', component: ActivityPage.component({filter: 'startedDiscussion'})}, - 'user.posts': {path: '/u/:username/posts', component: ActivityPage.component({filter: 'posted'})}, + 'user': {path: '/u/:username', component: UserPostsPage.component()}, + 'user.posts': {path: '/u/:username', component: UserPostsPage.component()}, + 'user.discussions': {path: '/u/:username/discussions', component: UserDiscussionsPage.component()}, 'settings': {path: '/settings', component: SettingsPage.component()}, 'notifications': {path: '/notifications', component: NotificationsPage.component()} diff --git a/js/lib/models/Activity.js b/js/lib/models/Activity.js deleted file mode 100644 index 865fe3715..000000000 --- a/js/lib/models/Activity.js +++ /dev/null @@ -1,11 +0,0 @@ -import Model from 'flarum/Model'; -import mixin from 'flarum/utils/mixin'; - -export default class Activity extends mixin(Model, { - contentType: Model.attribute('contentType'), - content: Model.attribute('content'), - time: Model.attribute('time', Model.transformDate), - - user: Model.hasOne('user'), - subject: Model.hasOne('subject') -}) {} diff --git a/less/forum/ActivityPage.less b/less/forum/ActivityPage.less index 535ba4cc2..a36ca63a9 100644 --- a/less/forum/ActivityPage.less +++ b/less/forum/ActivityPage.less @@ -1,11 +1,11 @@ -.ActivityPage-loadMore { +.UserPostsPage-loadMore { text-align: center; .LoadingIndicator { height: 46px; } } -.ActivityPage-list { +.ActivityList { border-left: 3px solid @control-bg; list-style: none; margin: 0 0 0 16px; diff --git a/src/Api/Actions/Activity/IndexAction.php b/src/Api/Actions/Activity/IndexAction.php deleted file mode 100644 index bae677d43..000000000 --- a/src/Api/Actions/Activity/IndexAction.php +++ /dev/null @@ -1,94 +0,0 @@ - true, - 'subject.user' => true, - 'subject.discussion' => true - ]; - - /** - * @inheritdoc - */ - public $link = ['user']; - - /** - * @inheritdoc - */ - public $limitMax = 50; - - /** - * @inheritdoc - */ - public $limit = 20; - - /** - * @inheritdoc - */ - public $sortFields = []; - - /** - * @inheritdoc - */ - public $sort; - - /** - * @param UserRepository $users - * @param ActivityRepository $activity - */ - public function __construct(UserRepository $users, ActivityRepository $activity) - { - $this->users = $users; - $this->activity = $activity; - } - - /** - * Get the activity results, ready to be serialized and assigned to the - * document response. - * - * @param JsonApiRequest $request - * @param Document $document - * @return \Illuminate\Database\Eloquent\Collection - */ - protected function data(JsonApiRequest $request, Document $document) - { - $userId = $request->get('filter.user'); - $actor = $request->actor; - - $user = $this->users->findOrFail($userId, $actor); - - return $this->activity->findByUser( - $user->id, - $actor, - $request->limit, - $request->offset, - $request->get('filter.type') - ) - ->load($request->include); - } -} diff --git a/src/Api/Actions/Posts/GetsPosts.php b/src/Api/Actions/Posts/GetsPosts.php index 5fb52022c..ae503eaa7 100644 --- a/src/Api/Actions/Posts/GetsPosts.php +++ b/src/Api/Actions/Posts/GetsPosts.php @@ -22,7 +22,7 @@ trait GetsPosts $offset = $this->posts->getIndexForNumber($where['discussion_id'], $near, $actor); $offset = max(0, $offset - $request->limit / 2); } else { - $offset = 0; + $offset = $request->offset; } return $this->posts->findWhere( diff --git a/src/Api/Actions/Posts/IndexAction.php b/src/Api/Actions/Posts/IndexAction.php index dcae21373..7221f3c13 100644 --- a/src/Api/Actions/Posts/IndexAction.php +++ b/src/Api/Actions/Posts/IndexAction.php @@ -43,7 +43,7 @@ class IndexAction extends SerializeCollectionAction /** * @inheritdoc */ - public $sortFields = []; + public $sortFields = ['time']; /** * @inheritdoc @@ -84,6 +84,9 @@ class IndexAction extends SerializeCollectionAction if ($userId = $request->get('filter.user')) { $where['user_id'] = $userId; } + if ($type = $request->get('filter.type')) { + $where['type'] = $type; + } $posts = $this->getPosts($request, $where); } diff --git a/src/Core/Activity/Activity.php b/src/Core/Activity/Activity.php deleted file mode 100644 index 16b5de481..000000000 --- a/src/Core/Activity/Activity.php +++ /dev/null @@ -1,117 +0,0 @@ -attributes['data'] = json_encode($value); - } - - /** - * Get the subject model for this activity record by looking up its type in - * our subject model map. - * - * @return string|null - */ - public function getSubjectModelAttribute() - { - return array_get(static::$subjectModels, $this->type); - } - - /** - * Define the relationship with the activity's recipient. - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo - */ - public function user() - { - return $this->belongsTo('Flarum\Core\Users\User', 'user_id'); - } - - /** - * Define the relationship with the activity's subject. - * - * @return \Illuminate\Database\Eloquent\Relations\MorphTo - */ - public function subject() - { - return $this->morphTo('subject', 'subjectModel', 'subject_id'); - } - - /** - * Get the type-to-subject-model map. - * - * @return array - */ - public static function getSubjectModels() - { - return static::$subjectModels; - } - - /** - * Set the subject model for the given activity type. - * - * @param string $type The activity type. - * @param string $subjectModel The class name of the subject model for that - * type. - * @return void - */ - public static function setSubjectModel($type, $subjectModel) - { - static::$subjectModels[$type] = $subjectModel; - } -} diff --git a/src/Core/Activity/ActivityRepository.php b/src/Core/Activity/ActivityRepository.php deleted file mode 100644 index d74135d83..000000000 --- a/src/Core/Activity/ActivityRepository.php +++ /dev/null @@ -1,42 +0,0 @@ -whereIn('type', $this->getRegisteredTypes()) - ->latest('time') - ->skip($offset) - ->take($limit); - - if ($type !== null) { - $query->where('type', $type); - } - - return $query->get(); - } - - /** - * Get a list of activity types that have been registered with the activity - * model. - * - * @return array - */ - protected function getRegisteredTypes() - { - return array_keys(Activity::getSubjectModels()); - } -} diff --git a/src/Core/Activity/ActivityServiceProvider.php b/src/Core/Activity/ActivityServiceProvider.php deleted file mode 100644 index c689ebbff..000000000 --- a/src/Core/Activity/ActivityServiceProvider.php +++ /dev/null @@ -1,58 +0,0 @@ -registerActivityTypes(); - - $events = $this->app->make('events'); - $events->subscribe('Flarum\Core\Activity\Listeners\UserActivitySyncer'); - } - - /** - * Register activity types. - * - * @return void - */ - public function registerActivityTypes() - { - $blueprints = [ - 'Flarum\Core\Activity\PostedBlueprint', - 'Flarum\Core\Activity\StartedDiscussionBlueprint', - 'Flarum\Core\Activity\JoinedBlueprint' - ]; - - event(new RegisterActivityTypes($blueprints)); - - foreach ($blueprints as $blueprint) { - Activity::setSubjectModel( - $blueprint::getType(), - $blueprint::getSubjectModel() - ); - } - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $this->app->bind( - 'Flarum\Core\Activity\ActivityRepositoryInterface', - 'Flarum\Core\Activity\EloquentActivityRepository' - ); - } -} diff --git a/src/Core/Activity/ActivitySyncer.php b/src/Core/Activity/ActivitySyncer.php deleted file mode 100644 index 57eacf98e..000000000 --- a/src/Core/Activity/ActivitySyncer.php +++ /dev/null @@ -1,117 +0,0 @@ -activity = $activity; - } - - /** - * Sync a piece of activity so that it is present for the specified users, - * and not present for anyone else. - * - * @param Blueprint $blueprint - * @param \Flarum\Core\Models\User[] $users - * @return void - */ - public function sync(Blueprint $blueprint, array $users) - { - $attributes = $this->getAttributes($blueprint); - - // Find all existing activity records in the database matching this - // blueprint. We will begin by assuming that they all need to be - // deleted in order to match the provided list of users. - $toDelete = Activity::where($attributes)->get(); - $toInsert = []; - - // For each of the provided users, check to see if they already have - // an activity record in the database. If they do, we can leave it be; - // otherwise, we will need to create a new one for them. - foreach ($users as $user) { - $existing = $toDelete->first(function ($activity) use ($user) { - return $activity->user_id === $user->id; - }); - - if ($existing) { - $toDelete->forget($toDelete->search($existing)); - } else { - $toInsert[] = $attributes + ['user_id' => $user->id]; - } - } - - // Finally, delete all of the remaining activity records which weren't - // removed from this collection by the above loop. Insert the records - // we need to insert as well. - if (count($toDelete)) { - $this->deleteActivity($toDelete->lists('id')); - } - - if (count($toInsert)) { - $this->createActivity($toInsert); - } - } - - /** - * Delete a piece of activity for all users. - * - * @param Blueprint $blueprint - * @return void - */ - public function delete(Blueprint $blueprint) - { - Activity::where($this->getAttributes($blueprint))->delete(); - } - - /** - * Delete a list of activity records. - * - * @param int[] $ids - */ - protected function deleteActivity(array $ids) - { - Activity::whereIn('id', $ids)->delete(); - } - - /** - * Insert a list of activity record into the database. - * - * @param array[] $records An array containing arrays of activity record - * attributes to insert. - */ - protected function createActivity(array $records) - { - Activity::insert($records); - } - - /** - * Construct an array of attributes to be stored in an activity record in - * the database, given an activity blueprint. - * - * @param Blueprint $blueprint - * @return array - */ - protected function getAttributes(Blueprint $blueprint) - { - return [ - 'type' => $blueprint::getType(), - 'subject_id' => $blueprint->getSubject()->id, - 'time' => $blueprint->getTime() - ]; - } -} diff --git a/src/Core/Activity/Blueprint.php b/src/Core/Activity/Blueprint.php deleted file mode 100644 index 1a30fdbb5..000000000 --- a/src/Core/Activity/Blueprint.php +++ /dev/null @@ -1,37 +0,0 @@ -user = $user; - } - - /** - * {@inheritdoc} - */ - public function getSubject() - { - return $this->user; - } - - /** - * {@inheritdoc} - */ - public function getTime() - { - return $this->user->join_time; - } - - /** - * {@inheritdoc} - */ - public static function getType() - { - return 'joined'; - } - - /** - * {@inheritdoc} - */ - public static function getSubjectModel() - { - return 'Flarum\Core\Users\User'; - } -} diff --git a/src/Core/Activity/Listeners/UserActivitySyncer.php b/src/Core/Activity/Listeners/UserActivitySyncer.php deleted file mode 100755 index 707a785f0..000000000 --- a/src/Core/Activity/Listeners/UserActivitySyncer.php +++ /dev/null @@ -1,129 +0,0 @@ -activity = $activity; - } - - /** - * @param \Illuminate\Contracts\Events\Dispatcher $events - * @return void - */ - public function subscribe(Dispatcher $events) - { - $events->listen('Flarum\Events\PostWasPosted', [$this, 'whenPostWasPosted']); - $events->listen('Flarum\Events\PostWasHidden', [$this, 'whenPostWasHidden']); - $events->listen('Flarum\Events\PostWasRestored', [$this, 'whenPostWasRestored']); - $events->listen('Flarum\Events\PostWasDeleted', [$this, 'whenPostWasDeleted']); - $events->listen('Flarum\Events\UserWasRegistered', [$this, 'whenUserWasRegistered']); - } - - /** - * @param \Flarum\Events\PostWasPosted $event - * @return void - */ - public function whenPostWasPosted(PostWasPosted $event) - { - $this->postBecameVisible($event->post); - } - - /** - * @param \Flarum\Events\PostWasHidden $event - * @return void - */ - public function whenPostWasHidden(PostWasHidden $event) - { - $this->postBecameInvisible($event->post); - } - - /** - * @param \Flarum\Events\PostWasRestored $event - * @return void - */ - public function whenPostWasRestored(PostWasRestored $event) - { - $this->postBecameVisible($event->post); - } - - /** - * @param \Flarum\Events\PostWasDeleted $event - * @return void - */ - public function whenPostWasDeleted(PostWasDeleted $event) - { - $this->postBecameInvisible($event->post); - } - - /** - * @param \Flarum\Events\UserWasRegistered $event - * @return void - */ - public function whenUserWasRegistered(UserWasRegistered $event) - { - $blueprint = new JoinedBlueprint($event->user); - - $this->activity->sync($blueprint, [$event->user]); - } - - /** - * Sync activity to a post's author when a post becomes visible. - * - * @param \Flarum\Core\Posts\Post $post - * @return void - */ - protected function postBecameVisible(Post $post) - { - if ($post->isVisibleTo(new Guest)) { - $blueprint = $this->postedBlueprint($post); - - $this->activity->sync($blueprint, [$post->user]); - } - } - - /** - * Delete activity when a post becomes invisible. - * - * @param \Flarum\Core\Posts\Post $post - * @return void - */ - protected function postBecameInvisible(Post $post) - { - $blueprint = $this->postedBlueprint($post); - - $this->activity->delete($blueprint); - } - - /** - * Create the appropriate activity blueprint for a post. - * - * @param \Flarum\Core\Posts\Post $post - * @return \Flarum\Core\Activity\Blueprint - */ - protected function postedBlueprint(Post $post) - { - return $post->number == 1 ? new StartedDiscussionBlueprint($post) : new PostedBlueprint($post); - } -} diff --git a/src/Core/Activity/PostedBlueprint.php b/src/Core/Activity/PostedBlueprint.php deleted file mode 100644 index 6ba2a3874..000000000 --- a/src/Core/Activity/PostedBlueprint.php +++ /dev/null @@ -1,59 +0,0 @@ -post = $post; - } - - /** - * {@inheritdoc} - */ - public function getSubject() - { - return $this->post; - } - - /** - * {@inheritdoc} - */ - public function getTime() - { - return $this->post->time; - } - - /** - * {@inheritdoc} - */ - public static function getType() - { - return 'posted'; - } - - /** - * {@inheritdoc} - */ - public static function getSubjectModel() - { - return 'Flarum\Core\Posts\Post'; - } -} diff --git a/src/Core/Activity/StartedDiscussionBlueprint.php b/src/Core/Activity/StartedDiscussionBlueprint.php deleted file mode 100644 index 08b549021..000000000 --- a/src/Core/Activity/StartedDiscussionBlueprint.php +++ /dev/null @@ -1,16 +0,0 @@ -app->register('Flarum\Core\Activity\ActivityServiceProvider'); $this->app->register('Flarum\Core\Discussions\DiscussionsServiceProvider'); $this->app->register('Flarum\Core\Formatter\FormatterServiceProvider'); $this->app->register('Flarum\Core\Groups\GroupsServiceProvider'); diff --git a/src/Core/Posts/PostRepository.php b/src/Core/Posts/PostRepository.php index 9e48e5495..cba923fbb 100644 --- a/src/Core/Posts/PostRepository.php +++ b/src/Core/Posts/PostRepository.php @@ -85,9 +85,20 @@ class PostRepository */ public function findByIds(array $ids, User $actor = null) { - $ids = $this->filterDiscussionVisibleTo($ids, $actor); + $visibleIds = $this->filterDiscussionVisibleTo($ids, $actor); - $posts = Post::with('discussion')->whereIn('id', (array) $ids)->get(); + $posts = Post::with('discussion')->whereIn('id', $visibleIds)->get(); + + $posts->sort(function ($a, $b) use ($ids) { + $aPos = array_search($a->id, $ids); + $bPos = array_search($b->id, $ids); + + if ($aPos === $bPos) { + return 0; + } + + return $aPos < $bPos ? -1 : 1; + }); return $this->filterVisibleTo($posts, $actor); }