From 20df342c9ad28c1ca35d14f7c6523ea4cdfed5a7 Mon Sep 17 00:00:00 2001
From: Eugen Ciur <eugen@papermerge.com>
Date: Tue, 12 Apr 2022 21:22:18 +0200
Subject: [PATCH] add notification service

---
 app/app.js                                    |  9 +++++-
 app/components/notifications/index.hbs        |  5 ++++
 app/components/notifications/index.js         | 11 +++++++
 app/components/notifications/toast/index.hbs  | 21 ++++++++++++++
 app/components/notifications/toast/index.js   | 12 ++++++++
 app/components/role/edit.js                   |  1 -
 .../viewer/action_buttons/index.hbs           |  5 ++--
 app/components/viewer/index.hbs               |  1 +
 app/components/viewer/index.js                | 23 +++++++++++++--
 app/services/notify.js                        | 29 +++++++++++++++++++
 app/styles/app.scss                           |  4 +++
 app/templates/authenticated.hbs               |  1 +
 package.json                                  |  4 +--
 13 files changed, 117 insertions(+), 9 deletions(-)
 create mode 100644 app/components/notifications/index.hbs
 create mode 100644 app/components/notifications/index.js
 create mode 100644 app/components/notifications/toast/index.hbs
 create mode 100644 app/components/notifications/toast/index.js
 create mode 100644 app/services/notify.js

diff --git a/app/app.js b/app/app.js
index 45290a9..a6a2cdc 100644
--- a/app/app.js
+++ b/app/app.js
@@ -3,7 +3,7 @@ import Resolver from 'ember-resolver';
 import loadInitializers from 'ember-load-initializers';
 import config from 'papermerge/config/environment';
 import '@popperjs/core';
-import 'bootstrap';
+import bootstrap from 'bootstrap';
 
 
 export default class App extends Application {
@@ -27,6 +27,13 @@ export default class App extends Application {
     if (divs.length > 0) {
       divs[0].style.display = 'None';
     }
+
+    /*Initializer bootstrap toasts*/
+    let toastElList = [].slice.call(document.querySelectorAll('.toast'))
+    toastElList.map(function (toastEl) {
+      return new bootstrap.Toast(toastEl)
+    })
+
   }
 }
 
diff --git a/app/components/notifications/index.hbs b/app/components/notifications/index.hbs
new file mode 100644
index 0000000..f6cc4c6
--- /dev/null
+++ b/app/components/notifications/index.hbs
@@ -0,0 +1,5 @@
+<div class="toast-container position-fixed top-0 end-0 p-3">
+  {{#each this.notifications as |notification|}}
+    <Notifications::Toast @notification={{notification}} />
+  {{/each}}
+</div>
diff --git a/app/components/notifications/index.js b/app/components/notifications/index.js
new file mode 100644
index 0000000..e025c2d
--- /dev/null
+++ b/app/components/notifications/index.js
@@ -0,0 +1,11 @@
+import Component from '@glimmer/component';
+import { service } from '@ember/service';
+
+
+export default class NotificationsComponent extends Component {
+  @service notify;
+
+  get notifications() {
+    return this.notify.notifications;
+  }
+}
\ No newline at end of file
diff --git a/app/components/notifications/toast/index.hbs b/app/components/notifications/toast/index.hbs
new file mode 100644
index 0000000..ac47068
--- /dev/null
+++ b/app/components/notifications/toast/index.hbs
@@ -0,0 +1,21 @@
+<div class="toast show" role="alert" aria-live="assertive" aria-atomic="true">
+  <div class="toast-header">
+    {{#if (is_equal this.type 'info') }}
+      <strong class="me-auto text-primary">
+        <i class='bi bi-info-circle mx-2'></i>Info
+      </strong>
+    {{else if (is_equal this.type 'error')}}
+      <strong class="me-auto text-danger">
+        <i class='bi bi-info-circle mx-2'></i>Error
+      </strong>
+    {{else}}
+      <strong class="me-auto text-warning">
+        <i class='bi bi-info-circle mx-2'></i>Warning
+      </strong>
+    {{/if}}
+    <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
+  </div>
+  <div class="toast-body">
+    {{this.message}}
+  </div>
+</div>
diff --git a/app/components/notifications/toast/index.js b/app/components/notifications/toast/index.js
new file mode 100644
index 0000000..03f0ff4
--- /dev/null
+++ b/app/components/notifications/toast/index.js
@@ -0,0 +1,12 @@
+import Component from '@glimmer/component';
+
+
+export default class ToastComponent extends Component {
+  get type() {
+    return this.args.notification.type;
+  }
+
+  get message() {
+    return this.args.notification.message;
+  }
+}
\ No newline at end of file
diff --git a/app/components/role/edit.js b/app/components/role/edit.js
index 4096a36..2ff0d1c 100644
--- a/app/components/role/edit.js
+++ b/app/components/role/edit.js
@@ -1,6 +1,5 @@
 import Component from '@glimmer/component';
 import { action } from '@ember/object';
-import { tracked } from '@glimmer/tracking';
 import { inject as service } from '@ember/service';
 import { group_perms_by_model } from 'papermerge/utils';
 
diff --git a/app/components/viewer/action_buttons/index.hbs b/app/components/viewer/action_buttons/index.hbs
index e52acb6..67102cd 100644
--- a/app/components/viewer/action_buttons/index.hbs
+++ b/app/components/viewer/action_buttons/index.hbs
@@ -31,10 +31,11 @@
   {{#if @page_order_changed }}
     Page order changed.
     <button
-      class="btn btn-success"
+      class="btn btn-success pe-4"
       type="button"
       {{on "click" @onPageOrderApply}}>
-      Apply Changes
+        <Spinner @inProgress={{@apply_page_order_changes_in_progress}} />
+        Apply Changes
     </button>
     <button
       class="btn btn-secondary"
diff --git a/app/components/viewer/index.hbs b/app/components/viewer/index.hbs
index 1925961..87dccdc 100644
--- a/app/components/viewer/index.hbs
+++ b/app/components/viewer/index.hbs
@@ -6,6 +6,7 @@
       @ocrStatus={{this.ocrStatus}}
       @selectedPages={{this.selected_pages}}
       @page_order_changed={{this.page_order_changed}}
+      @apply_page_order_changes_in_progress={{this.apply_page_order_changes_in_progress}}
       @onPageOrderApply={{this.onPageOrderApply}}
       @onPageOrderDiscard={{this.onPageOrderDiscard}}
       @openConfirmDeletionModal={{this.openConfirmDeletionModal}}
diff --git a/app/components/viewer/index.js b/app/components/viewer/index.js
index 3afae65..ec2e4a2 100644
--- a/app/components/viewer/index.js
+++ b/app/components/viewer/index.js
@@ -23,6 +23,7 @@ export default class ViewerComponent extends Component {
   @service websockets;
   @service store;
   @service requests;
+  @service notify;
   @service router;
 
   @tracked ocr_status = null;
@@ -38,6 +39,7 @@ export default class ViewerComponent extends Component {
   @tracked show_confirm_pages_deletion_modal = false;
   @tracked show_rename_node_modal = false;
   @tracked page_order_changed = false;
+  @tracked apply_page_order_changes_in_progress = false;
 
   initial_pages_memo = A([]);
 
@@ -254,11 +256,26 @@ export default class ViewerComponent extends Component {
 
   @action
   async onPageOrderApply() {
-    await this.requests.reorderPagesApply({
+    this.apply_page_order_changes_in_progress = true;
+    this.requests.reorderPagesApply({
       old_items: this.initial_pages_memo,
       new_items: this.pages
-    });
-    this.router.refresh();
+    }).then(
+      () => { // on success
+        this.apply_page_order_changes_in_progress = false;
+        this.notify.info(
+          'New page order successfully applied'
+        );
+        this.page_order_changed = false;
+      },
+      () => { // on failure
+        this.apply_page_order_changes_in_progress = false;
+        this.notify.error(
+          'There was a problem while saving new page order'
+        );
+        this.page_order_changed = false;
+      }
+    );
   }
 
   @action
diff --git a/app/services/notify.js b/app/services/notify.js
new file mode 100644
index 0000000..0c8df9b
--- /dev/null
+++ b/app/services/notify.js
@@ -0,0 +1,29 @@
+import Service from '@ember/service';
+import { tracked } from 'tracked-built-ins';
+import { TrackedArray } from 'tracked-built-ins';
+
+
+export default class Notify extends Service {
+  /*
+    Push notifications to your visitors with a toast, a lightweight and easily customizable alert message.
+  */
+  @tracked notifications = new TrackedArray([]);
+
+  info(message) {
+    let type = 'info';
+
+    this.notifications.push({message, type});
+  }
+
+  warning(message) {
+    let type = 'warning';
+
+    this.notifications.push({message, type});
+  }
+
+  error(message) {
+    let type = 'error';
+
+    this.notifications.push({message, type});
+  }
+}
diff --git a/app/styles/app.scss b/app/styles/app.scss
index 62e279d..3bd27af 100644
--- a/app/styles/app.scss
+++ b/app/styles/app.scss
@@ -73,3 +73,7 @@ main {
 .droparea {
   outline: 1px solid green;
 }
+
+.toast-container {
+  z-index: 1000;
+}
\ No newline at end of file
diff --git a/app/templates/authenticated.hbs b/app/templates/authenticated.hbs
index 56b274a..a28f426 100644
--- a/app/templates/authenticated.hbs
+++ b/app/templates/authenticated.hbs
@@ -6,6 +6,7 @@
     @pinned_tags={{@model.pinned_tags}}
     @expanded={{this.expanded_sidebar}} />
   <div class="w-100 central-bar">
+    <Notifications />
     <Nav::Topbar
       @onSidebarToggle={{this.onSidebarToggle}} />
     <div class="container-fluid mx-2 my-1">
diff --git a/package.json b/package.json
index f208e37..d26c936 100644
--- a/package.json
+++ b/package.json
@@ -69,7 +69,8 @@
     "prettier": "^2.5.1",
     "qunit": "^2.17.2",
     "qunit-dom": "^2.0.0",
-    "sass": "^1.47.0"
+    "sass": "^1.47.0",
+    "tracked-built-ins": "^2.0.1"
   },
   "engines": {
     "node": "12.* || 14.* || >= 16"
@@ -78,7 +79,6 @@
     "edition": "octane"
   },
   "devDependencies": {
-    "tracked-built-ins": "^2.0.1",
     "webpack": "^5.65.0"
   }
 }
-- 
GitLab