Add channel store
This commit is contained in:
parent
e63d8f0305
commit
c3a24ee300
7 changed files with 189 additions and 81 deletions
|
@ -63,6 +63,7 @@
|
||||||
<script type="text/javascript" src="/js/util.js"></script>
|
<script type="text/javascript" src="/js/util.js"></script>
|
||||||
<script type="text/javascript" src="/js/pubsub.js"></script>
|
<script type="text/javascript" src="/js/pubsub.js"></script>
|
||||||
<script type="text/javascript" src="/js/localstorage_credentials.js"></script>
|
<script type="text/javascript" src="/js/localstorage_credentials.js"></script>
|
||||||
|
<script type="text/javascript" src="/js/store.js"></script>
|
||||||
<script type="text/javascript" src="/js/mm_client.js"></script>
|
<script type="text/javascript" src="/js/mm_client.js"></script>
|
||||||
<script type="text/javascript" src="/js/view/messages.js"></script>
|
<script type="text/javascript" src="/js/view/messages.js"></script>
|
||||||
<script type="text/javascript" src="/js/view/sidebar.js"></script>
|
<script type="text/javascript" src="/js/view/sidebar.js"></script>
|
||||||
|
|
|
@ -62,33 +62,32 @@ function logOut(endpoint, button) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function channelNameElements(team, channel) {
|
function channelNameElements(team, channel) {
|
||||||
|
const teamName = team ? team.name : "";
|
||||||
|
const inTeam = teamName ? " in team " + teamName : "";
|
||||||
let icon = "";
|
let icon = "";
|
||||||
let teamName = team.name;
|
|
||||||
let channelName = channel.name;
|
let channelName = channel.name;
|
||||||
let title = "";
|
let title = "";
|
||||||
|
|
||||||
switch (channel.type) {
|
switch (channel.type) {
|
||||||
case "O": // Public channel
|
case "O": // Public channel
|
||||||
title = `${channel.name} in team ${team.name} (public)`;
|
title = `${channel.name}${inTeam} (public)`;
|
||||||
break;
|
break;
|
||||||
case "P": // Private channel
|
case "P": // Private channel
|
||||||
icon = "🔒";
|
icon = "🔒";
|
||||||
title = `${channel.name} in team ${team.name} (private)`;
|
title = `${channel.name}${inTeam} (private)`;
|
||||||
break;
|
break;
|
||||||
case "D": // Direct message
|
case "D": // Direct message
|
||||||
return undefined; // XXX Because they clutter the list
|
return undefined; // XXX Because they clutter the list
|
||||||
teamName = "";
|
|
||||||
channelName = `👤 ...`;
|
channelName = `👤 ...`;
|
||||||
title = `Direct message`;
|
title = `Direct message`;
|
||||||
break;
|
break;
|
||||||
case "G": // Group chat
|
case "G": // Group chat
|
||||||
teamName = "";
|
|
||||||
channelName = `👥 ${channel.display_name}`;
|
channelName = `👥 ${channel.display_name}`;
|
||||||
title = `Group chat with ${channel.display_name}`;
|
title = `Group chat with ${channel.display_name}`;
|
||||||
break;
|
break;
|
||||||
default: // Unsupported
|
default: // Unsupported
|
||||||
icon = channel.type;
|
icon = channel.type;
|
||||||
title = `${channel.name} in team ${team.name} (type ${channel.type})`;
|
title = `${channel.name}${inTeam} (type ${channel.type})`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,16 +209,19 @@ function checkKeyPress(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pubsub.subscribe("MESSAGES_NEW", post => {
|
pubsub.subscribe("MESSAGES_NEW", post => {
|
||||||
if (!window.hasFocus) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const curChan = currentChannel();
|
const curChan = currentChannel();
|
||||||
if (post.endpoint === curChan.endpoint && post.channel_id === curChan.channel_id) {
|
const client = mm_client.getOrCreate(post.endpoint);
|
||||||
mm_client.getOrCreate(post.endpoint).markChannelRead(post.channel_id);
|
if (window.hasFocus && post.endpoint === curChan.endpoint && post.channel_id === curChan.channel_id) {
|
||||||
|
client.markChannelRead(post.channel_id);
|
||||||
|
} else {
|
||||||
|
client.channelStore.increaseUnread(post);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pubsub.subscribe("CHANNEL_READ", ({endpoint, channel_id}) => {
|
||||||
|
mm_client.getOrCreate(endpoint).channelStore.setUnread(channel_id, 0, 0);
|
||||||
|
});
|
||||||
|
|
||||||
pubsub.subscribe("WINDOW_FOCUSED", () => {
|
pubsub.subscribe("WINDOW_FOCUSED", () => {
|
||||||
const curChan = currentChannel();
|
const curChan = currentChannel();
|
||||||
if (!curChan.channel_id) return;
|
if (!curChan.channel_id) return;
|
||||||
|
|
|
@ -9,6 +9,8 @@ class MattermostClient {
|
||||||
const creds = this.credentials.get(this.endpoint);
|
const creds = this.credentials.get(this.endpoint);
|
||||||
this.token = creds ? creds.token : null;
|
this.token = creds ? creds.token : null;
|
||||||
console.info(`Created MattermostClient for ${this.endpoint}, ${this.token ? "found token" : "did not find token"}`);
|
console.info(`Created MattermostClient for ${this.endpoint}, ${this.token ? "found token" : "did not find token"}`);
|
||||||
|
|
||||||
|
this.channelStore = new store.ChannelStore(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(path, queryParams) {
|
async get(path, queryParams) {
|
||||||
|
@ -56,11 +58,16 @@ class MattermostClient {
|
||||||
start() {
|
start() {
|
||||||
assert(this.token);
|
assert(this.token);
|
||||||
|
|
||||||
let _ = this.getUsers();
|
const users = this.getUsers();
|
||||||
return this.userMe().then(data => {
|
const me = this.userMe().then(data => {this.me = data;});
|
||||||
this.me = data;
|
const channels = this.channelStore.get();
|
||||||
this.websocket();
|
|
||||||
});
|
return Promise.all([
|
||||||
|
users,
|
||||||
|
me,
|
||||||
|
channels
|
||||||
|
])
|
||||||
|
.then(() => {this.websocket();});
|
||||||
}
|
}
|
||||||
|
|
||||||
async loggedIn() {
|
async loggedIn() {
|
||||||
|
|
|
@ -3,8 +3,9 @@ const pubsub = (function() { "use strict";
|
||||||
const topics = [
|
const topics = [
|
||||||
"MESSAGES_NEW", // {endpoint, channel_id, create_at, user_id, message}
|
"MESSAGES_NEW", // {endpoint, channel_id, create_at, user_id, message}
|
||||||
"MESSAGES_CHANGED",
|
"MESSAGES_CHANGED",
|
||||||
"CHANNELS_NEW",
|
"CHANNELS_RELOADED", //
|
||||||
"CHANNELS_CHANGED",
|
"CHANNEL_UPDATED", // {endpoint, channel}
|
||||||
|
"CHANNEL_UNREAD_UPDATED", // {endpoint, channel_id, unread, mentions}
|
||||||
"USERS_NEW",
|
"USERS_NEW",
|
||||||
"USERS_CHANGED",
|
"USERS_CHANGED",
|
||||||
"CHANNEL_MEMBERS_NEW",
|
"CHANNEL_MEMBERS_NEW",
|
||||||
|
|
106
js/store.js
Normal file
106
js/store.js
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
const store = (function() { "use strict";
|
||||||
|
|
||||||
|
class ChannelStore {
|
||||||
|
constructor(client) {
|
||||||
|
this.client = client;
|
||||||
|
this._channels = null;
|
||||||
|
this._unread = null;
|
||||||
|
this._promise = null;
|
||||||
|
this._fetching = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch() {
|
||||||
|
if (this._fetching) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._fetching = true;
|
||||||
|
this._channels = null;
|
||||||
|
this._unread = null;
|
||||||
|
|
||||||
|
const teams = this.client.myTeams();
|
||||||
|
|
||||||
|
const channels = teams
|
||||||
|
.then(teams => Promise.all(teams.map(team => this.client.myChannels(team.id))))
|
||||||
|
.then(teams => teams.map(channels => arrayToHashmap(channels, "id")))
|
||||||
|
.then(flattenHashmaps);
|
||||||
|
|
||||||
|
const unread = teams
|
||||||
|
.then(teams => Promise.all(teams.map(team => this.client.getUnread(team.id))))
|
||||||
|
.then(flattenArrays);
|
||||||
|
|
||||||
|
function processUnread(channels, unread) {
|
||||||
|
// In the Mattermost API, not the number of *unread* messages but the number of *read*
|
||||||
|
// messages is returned, so we have to subtract that from the number of total messages.
|
||||||
|
let result = Object.create(null);
|
||||||
|
for (let x of unread) {
|
||||||
|
if (!(x["channel_id"] in channels)) continue;
|
||||||
|
let object = Object.create(null);
|
||||||
|
object.unread = channels[x["channel_id"]]["total_msg_count"] - x["msg_count"];
|
||||||
|
object.mentions = x["mention_count"];
|
||||||
|
result[x["channel_id"]] = object;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._promise = Promise.all([teams, channels, unread])
|
||||||
|
.then(([teams, channels, unread]) => {
|
||||||
|
this._teams = arrayToHashmap(teams, "id");
|
||||||
|
this._channels = channels;
|
||||||
|
this._unread = processUnread(channels, unread);
|
||||||
|
this._fetching = false;
|
||||||
|
|
||||||
|
pubsub.publish("CHANNELS_RELOADED");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async get() {
|
||||||
|
if (this._promise === null) {
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
await this._promise;
|
||||||
|
return {teams: this._teams, channels: this._channels, unread: this._unread};
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateChannel(channel) {
|
||||||
|
if (this._promise === null) {
|
||||||
|
// Data does not exist yet and is not being requested
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this._promise;
|
||||||
|
this._channels[channel["id"]] = channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
async increaseUnread(post) {
|
||||||
|
if (this._promise === null) {
|
||||||
|
// Data does not exist yet and is not being requested
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this._promise;
|
||||||
|
|
||||||
|
// TODO Check if post is mention and increase mentions counter if it is
|
||||||
|
this._unread[post["channel_id"]].unread += 1;
|
||||||
|
|
||||||
|
const unread = this._unread[post["channel_id"]].unread;
|
||||||
|
const mentions = this._unread[post["channel_id"]].mentions;
|
||||||
|
|
||||||
|
pubsub.publish("CHANNEL_UNREAD_UPDATED", {endpoint: this.client.endpoint, channel_id: post["channel_id"], unread, mentions});
|
||||||
|
}
|
||||||
|
|
||||||
|
async setUnread(channel_id, unread, mentions) {
|
||||||
|
if (this._promise === null) {
|
||||||
|
// Data does not exist yet and is not being requested
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this._promise;
|
||||||
|
|
||||||
|
this._unread[channel_id].unread = unread;
|
||||||
|
this._unread[channel_id].mentions = mentions;
|
||||||
|
|
||||||
|
pubsub.publish("CHANNEL_UNREAD_UPDATED", {endpoint: this.client.endpoint, channel_id, unread, mentions});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return {ChannelStore};
|
||||||
|
|
||||||
|
})();
|
17
js/util.js
17
js/util.js
|
@ -71,6 +71,23 @@ function arrayToHashmap(array, key) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flattenHashmaps(hashmaps) {
|
||||||
|
let result = Object.create(null);
|
||||||
|
for (let x of hashmaps) {
|
||||||
|
Object.assign(result, x);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenArrays(arrays) {
|
||||||
|
let result = [];
|
||||||
|
for (let x of arrays) {
|
||||||
|
Object.assign(result, x);
|
||||||
|
Array.prototype.push.apply(result, x)
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function startsWith(string, start) {
|
function startsWith(string, start) {
|
||||||
return string.slice(0, start.length) == start;
|
return string.slice(0, start.length) == start;
|
||||||
|
|
|
@ -21,20 +21,16 @@ function populateServerSelectionList() {
|
||||||
|
|
||||||
function populateChannelList() {
|
function populateChannelList() {
|
||||||
async function addChannelItems(client) {
|
async function addChannelItems(client) {
|
||||||
const teams = await client.myTeams();
|
const {teams, channels, unread} = await client.channelStore.get();
|
||||||
|
|
||||||
for (let team of teams) {
|
|
||||||
let nodes = [];
|
let nodes = [];
|
||||||
const [channels, unreadsList] = await Promise.all([
|
|
||||||
client.myChannels(team.id),
|
|
||||||
client.getUnread(team.id)
|
|
||||||
]);
|
|
||||||
|
|
||||||
const unreads = arrayToHashmap(unreadsList, "channel_id");
|
for (let channel_id in channels) {
|
||||||
|
const channel = channels[channel_id];
|
||||||
|
const team = teams[channel["team_id"]];
|
||||||
|
|
||||||
for (let channel of channels) {
|
const chanUnread = unread[channel.id].unread;
|
||||||
const chanUnreads = channel["total_msg_count"] - unreads[channel.id]["msg_count"];
|
const chanMentions = unread[channel.id].mentions;
|
||||||
const chanMentions = unreads[channel.id]["mention_count"];
|
|
||||||
|
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
|
@ -56,15 +52,12 @@ function populateChannelList() {
|
||||||
li.appendChild(a);
|
li.appendChild(a);
|
||||||
li.dataset["id"] = channel.id;
|
li.dataset["id"] = channel.id;
|
||||||
li.dataset["server"] = client.endpoint;
|
li.dataset["server"] = client.endpoint;
|
||||||
li.dataset["unreads"] = chanUnreads;
|
|
||||||
li.dataset["mentions"] = chanMentions;
|
|
||||||
|
|
||||||
if (chanUnreads > 0) li.className = "unread";
|
if (chanUnread > 0) li.className = "unread";
|
||||||
nodes.push(li);
|
nodes.push(li);
|
||||||
}
|
}
|
||||||
byId("channel_list").append(...nodes);
|
byId("channel_list").append(...nodes);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
byId("channel_list").innerHTML = "";
|
byId("channel_list").innerHTML = "";
|
||||||
const endpoints = localstorage_credentials.getServers().map(server => server.endpoint);
|
const endpoints = localstorage_credentials.getServers().map(server => server.endpoint);
|
||||||
|
@ -73,18 +66,7 @@ function populateChannelList() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function increaseUnreadCount(el, post) {
|
function updateUnreadCount(el, unread, mentions) {
|
||||||
// TODO Check if post is mention and increase mentions counter if it is
|
|
||||||
setUnreadCount(
|
|
||||||
el,
|
|
||||||
el.dataset["unreads"] * 1 + 1,
|
|
||||||
el.dataset["mentions"] * 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setUnreadCount(el, unreads, mentions) {
|
|
||||||
el.dataset["unreads"] = unreads;
|
|
||||||
el.dataset["mentions"] = mentions;
|
|
||||||
let msgCountGem = el.querySelector('.msg_count_gem');
|
let msgCountGem = el.querySelector('.msg_count_gem');
|
||||||
if (mentions > 0) {
|
if (mentions > 0) {
|
||||||
msgCountGem.style.display = "inline-block";
|
msgCountGem.style.display = "inline-block";
|
||||||
|
@ -92,29 +74,21 @@ function setUnreadCount(el, unreads, mentions) {
|
||||||
} else {
|
} else {
|
||||||
msgCountGem.style.display = "none";
|
msgCountGem.style.display = "none";
|
||||||
}
|
}
|
||||||
if (unreads > 0) {
|
if (unread > 0) {
|
||||||
addClass(el, "unread");
|
addClass(el, "unread");
|
||||||
} else {
|
} else {
|
||||||
removeClass(el, "unread");
|
removeClass(el, "unread");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pubsub.subscribe("CHANNEL_UNREAD_UPDATED", ({endpoint, channel_id, unread, mentions}) => {
|
||||||
pubsub.subscribe("MESSAGES_NEW", post => {
|
|
||||||
const curChan = currentChannel();
|
|
||||||
if (!(post.endpoint === curChan.endpoint && post.channel_id === curChan.channel_id)) {
|
|
||||||
for (let el of byId("channel_list").childNodes) {
|
for (let el of byId("channel_list").childNodes) {
|
||||||
if (el.dataset["server"] == post.endpoint && el.dataset["id"] == post.channel_id) {
|
if (el.dataset["server"] == endpoint && el.dataset["id"] == channel_id) {
|
||||||
increaseUnreadCount(el, post);
|
updateUnreadCount(el, unread, mentions);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pubsub.subscribe("CHANNEL_READ", channel => {
|
pubsub.subscribe("CHANNELS_RELOADED", () => {
|
||||||
for (let el of byId("channel_list").childNodes) {
|
populateChannelList();
|
||||||
if (el.dataset["server"] == channel.endpoint && el.dataset["id"] == channel.channel_id) {
|
|
||||||
setUnreadCount(el, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue