Skip to content
This repository has been archived by the owner on Jan 16, 2018. It is now read-only.

Commit

Permalink
Fix #42 Update timeline realtime by implementing BMO push notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
kyoshino committed Jun 29, 2014
1 parent f92396c commit 88c262d
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 6 deletions.
164 changes: 164 additions & 0 deletions webroot/static/scripts/bug.js
Expand Up @@ -197,6 +197,37 @@ BzDeck.bug.set_bug_tooltips = function ($bug, bug) {
}
};

BzDeck.bug.update = function ($bug, bug, changes) {
let $timeline = $bug.querySelector('.bug-timeline');

if ($timeline) {
let $parent = $timeline.querySelector('section, .scrollable-area-content'),
$entry = BzDeck.timeline.create_entry($timeline.id, changes);

if (BzDeck.data.prefs['ui.timeline.sort.order'] === 'descending') {
$parent.insertBefore($entry, $timeline.querySelector('[itemprop="comment"]'));
} else {
$parent.appendChild($entry);
}
}

if (changes.has('attachment') && $bug.querySelector('[data-field="attachments"]')) {
BzDeck.DetailsPage.attachments.render($bug, [changes.get('attachment')], true);
}

if (changes.has('history') && $bug.querySelector('[data-field="history"]')) {
let _bug = { 'id': bug.id, '_update_needed': true };

// Prep partial data
for (let change in changes.get('history').changes) {
_bug[change.field_name] = bug[change.field_name];
}

BzDeck.bug.fill_data($bug, _bug, true);
BzDeck.DetailsPage.history.render($bug, [changes.get('history')], true);
}
};

/* ----------------------------------------------------------------------------------------------
* Timeline
* ---------------------------------------------------------------------------------------------- */
Expand Down Expand Up @@ -518,3 +549,136 @@ BzDeck.timeline.handle_keydown = function (event) {

return FlareTail.util.event.ignore(event);
};

/* ----------------------------------------------------------------------------------------------
* Bugzilla Push Notifications support
* https://wiki.mozilla.org/BMO/ChangeNotificationSystem
* ---------------------------------------------------------------------------------------------- */

BzDeck.bugzfeed = {
subscription: new Set()
};

BzDeck.bugzfeed.connect = function () {
let endpoint = BzDeck.options.api.endpoints.websocket;

if (!endpoint || !navigator.onLine) {
return;
}

this.websocket = new WebSocket(endpoint);

this.websocket.addEventListener('open', event => {
if (this.reconnector) {
window.clearInterval(this.reconnector);
}

// Subscribe bugs once (re)connected
if (this.subscription.size) {
this.subscribe([...this.subscription]);
}
});

this.websocket.addEventListener('close', event => {
// Try to reconnect every 30 seconds when unexpectedly disconnected
if (event.code !== 1000) {
this.reconnector = window.setInterval(() => this.connect(), 30000);
}
});

this.websocket.addEventListener('message', event => {
let message = JSON.parse(event.data)

if (message.command === 'update') {
this.get_changes(message);
}
});
};

BzDeck.bugzfeed.send = function (command, bugs) {
if (this.websocket.readyState === 1) {
this.websocket.send(JSON.stringify({ 'command': command, 'bugs': bugs }));
}
};

BzDeck.bugzfeed.subscribe = function (bugs) {
for (let bug of bugs) {
this.subscription.add(bug);
}

this.send('subscribe', bugs);
};

BzDeck.bugzfeed.unsubscribe = function (bugs) {
for (let bug of bugs) {
this.subscription.delete(bug);
}

this.send('unsubscribe', bugs);
};

BzDeck.bugzfeed.get_changes = function (message) {
let api = BzDeck.options.api,
id = message.bug,
time = new Date(message.when),
params = new URLSearchParams();

params.append('include_fields', [...api.default_fields, ...api.extra_fields].join());
params.append('exclude_fields', 'attachments.data');

BzDeck.core.request('GET', 'bug/' + id, params, null, bug => {
if (!bug || !bug.comments) {
return;
}

let get_change = (field, time_field = 'creation_time') =>
[for (item of bug[field] || []) if (new Date(item[time_field]) - time === 0) item][0],
changes = new Map(),
comment = get_change('comments'),
attachment = get_change('attachments'),
history = get_change('history', 'change_time');

if (comment) {
changes.set('comment', comment);
}

if (attachment) {
changes.set('attachment', attachment);
}

if (history) {
changes.set('history', history);
}

this.save_changes(bug, changes);

FlareTail.util.event.dispatch(window, 'bug:updated', { 'detail': {
'bug': bug,
'changes': changes
}});
});
};

