Browse Source

Display "refresh page" message when auth goes out of sync between tabs

Rafał Pitoń 10 years ago
parent
commit
63e3f54ad0

+ 6 - 0
misago/emberapp/app/components/desync-message.js

@@ -0,0 +1,6 @@
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  classNames: 'desync-message',
+  classNameBindings: ['auth.needsSync:visible']
+});

+ 1 - 1
misago/emberapp/app/initializers/ajax-service.js

@@ -1,6 +1,6 @@
 import AjaxService from 'misago/services/ajax';
 
-export function initialize(_container, application) {
+export function initialize(container, application) {
   application.register('service:ajax', AjaxService, { singleton: true });
 
   application.inject('service:ajax', 'store', 'store:main');

+ 2 - 0
misago/emberapp/app/initializers/auth-service.js

@@ -10,6 +10,7 @@ export function initialize(container, application) {
 
   application.inject('service:auth', 'isAuthenticated', 'misago:isAuthenticated');
   application.inject('service:auth', 'user', 'misago:user');
+  application.inject('service:auth', 'session', 'store:local');
 
   application.inject('route', 'auth', 'service:auth');
   application.inject('controller', 'auth', 'service:auth');
@@ -18,5 +19,6 @@ export function initialize(container, application) {
 
 export default {
   name: 'auth-service',
+  after: 'local-store',
   initialize: initialize
 };

+ 10 - 0
misago/emberapp/app/initializers/local-store.js

@@ -0,0 +1,10 @@
+import LocalStore from 'misago/services/local-store';
+
+export function initialize(container, application) {
+  application.register('store:local', LocalStore, { singleton: true });
+}
+
+export default {
+  name: 'local-store',
+  initialize: initialize
+};

+ 1 - 1
misago/emberapp/app/initializers/toast-message-service.js

@@ -1,6 +1,6 @@
 import ToastMessageService from 'misago/services/toast-message';
 
-export function initialize(_container, application) {
+export function initialize(container, application) {
   application.register('service:toast-message', ToastMessageService, { singleton: true });
 
   [ 'route', 'controller', 'component' ].forEach((factory) => {

+ 25 - 0
misago/emberapp/app/services/auth.js

@@ -1,9 +1,34 @@
 import Ember from 'ember';
 
 export default Ember.Service.extend({
+  // State synchronization across tabs
+
+  needsSync: false, // becomes true if auth state between tabs differs
+  syncToUser: false, // becomes user obj to which we want to sync or none for anon
+
+  syncSession: function() {
+    this.session.setItem('auth-user', this.get('user'));
+    this.session.setItem('auth-is-authenticated', this.get('isAuthenticated'));
+
+    var self = this;
+    this.session.watchItem('auth-is-authenticated', function(isAuthenticated) {
+      if (!self.get('needsSync')) {
+        // display annoying "you were desynced" message
+        self.set('needsSync', true);
+
+        if (isAuthenticated) {
+          self.set('syncToUser', Ember.Object.create(self.session.getItem('auth-user')));
+        }
+      }
+    });
+  }.on('init'),
+
+  // Anon/auth state
   isAnonymous: Ember.computed.not('isAuthenticated'),
 
   logout: function() {
+    this.session.setItem('auth-user', false);
+    this.session.setItem('auth-is-authenticated', false);
     Ember.$('#hidden-logout-form').submit();
   },
 

+ 41 - 0
misago/emberapp/app/services/local-store.js

@@ -0,0 +1,41 @@
+import Ember from 'ember';
+
+export default Ember.Service.extend({
+  _storage: window.localStorage,
+  _prefix: '_misago_',
+  _watchers: null,
+
+  _initWatches: function() {
+    this.set('_watchers', []);
+
+    var self = this;
+    window.addEventListener('storage', function(e) {
+      Ember.$.each(self.get('_watchers'), function(i, watcher) {
+        if (watcher.keyName === e.key) {
+          watcher.callback(e.newValue);
+        }
+      });
+    });
+  }.on('init'),
+
+  prefixKey: function(keyName) {
+    return this.get('_prefix') + keyName;
+  },
+
+  setItem: function(keyName, value) {
+    this.get('_storage').setItem(this.prefixKey(keyName), JSON.stringify(value));
+  },
+
+  getItem: function(keyName) {
+    var itemJson = this.get('_storage').getItem(this.prefixKey(keyName));
+    if (itemJson) {
+      return JSON.parse(itemJson);
+    } else {
+      return null;
+    }
+  },
+
+  watchItem: function(keyName, callback) {
+    this.get('_watchers').push({keyName: this.prefixKey(keyName), callback: callback});
+  }
+});

+ 23 - 0
misago/emberapp/app/styles/misago/desync-message.less

@@ -0,0 +1,23 @@
+//
+// Desync Message
+// --------------------------------------------------
+
+
+.desync-message {
+  background: @alert-desync-bg;
+  padding: @line-height-computed * 1.5 0px;
+  .box-shadow(0px 0px 3px fadeOut(#333, 70%));
+
+  position: fixed;
+  top: -100%;
+  width: 100%;
+  z-index: @zindex-modal + 20;
+
+  color: @alert-desync-text;
+  text-align: center;
+
+  &.visible {
+    top: 0px;
+    transition: top 200ms ease;
+  }
+}

+ 1 - 0
misago/emberapp/app/styles/misago/misago.less

@@ -2,6 +2,7 @@
 @import "buttons.less";
 @import "dropdowns.less";
 @import "toast-message.less";
+@import "desync-message.less";
 @import "footer.less";
 @import "forms.less";
 @import "inputs.less";

+ 1 - 1
misago/emberapp/app/styles/misago/toast-message.less

@@ -7,7 +7,7 @@
   background: @color-bg;
   border: 1px solid @color-border;
   border-top: none;
-  .box-shadow(0px 2px 3px fadeOut(@gray-darker, 70%));
+  .box-shadow(0px 0px 3px fadeOut(#333, 70%));
   padding: @alert-padding;
 
   color: @color-text;

+ 4 - 0
misago/emberapp/app/styles/misago/variables.less

@@ -178,6 +178,10 @@
 @alert-danger-border:         #ef5350;
 @alert-danger-text:           #fff;
 
+//** Special non-dimissible alert used to make user refresh page
+@alert-desync-bg:             fadeOut(@body-bg, 5%);
+@alert-desync-text:           @text-color;
+
 
 //== Form states and alerts
 //

+ 1 - 0
misago/emberapp/app/templates/application.hbs

@@ -1,5 +1,6 @@
 {{forum-navbar}}
 {{toast-message}}
+{{desync-message}}
 
 <div class="main-outlet">
   {{outlet}}

+ 15 - 0
misago/emberapp/app/templates/components/desync-message.hbs

@@ -0,0 +1,15 @@
+<div class="container">
+
+  {{#if auth.syncToUser}}
+  <p class="lead">{{gettext "You have logged in as %(username)s. Please refresh this page before continuing." username=auth.syncToUser.username}}</p>
+  {{else}}
+  <p class="lead">{{gettext "%(username)s, you have been logged out. Please refresh this page before continuing." username=auth.user.username}}</p>
+  {{/if}}
+
+  <p>
+    {{#refresh-button class="btn btn-outlined btn-default"}}
+      {{gettext "Refresh page"}}
+    {{/refresh-button}} <span class="hidden-xs hidden-sm text-muted">{{gettext "or press F5 key."}}</span>
+  </p>
+
+</div>