Add support for channel read status
This commit is contained in:
parent
38b4e49dfb
commit
0cc0cb1c13
6 changed files with 125 additions and 7 deletions
|
@ -78,6 +78,7 @@ h1 img {
|
||||||
border-right: 1px solid #aaa;
|
border-right: 1px solid #aaa;
|
||||||
}
|
}
|
||||||
#channel_list a {
|
#channel_list a {
|
||||||
|
position: relative;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -105,6 +106,23 @@ h1 img {
|
||||||
color: #888;
|
color: #888;
|
||||||
margin: 0 1px;
|
margin: 0 1px;
|
||||||
}
|
}
|
||||||
|
#channel_list .unread {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
#channel_list a .msg_count_gem {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: #444;
|
||||||
|
border-radius: 100%;
|
||||||
|
color: #ccc;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 50%;
|
||||||
|
padding: 0 0.6em;
|
||||||
|
right: 0.5em;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
ul#channel_list, ul#server_selection_list {
|
ul#channel_list, ul#server_selection_list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
|
@ -120,9 +120,9 @@ function channelNameElements(team, channel) {
|
||||||
function switchToChannel(client, team, channel) {
|
function switchToChannel(client, team, channel) {
|
||||||
for (let el of byId("channel_list").childNodes) {
|
for (let el of byId("channel_list").childNodes) {
|
||||||
if (el.dataset["server"] == client.endpoint && el.dataset["id"] == channel.id) {
|
if (el.dataset["server"] == client.endpoint && el.dataset["id"] == channel.id) {
|
||||||
el.className = "active";
|
addClass(el, "active");
|
||||||
} else {
|
} else {
|
||||||
el.className = "";
|
removeClass(el, "active");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +132,14 @@ function switchToChannel(client, team, channel) {
|
||||||
console.info(`Got channel contents of ${channel.id} (${channel.name})`);
|
console.info(`Got channel contents of ${channel.id} (${channel.name})`);
|
||||||
response.order.reverse();
|
response.order.reverse();
|
||||||
populateChannelContents(client, channel, response);
|
populateChannelContents(client, channel, response);
|
||||||
|
|
||||||
|
markChannelAsRead(client, channel)
|
||||||
|
.then(_ => {
|
||||||
|
pubsub.publish("CHANNEL_READ", {
|
||||||
|
endpoint: client.endpoint,
|
||||||
|
channel_id: channel.id
|
||||||
|
});
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
@ -156,6 +164,10 @@ function sendMessage(endpoint, channel_id, message) {
|
||||||
//});
|
//});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function markChannelAsRead(client, channel) {
|
||||||
|
return client.markChannelRead(channel.id);
|
||||||
|
}
|
||||||
|
|
||||||
function checkKeyPress(event) {
|
function checkKeyPress(event) {
|
||||||
// Battle tested for many years in several browsers
|
// Battle tested for many years in several browsers
|
||||||
if ((event.keyCode === event.DOM_VK_RETURN || event.keyCode === 13 || event.keyCode === 10 || event.key === "Enter" || event.keyIdentifier === "U+000A") && !event.shiftKey && !event.ctrlKey) {
|
if ((event.keyCode === event.DOM_VK_RETURN || event.keyCode === 13 || event.keyCode === 10 || event.key === "Enter" || event.keyIdentifier === "U+000A") && !event.shiftKey && !event.ctrlKey) {
|
||||||
|
|
|
@ -137,6 +137,17 @@ class MattermostClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUnread(team_id) {
|
||||||
|
return this.get(`/users/me/teams/${team_id}/channels/members`);
|
||||||
|
}
|
||||||
|
|
||||||
|
markChannelRead(channel_id) {
|
||||||
|
assertIsMattermostId(channel_id);
|
||||||
|
return this.post(`/channels/members/me/view`, {
|
||||||
|
channel_id: channel_id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
writePost(channel_id, message) {
|
writePost(channel_id, message) {
|
||||||
return this.post("/posts", {
|
return this.post("/posts", {
|
||||||
"channel_id": channel_id,
|
"channel_id": channel_id,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const pubsub = (function() { "use strict";
|
const pubsub = (function() { "use strict";
|
||||||
|
|
||||||
const topics = [
|
const topics = [
|
||||||
"MESSAGES_NEW",
|
"MESSAGES_NEW", // {endpoint, channel_id, create_at, user_id, message}
|
||||||
"MESSAGES_CHANGED",
|
"MESSAGES_CHANGED",
|
||||||
"CHANNELS_NEW",
|
"CHANNELS_NEW",
|
||||||
"CHANNELS_CHANGED",
|
"CHANNELS_CHANGED",
|
||||||
|
@ -9,6 +9,7 @@ const topics = [
|
||||||
"USERS_CHANGED",
|
"USERS_CHANGED",
|
||||||
"CHANNEL_MEMBERS_NEW",
|
"CHANNEL_MEMBERS_NEW",
|
||||||
"CHANNEL_MEMBERS_REMOVED",
|
"CHANNEL_MEMBERS_REMOVED",
|
||||||
|
"CHANNEL_READ", // {endpoint, channel_id}
|
||||||
];
|
];
|
||||||
|
|
||||||
let subscribers = Object.create(null);
|
let subscribers = Object.create(null);
|
||||||
|
|
17
js/util.js
17
js/util.js
|
@ -35,6 +35,14 @@ function byId(id, nullOk=false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function removeClass(el, className) {
|
||||||
|
el.className = el.className.split(" ").filter(x => x !== className).join(" ");
|
||||||
|
}
|
||||||
|
function addClass(el, className) {
|
||||||
|
el.className = el.className.split(" ").filter(x => x !== className).join(" ") + ` ${className}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap a function so that it receives `this` as first argument
|
* Wrap a function so that it receives `this` as first argument
|
||||||
*/
|
*/
|
||||||
|
@ -55,6 +63,15 @@ function extend(obj1, obj2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function arrayToHashmap(array, key) {
|
||||||
|
let result = Object.create(null);
|
||||||
|
for (let x of array) {
|
||||||
|
result[x[key]] = x;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function padLeft(input, width, padding=" ") {
|
function padLeft(input, width, padding=" ") {
|
||||||
text = input + "";
|
text = input + "";
|
||||||
while (text.length < width) {
|
while (text.length < width) {
|
||||||
|
|
|
@ -25,8 +25,17 @@ function populateChannelList() {
|
||||||
|
|
||||||
for (let team of teams) {
|
for (let team of teams) {
|
||||||
let nodes = [];
|
let nodes = [];
|
||||||
const channels = await client.myChannels(team.id);
|
const [channels, unreadsList] = await Promise.all([
|
||||||
|
client.myChannels(team.id),
|
||||||
|
client.getUnread(team.id)
|
||||||
|
]);
|
||||||
|
|
||||||
|
const unreads = arrayToHashmap(unreadsList, "channel_id");
|
||||||
|
|
||||||
for (let channel of channels) {
|
for (let channel of channels) {
|
||||||
|
const chanUnreads = channel["total_msg_count"] - unreads[channel.id]["msg_count"];
|
||||||
|
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");
|
||||||
a.href = "javascript:void(0)";
|
a.href = "javascript:void(0)";
|
||||||
|
@ -35,11 +44,23 @@ function populateChannelList() {
|
||||||
a.title = titleAndElements[0];
|
a.title = titleAndElements[0];
|
||||||
a.append(...titleAndElements[1]);
|
a.append(...titleAndElements[1]);
|
||||||
|
|
||||||
|
a.append(document.createTextNode(" "));
|
||||||
|
const msgCountGem = document.createElement("span");
|
||||||
|
msgCountGem.className = "msg_count_gem";
|
||||||
|
msgCountGem.innerText = chanMentions;
|
||||||
|
if (chanMentions > 0) msgCountGem.style.display = "inline-block";
|
||||||
|
a.append(msgCountGem);
|
||||||
|
|
||||||
a.addEventListener("click", () => switchToChannel(client, team, channel));
|
a.addEventListener("click", () => switchToChannel(client, team, channel));
|
||||||
|
|
||||||
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";
|
||||||
|
console.debug(`Channel ${channel.name} with ${chanUnreads} unreads`);
|
||||||
nodes.push(li);
|
nodes.push(li);
|
||||||
}
|
}
|
||||||
byId("channel_list").append(...nodes);
|
byId("channel_list").append(...nodes);
|
||||||
|
@ -53,10 +74,48 @@ 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;
|
||||||
|
let msgCountGem = el.querySelector('.msg_count_gem');
|
||||||
|
if (mentions > 0) {
|
||||||
|
msgCountGem.style.display = "inline-block";
|
||||||
|
msgCountGem.innerText = mentions;
|
||||||
|
} else {
|
||||||
|
msgCountGem.style.display = "none";
|
||||||
|
}
|
||||||
|
if (unreads > 0) {
|
||||||
|
addClass(el, "unread");
|
||||||
|
} else {
|
||||||
|
removeClass(el, "unread");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pubsub.subscribe("MESSAGES_NEW", post => {
|
pubsub.subscribe("MESSAGES_NEW", post => {
|
||||||
const chan = currentChannel();
|
const curChan = currentChannel();
|
||||||
if (!(post.endpoint === chan.endpoint && post.channel_id === chan.channel_id)) {
|
if (!(post.endpoint === curChan.endpoint && post.channel_id === curChan.channel_id)) {
|
||||||
// TODO mark channel unread
|
for (let el of byId("channel_list").childNodes) {
|
||||||
|
if (el.dataset["server"] == post.endpoint && el.dataset["id"] == post.channel_id) {
|
||||||
|
increaseUnreadCount(el, post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue