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/pubsub.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/view/messages.js"></script>
|
||||
<script type="text/javascript" src="/js/view/sidebar.js"></script>
|
||||
|
|
|
@ -62,33 +62,32 @@ function logOut(endpoint, button) {
|
|||
}
|
||||
|
||||
function channelNameElements(team, channel) {
|
||||
const teamName = team ? team.name : "";
|
||||
const inTeam = teamName ? " in team " + teamName : "";
|
||||
let icon = "";
|
||||
let teamName = team.name;
|
||||
let channelName = channel.name;
|
||||
let title = "";
|
||||
|
||||
switch (channel.type) {
|
||||
case "O": // Public channel
|
||||
title = `${channel.name} in team ${team.name} (public)`;
|
||||
title = `${channel.name}${inTeam} (public)`;
|
||||
break;
|
||||
case "P": // Private channel
|
||||
icon = "🔒";
|
||||
title = `${channel.name} in team ${team.name} (private)`;
|
||||
title = `${channel.name}${inTeam} (private)`;
|
||||
break;
|
||||
case "D": // Direct message
|
||||
return undefined; // XXX Because they clutter the list
|
||||
teamName = "";
|
||||
channelName = `👤 ...`;
|
||||
title = `Direct message`;
|
||||
break;
|
||||
case "G": // Group chat
|
||||
teamName = "";
|
||||
channelName = `👥 ${channel.display_name}`;
|
||||
title = `Group chat with ${channel.display_name}`;
|
||||
break;
|
||||
default: // Unsupported
|
||||
icon = channel.type;
|
||||
title = `${channel.name} in team ${team.name} (type ${channel.type})`;
|
||||
title = `${channel.name}${inTeam} (type ${channel.type})`;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -210,16 +209,19 @@ function checkKeyPress(event) {
|
|||
}
|
||||
|
||||
pubsub.subscribe("MESSAGES_NEW", post => {
|
||||
if (!window.hasFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const curChan = currentChannel();
|
||||
if (post.endpoint === curChan.endpoint && post.channel_id === curChan.channel_id) {
|
||||
mm_client.getOrCreate(post.endpoint).markChannelRead(post.channel_id);
|
||||
const client = mm_client.getOrCreate(post.endpoint);
|
||||
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", () => {
|
||||
const curChan = currentChannel();
|
||||
if (!curChan.channel_id) return;
|
||||
|
|
|
@ -9,6 +9,8 @@ class MattermostClient {
|
|||
const creds = this.credentials.get(this.endpoint);
|
||||
this.token = creds ? creds.token : null;
|
||||
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) {
|
||||
|
@ -56,11 +58,16 @@ class MattermostClient {
|
|||
start() {
|
||||
assert(this.token);
|
||||
|
||||
let _ = this.getUsers();
|
||||
return this.userMe().then(data => {
|
||||
this.me = data;
|
||||
this.websocket();
|
||||
});
|
||||
const users = this.getUsers();
|
||||
const me = this.userMe().then(data => {this.me = data;});
|
||||
const channels = this.channelStore.get();
|
||||
|
||||
return Promise.all([
|
||||
users,
|
||||
me,
|
||||
channels
|
||||
])
|
||||
.then(() => {this.websocket();});
|
||||
}
|
||||
|
||||
async loggedIn() {
|
||||
|
|
|
@ -3,8 +3,9 @@ const pubsub = (function() { "use strict";
|
|||
const topics = [
|
||||
"MESSAGES_NEW", // {endpoint, channel_id, create_at, user_id, message}
|
||||
"MESSAGES_CHANGED",
|
||||
"CHANNELS_NEW",
|
||||
"CHANNELS_CHANGED",
|
||||
"CHANNELS_RELOADED", //
|
||||
"CHANNEL_UPDATED", // {endpoint, channel}
|
||||
"CHANNEL_UNREAD_UPDATED", // {endpoint, channel_id, unread, mentions}
|
||||
"USERS_NEW",
|
||||
"USERS_CHANGED",
|
||||
"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;
|
||||
}
|
||||
|
||||
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) {
|
||||
return string.slice(0, start.length) == start;
|
||||
|
|
|
@ -21,20 +21,16 @@ function populateServerSelectionList() {
|
|||
|
||||
function populateChannelList() {
|
||||
async function addChannelItems(client) {
|
||||
const teams = await client.myTeams();
|
||||
const {teams, channels, unread} = await client.channelStore.get();
|
||||
|
||||
for (let team of teams) {
|
||||
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 chanUnreads = channel["total_msg_count"] - unreads[channel.id]["msg_count"];
|
||||
const chanMentions = unreads[channel.id]["mention_count"];
|
||||
const chanUnread = unread[channel.id].unread;
|
||||
const chanMentions = unread[channel.id].mentions;
|
||||
|
||||
const li = document.createElement("li");
|
||||
const a = document.createElement("a");
|
||||
|
@ -56,15 +52,12 @@ function populateChannelList() {
|
|||
li.appendChild(a);
|
||||
li.dataset["id"] = channel.id;
|
||||
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);
|
||||
}
|
||||
byId("channel_list").append(...nodes);
|
||||
}
|
||||
}
|
||||
|
||||
byId("channel_list").innerHTML = "";
|
||||
const endpoints = localstorage_credentials.getServers().map(server => server.endpoint);
|
||||
|
@ -73,18 +66,7 @@ function populateChannelList() {
|
|||
}
|
||||
}
|
||||
|
||||
function increaseUnreadCount(el, post) {
|
||||
// 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;
|
||||
function updateUnreadCount(el, unread, mentions) {
|
||||
let msgCountGem = el.querySelector('.msg_count_gem');
|
||||
if (mentions > 0) {
|
||||
msgCountGem.style.display = "inline-block";
|
||||
|
@ -92,29 +74,21 @@ function setUnreadCount(el, unreads, mentions) {
|
|||
} else {
|
||||
msgCountGem.style.display = "none";
|
||||
}
|
||||
if (unreads > 0) {
|
||||
if (unread > 0) {
|
||||
addClass(el, "unread");
|
||||
} else {
|
||||
removeClass(el, "unread");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pubsub.subscribe("MESSAGES_NEW", post => {
|
||||
const curChan = currentChannel();
|
||||
if (!(post.endpoint === curChan.endpoint && post.channel_id === curChan.channel_id)) {
|
||||
pubsub.subscribe("CHANNEL_UNREAD_UPDATED", ({endpoint, channel_id, unread, mentions}) => {
|
||||
for (let el of byId("channel_list").childNodes) {
|
||||
if (el.dataset["server"] == post.endpoint && el.dataset["id"] == post.channel_id) {
|
||||
increaseUnreadCount(el, post);
|
||||
}
|
||||
if (el.dataset["server"] == endpoint && el.dataset["id"] == channel_id) {
|
||||
updateUnreadCount(el, unread, mentions);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pubsub.subscribe("CHANNEL_READ", channel => {
|
||||
for (let el of byId("channel_list").childNodes) {
|
||||
if (el.dataset["server"] == channel.endpoint && el.dataset["id"] == channel.channel_id) {
|
||||
setUnreadCount(el, 0, 0);
|
||||
}
|
||||
}
|
||||
pubsub.subscribe("CHANNELS_RELOADED", () => {
|
||||
populateChannelList();
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue