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;
|
||||
}
|
||||
#channel_list a {
|
||||
position: relative;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
@ -105,6 +106,23 @@ h1 img {
|
|||
color: #888;
|
||||
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 {
|
||||
list-style: none;
|
||||
|
|
|
@ -120,9 +120,9 @@ function channelNameElements(team, channel) {
|
|||
function switchToChannel(client, team, channel) {
|
||||
for (let el of byId("channel_list").childNodes) {
|
||||
if (el.dataset["server"] == client.endpoint && el.dataset["id"] == channel.id) {
|
||||
el.className = "active";
|
||||
addClass(el, "active");
|
||||
} 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})`);
|
||||
response.order.reverse();
|
||||
populateChannelContents(client, channel, response);
|
||||
|
||||
markChannelAsRead(client, channel)
|
||||
.then(_ => {
|
||||
pubsub.publish("CHANNEL_READ", {
|
||||
endpoint: client.endpoint,
|
||||
channel_id: channel.id
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(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) {
|
||||
// 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) {
|
||||
|
|
|
@ -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) {
|
||||
return this.post("/posts", {
|
||||
"channel_id": channel_id,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const pubsub = (function() { "use strict";
|
||||
|
||||
const topics = [
|
||||
"MESSAGES_NEW",
|
||||
"MESSAGES_NEW", // {endpoint, channel_id, create_at, user_id, message}
|
||||
"MESSAGES_CHANGED",
|
||||
"CHANNELS_NEW",
|
||||
"CHANNELS_CHANGED",
|
||||
|
@ -9,6 +9,7 @@ const topics = [
|
|||
"USERS_CHANGED",
|
||||
"CHANNEL_MEMBERS_NEW",
|
||||
"CHANNEL_MEMBERS_REMOVED",
|
||||
"CHANNEL_READ", // {endpoint, channel_id}
|
||||
];
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -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=" ") {
|
||||
text = input + "";
|
||||
while (text.length < width) {
|
||||
|
|
|
@ -25,8 +25,17 @@ function populateChannelList() {
|
|||
|
||||
for (let team of teams) {
|
||||
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) {
|
||||
const chanUnreads = channel["total_msg_count"] - unreads[channel.id]["msg_count"];
|
||||
const chanMentions = unreads[channel.id]["mention_count"];
|
||||
|
||||
const li = document.createElement("li");
|
||||
const a = document.createElement("a");
|
||||
a.href = "javascript:void(0)";
|
||||
|
@ -35,11 +44,23 @@ function populateChannelList() {
|
|||
a.title = titleAndElements[0];
|
||||
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));
|
||||
|
||||
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";
|
||||
console.debug(`Channel ${channel.name} with ${chanUnreads} unreads`);
|
||||
nodes.push(li);
|
||||
}
|
||||
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 => {
|
||||
const chan = currentChannel();
|
||||
if (!(post.endpoint === chan.endpoint && post.channel_id === chan.channel_id)) {
|
||||
// TODO mark channel unread
|
||||
const curChan = currentChannel();
|
||||
if (!(post.endpoint === curChan.endpoint && post.channel_id === curChan.channel_id)) {
|
||||
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