BzDeck.bugzfeed.save_changes = function (bug, changes) {
BzDeck.model.get_bug_by_id(bug.id, cache => {
if (changes.has('comment')) {
cache.comments.push(changes.get('comment'));
}

if (changes.has('attachment')) {
cache.attachments = cache.attachments || [];
cache.attachments.push(changes.get('attachment'));
}

if (changes.has('history')) {
cache.history = cache.history || [];
cache.history.push(changes.get('history'));

for (let change in changes.get('history').changes) {
cache[change.field_name] = bug[change.field_name];
}
}

BzDeck.model.save_bug(cache);
});
};
10 changes: 8 additions & 2 deletions webroot/static/scripts/bzdeck.js
Expand Up @@ -19,7 +19,10 @@ BzDeck.data = {};

BzDeck.options = {
api: {
endpoint: 'https://api-dev.bugzilla.mozilla.org/latest/',
endpoints: {
rest: 'https://api-dev.bugzilla.mozilla.org/latest/',
websocket: 'ws://bugzfeed.mozilla.org/'
},
extra_fields: [
'attachments', 'blocks', 'cc', 'comments', 'depends_on', 'dupe_of', 'flags', 'groups',
'history', 'is_cc_accessible', 'is_confirmed', 'is_creator_accessible', 'see_also',
Expand Down Expand Up @@ -401,6 +404,9 @@ BzDeck.bootstrap.finish = function () {
// Register the app for an activity on Firefox OS
BzDeck.global.register_activity_handler();

// Connect to the push notification server
BzDeck.bugzfeed.connect();

BzDeck.global.show_status('Loading complete.'); // l10n
BzDeck.session.login();
this.processing = false;
Expand Down Expand Up @@ -753,7 +759,7 @@ BzDeck.core.request = function (method, path, params, data, callback, auth = fal
}

let xhr = new XMLHttpRequest(),
url = new URL(BzDeck.options.api.endpoint);
url = new URL(BzDeck.options.api.endpoints.rest);

params = params || new URLSearchParams();

Expand Down
8 changes: 8 additions & 0 deletions webroot/static/scripts/details.js
Expand Up @@ -42,6 +42,8 @@ BzDeck.DetailsPage = function (id, bug_list = []) {

this.open(bug);
});

BzDeck.bugzfeed.subscribe([id]);
};

BzDeck.DetailsPage.prototype.open = function (bug, bug_list = []) {
Expand Down Expand Up @@ -77,6 +79,12 @@ BzDeck.DetailsPage.prototype.open = function (bug, bug_list = []) {
$tabpanel.querySelector('[role="checkbox"][data-field="_starred"]')
.setAttribute('aria-checked', event.detail.ids.has(bug.id));
});

window.addEventListener('bug:updated', event => {
if ($tabpanel && this.data.id === event.detail.bug.id) {
BzDeck.bug.update(this.view.$bug, event.detail.bug, event.detail.changes);
}
});
};

BzDeck.DetailsPage.prototype.prep_tabpanel = function (bug) {
Expand Down
9 changes: 9 additions & 0 deletions webroot/static/scripts/home.js
Expand Up @@ -189,6 +189,8 @@ BzDeck.HomePage = function () {
FlareTail.util.event.async(() => {
this.show_preview(oldval, newval);
});

BzDeck.bugzfeed.subscribe([newval]);
}

obj[prop] = newval;
Expand All @@ -213,6 +215,13 @@ BzDeck.HomePage = function () {
$row.setAttribute('data-unread', event.detail.ids.has(Number.parseInt($row.dataset.id)));
}
});

window.addEventListener('bug:updated', event => {
if (this.data.preview_id === event.detail.bug.id) {
BzDeck.bug.update(document.querySelector('#home-preview-bug'),
event.detail.bug, event.detail.changes);
}
});
};

BzDeck.HomePage.prototype.show_preview = function (oldval, newval) {
Expand Down
18 changes: 14 additions & 4 deletions webroot/static/scripts/search.js
Expand Up @@ -51,10 +51,14 @@ BzDeck.SearchPage = function () {
return;
}

if (prop === 'preview_id' && !FlareTail.util.device.type.startsWith('mobile')) {
FlareTail.util.event.async(() => {
this.show_preview(oldval, newval);
});
if (prop === 'preview_id') {
if (!FlareTail.util.device.type.startsWith('mobile')) {
FlareTail.util.event.async(() => {
this.show_preview(oldval, newval);
});
}

BzDeck.bugzfeed.subscribe([newval]);
}

obj[prop] = newval;
Expand Down Expand Up @@ -91,6 +95,12 @@ BzDeck.SearchPage = function () {
$tabpanel.querySelector('[role="article"] [role="checkbox"][data-field="_starred"]')
.setAttribute('aria-checked', event.detail.ids.has(this.data.preview_id));
});

window.addEventListener('bug:updated', event => {
if ($tabpanel && this.data.preview_id === event.detail.bug.id) {
BzDeck.bug.update($tabpanel.querySelector('article'), event.detail.bug, event.detail.changes);
}
});
};

BzDeck.SearchPage.prototype.setup_toolbar = function () {
Expand Down

0 comments on commit 88c262d

Please sign in to comment.