FEATURE: live refresh notifications as they happen
This commit is contained in:
parent
8bc7423045
commit
a54e8f3c5e
|
@ -50,10 +50,20 @@ export default Ember.Component.extend({
|
||||||
// TODO: It's a bit odd to use the store in a component, but this one really
|
// TODO: It's a bit odd to use the store in a component, but this one really
|
||||||
// wants to reach out and grab notifications
|
// wants to reach out and grab notifications
|
||||||
const store = this.container.lookup('store:main');
|
const store = this.container.lookup('store:main');
|
||||||
const stale = store.findStale('notification', {recent: true, limit });
|
const stale = store.findStale('notification', {recent: true, limit }, {storageKey: 'recent-notifications'});
|
||||||
|
|
||||||
if (stale.hasResults) {
|
if (stale.hasResults) {
|
||||||
this.set('notifications', stale.results);
|
const results = stale.results;
|
||||||
|
var content = results.get('content');
|
||||||
|
|
||||||
|
// we have to truncate to limit, otherwise we will render too much
|
||||||
|
if (content && (content.length > limit)) {
|
||||||
|
content = content.splice(0, limit);
|
||||||
|
results.set('content', content);
|
||||||
|
results.set('totalRows', limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('notifications', results);
|
||||||
} else {
|
} else {
|
||||||
this.set('loadingNotifications', true);
|
this.set('loadingNotifications', true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,12 @@ export default {
|
||||||
const user = container.lookup('current-user:main'),
|
const user = container.lookup('current-user:main'),
|
||||||
site = container.lookup('site:main'),
|
site = container.lookup('site:main'),
|
||||||
siteSettings = container.lookup('site-settings:main'),
|
siteSettings = container.lookup('site-settings:main'),
|
||||||
bus = container.lookup('message-bus:main');
|
bus = container.lookup('message-bus:main'),
|
||||||
|
keyValueStore = container.lookup('key-value-store:main');
|
||||||
|
|
||||||
|
// clear old cached notifications
|
||||||
|
// they could be a week old for all we know
|
||||||
|
keyValueStore.remove('recent-notifications');
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
||||||
|
@ -38,6 +43,32 @@ export default {
|
||||||
if (oldUnread !== data.unread_notifications || oldPM !== data.unread_private_messages) {
|
if (oldUnread !== data.unread_notifications || oldPM !== data.unread_private_messages) {
|
||||||
user.set('lastNotificationChange', new Date());
|
user.set('lastNotificationChange', new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stale = keyValueStore.getObject('recent-notifications');
|
||||||
|
const lastNotification = data.last_notification && data.last_notification.notification;
|
||||||
|
|
||||||
|
if (stale && stale.notifications && lastNotification) {
|
||||||
|
|
||||||
|
const oldNotifications = stale.notifications;
|
||||||
|
const staleIndex = _.findIndex(oldNotifications, {id: lastNotification.id});
|
||||||
|
|
||||||
|
if (staleIndex > -1) {
|
||||||
|
oldNotifications.splice(staleIndex, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this gets a bit tricky, uread pms are bumped to front
|
||||||
|
var insertPosition = 0;
|
||||||
|
if (lastNotification.notification_type !== 6) {
|
||||||
|
insertPosition = _.findIndex(oldNotifications, function(n){
|
||||||
|
return n.notification_type !== 6 || n.read;
|
||||||
|
});
|
||||||
|
insertPosition = insertPosition === -1 ? oldNotifications.length - 1 : insertPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
oldNotifications.splice(insertPosition, 0, lastNotification);
|
||||||
|
keyValueStore.setItem('recent-notifications', JSON.stringify(stale));
|
||||||
|
|
||||||
|
}
|
||||||
}, user.notification_channel_position);
|
}, user.notification_channel_position);
|
||||||
|
|
||||||
bus.subscribe("/categories", function(data) {
|
bus.subscribe("/categories", function(data) {
|
||||||
|
|
|
@ -43,6 +43,13 @@ KeyValueStore.prototype = {
|
||||||
get(key) {
|
get(key) {
|
||||||
if (!safeLocalStorage) { return null; }
|
if (!safeLocalStorage) { return null; }
|
||||||
return safeLocalStorage[this.context + key];
|
return safeLocalStorage[this.context + key];
|
||||||
|
},
|
||||||
|
|
||||||
|
getObject(key) {
|
||||||
|
if (!safeLocalStorage) { return null; }
|
||||||
|
try {
|
||||||
|
return JSON.parse(safeLocalStorage[this.context + key]);
|
||||||
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import StaleResult from 'discourse/lib/stale-result';
|
import StaleResult from 'discourse/lib/stale-result';
|
||||||
import { hashString } from 'discourse/lib/hash';
|
import { hashString } from 'discourse/lib/hash';
|
||||||
|
|
||||||
var skipFirst = true;
|
|
||||||
|
|
||||||
// Mix this in to an adapter to provide stale caching in our key value store
|
// Mix this in to an adapter to provide stale caching in our key value store
|
||||||
export default {
|
export default {
|
||||||
storageKey(type, findArgs) {
|
storageKey(type, findArgs) {
|
||||||
|
@ -10,27 +8,26 @@ export default {
|
||||||
return `${type}_${hashedArgs}`;
|
return `${type}_${hashedArgs}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
findStale(store, type, findArgs) {
|
findStale(store, type, findArgs, opts) {
|
||||||
const staleResult = new StaleResult();
|
const staleResult = new StaleResult();
|
||||||
|
const key = (opts && opts.storageKey) || this.storageKey(type, findArgs)
|
||||||
try {
|
try {
|
||||||
if (!skipFirst) {
|
const stored = this.keyValueStore.getItem(key);
|
||||||
const stored = this.keyValueStore.getItem(this.storageKey(type, findArgs));
|
|
||||||
if (stored) {
|
if (stored) {
|
||||||
const parsed = JSON.parse(stored);
|
const parsed = JSON.parse(stored);
|
||||||
staleResult.setResults(parsed);
|
staleResult.setResults(parsed);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
skipFirst = false;
|
|
||||||
}
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// JSON parsing error
|
// JSON parsing error
|
||||||
}
|
}
|
||||||
return staleResult;
|
return staleResult;
|
||||||
},
|
},
|
||||||
|
|
||||||
find(store, type, findArgs) {
|
find(store, type, findArgs, opts) {
|
||||||
|
const key = (opts && opts.storageKey) || this.storageKey(type, findArgs)
|
||||||
|
|
||||||
return this._super(store, type, findArgs).then((results) => {
|
return this._super(store, type, findArgs).then((results) => {
|
||||||
this.keyValueStore.setItem(this.storageKey(type, findArgs), JSON.stringify(results));
|
this.keyValueStore.setItem(key, JSON.stringify(results));
|
||||||
return results;
|
return results;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,18 +71,18 @@ export default Ember.Object.extend({
|
||||||
|
|
||||||
// See if the store can find stale data. We sometimes prefer to show stale data and
|
// See if the store can find stale data. We sometimes prefer to show stale data and
|
||||||
// refresh it in the background.
|
// refresh it in the background.
|
||||||
findStale(type, findArgs) {
|
findStale(type, findArgs, opts) {
|
||||||
const stale = this.adapterFor(type).findStale(this, type, findArgs);
|
const stale = this.adapterFor(type).findStale(this, type, findArgs, opts);
|
||||||
if (stale.hasResults) {
|
if (stale.hasResults) {
|
||||||
stale.results = this._hydrateFindResults(stale.results, type, findArgs);
|
stale.results = this._hydrateFindResults(stale.results, type, findArgs);
|
||||||
}
|
}
|
||||||
stale.refresh = () => this.find(type, findArgs);
|
stale.refresh = () => this.find(type, findArgs, opts);
|
||||||
return stale;
|
return stale;
|
||||||
},
|
},
|
||||||
|
|
||||||
find(type, findArgs) {
|
find(type, findArgs, opts) {
|
||||||
return this.adapterFor(type).find(this, type, findArgs).then((result) => {
|
return this.adapterFor(type).find(this, type, findArgs, opts).then((result) => {
|
||||||
return this._hydrateFindResults(result, type, findArgs);
|
return this._hydrateFindResults(result, type, findArgs, opts);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -306,10 +306,17 @@ class User < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def publish_notifications_state
|
def publish_notifications_state
|
||||||
|
# publish last notification json with the message so we
|
||||||
|
# can apply an update
|
||||||
|
notification = notifications.visible.order('notifications.id desc').first
|
||||||
|
json = NotificationSerializer.new(notification).as_json if notification
|
||||||
|
|
||||||
MessageBus.publish("/notification/#{id}",
|
MessageBus.publish("/notification/#{id}",
|
||||||
{unread_notifications: unread_notifications,
|
{unread_notifications: unread_notifications,
|
||||||
unread_private_messages: unread_private_messages,
|
unread_private_messages: unread_private_messages,
|
||||||
total_unread_notifications: total_unread_notifications},
|
total_unread_notifications: total_unread_notifications,
|
||||||
|
last_notification: json
|
||||||
|
},
|
||||||
user_ids: [id] # only publish the notification to this user
|
user_ids: [id] # only publish the notification to this user
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue