diff --git a/js/src/forum/components/DiscussionList.js b/js/src/forum/components/DiscussionList.js
index 1ab208ca58..b1fe6588aa 100644
--- a/js/src/forum/components/DiscussionList.js
+++ b/js/src/forum/components/DiscussionList.js
@@ -1,4 +1,6 @@
import Component from '../../common/Component';
+import ItemList from '../../common/utils/ItemList';
+import listItems from '../../common/helpers/listItems';
import DiscussionListItem from './DiscussionListItem';
import Button from '../../common/components/Button';
import LoadingIndicator from '../../common/components/LoadingIndicator';
@@ -38,6 +40,32 @@ export default class DiscussionList extends Component {
this.refresh();
}
+ aboveDiscussions() {
+ const items = new ItemList();
+
+ if (app.markedAllAsRead) {
+ items.add('cancel-read-all', Button.component({
+ className: 'Button Button--block',
+ onclick: () => {
+ app.request({
+ method: 'DELETE',
+ url: app.forum.attribute('apiUrl') + '/discussions/read'
+ }).then(payload => {
+ app.store.pushPayload(payload);
+ app.markedAllAsRead = false;
+ this.cancellingAllRead = false;
+ m.redraw();
+ });
+ this.cancellingAllRead = true;
+ },
+ loading: this.cancellingAllRead,
+ children: app.translator.trans('core.forum.index.mark_all_as_read_done')
+ }));
+ }
+
+ return items;
+ }
+
view() {
const params = this.props.params;
let loading;
@@ -64,6 +92,7 @@ export default class DiscussionList extends Component {
return (
+ {listItems(this.aboveDiscussions().toArray())}
{this.discussions.map(discussion => {
return (
-
diff --git a/js/src/forum/components/IndexPage.js b/js/src/forum/components/IndexPage.js
index 5970394a68..b0ae0ebc16 100644
--- a/js/src/forum/components/IndexPage.js
+++ b/js/src/forum/components/IndexPage.js
@@ -368,10 +368,18 @@ export default class IndexPage extends Page {
* @return void
*/
markAllAsRead() {
- const confirmation = confirm(app.translator.trans('core.forum.index.mark_all_as_read_confirmation'));
-
- if (confirmation) {
- app.session.user.save({markedAllAsReadAt: new Date()});
- }
+ app.request({
+ method: 'POST',
+ url: app.forum.attribute('apiUrl') + '/discussions/read'
+ }).then(payload => {
+ app.store.pushPayload(payload);
+ app.markedAllAsRead = true;
+ m.redraw();
+
+ setTimeout(() => {
+ app.markedAllAsRead = false;
+ m.redraw();
+ }, 10000);
+ });
}
}
diff --git a/src/Api/Controller/ReadAllDiscussionsCancelController.php b/src/Api/Controller/ReadAllDiscussionsCancelController.php
new file mode 100644
index 0000000000..08a9b4cdef
--- /dev/null
+++ b/src/Api/Controller/ReadAllDiscussionsCancelController.php
@@ -0,0 +1,68 @@
+users = $users;
+ }
+
+ /**
+ * Get the data to be serialized and assigned to the response document.
+ *
+ * @param ServerRequestInterface $request
+ * @param Document $document
+ * @return mixed
+ * @throws ValidationException
+ */
+ protected function data(ServerRequestInterface $request, Document $document)
+ {
+ /**
+ * @var $actor User
+ */
+ $actor = $request->getAttribute('actor');
+
+ /**
+ * @var $session Store
+ */
+ $session = $request->getAttribute('session');
+
+ if ($session->has('can_cancel_marked_all_until') && Carbon::now()->isBefore($session->get('can_cancel_marked_all_until'))) {
+ $actor->marked_all_as_read_at = $session->get('previous_marked_all_as_read_at');
+ $actor->save();
+ } else {
+ throw new ValidationException([
+ 'something' => [
+ app(TranslatorInterface::class)->trans('core.api.too_late_to_cancel_read_all'),
+ ],
+ ]);
+ }
+
+ return $this->users->findOrFail($actor->id, $actor);
+ }
+}
diff --git a/src/Api/Controller/ReadAllDiscussionsController.php b/src/Api/Controller/ReadAllDiscussionsController.php
new file mode 100644
index 0000000000..714acd3233
--- /dev/null
+++ b/src/Api/Controller/ReadAllDiscussionsController.php
@@ -0,0 +1,61 @@
+users = $users;
+ }
+
+ /**
+ * Get the data to be serialized and assigned to the response document.
+ *
+ * @param ServerRequestInterface $request
+ * @param Document $document
+ * @return mixed
+ */
+ protected function data(ServerRequestInterface $request, Document $document)
+ {
+ /**
+ * @var $actor User
+ */
+ $actor = $request->getAttribute('actor');
+
+ /**
+ * @var $session Store
+ */
+ $session = $request->getAttribute('session');
+
+ $session->put('previous_marked_all_as_read_at', $actor->marked_all_as_read_at);
+ $session->put('can_cancel_marked_all_until', Carbon::now()->addSeconds(15));
+ $session->save();
+
+ $actor->markAllAsRead();
+ $actor->save();
+
+ return $this->users->findOrFail($actor->id, $actor);
+ }
+}
diff --git a/src/Api/routes.php b/src/Api/routes.php
index 6af843c844..b133da6d6b 100644
--- a/src/Api/routes.php
+++ b/src/Api/routes.php
@@ -144,6 +144,20 @@
$route->toController(Controller\CreateDiscussionController::class)
);
+ // Mark all discussions as read
+ $map->post(
+ '/discussions/read',
+ 'discussions.readAll',
+ $route->toController(Controller\ReadAllDiscussionsController::class)
+ );
+
+ // Cancel marking all discussions as read
+ $map->delete(
+ '/discussions/read',
+ 'discussions.cancelReadAll',
+ $route->toController(Controller\ReadAllDiscussionsCancelController::class)
+ );
+
// Show a single discussion
$map->get(
'/discussions/{id}',
diff --git a/src/User/Command/EditUserHandler.php b/src/User/Command/EditUserHandler.php
index 64e539d479..4e606878f6 100644
--- a/src/User/Command/EditUserHandler.php
+++ b/src/User/Command/EditUserHandler.php
@@ -98,11 +98,6 @@ public function handle(EditUser $command)
$validate['password'] = $attributes['password'];
}
- if (! empty($attributes['markedAllAsReadAt'])) {
- $this->assertPermission($isSelf);
- $user->markAllAsRead();
- }
-
if (! empty($attributes['preferences'])) {
$this->assertPermission($isSelf);