Single chat container (with buffered input), user list in vue
This commit is contained in:
parent
e931866aeb
commit
25840dfef4
@ -59,38 +59,7 @@
|
|||||||
</aside>
|
</aside>
|
||||||
<div id="sidebar-overlay"/>
|
<div id="sidebar-overlay"/>
|
||||||
<article id="windows">
|
<article id="windows">
|
||||||
<div
|
<Chat v-if="activeChannel" :network="activeChannel.network" :channel="activeChannel.channel"/>
|
||||||
id="chat-container"
|
|
||||||
class="window">
|
|
||||||
<div id="chat">
|
|
||||||
<!--Chat v-if="activeChannel" :channel="activeChannel.channel"/-->
|
|
||||||
<template v-for="network in networks">
|
|
||||||
<Chat
|
|
||||||
v-for="channel in network.channels"
|
|
||||||
:key="channel.id"
|
|
||||||
:channel="channel"/>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<div id="connection-error"/>
|
|
||||||
<form
|
|
||||||
id="form"
|
|
||||||
method="post"
|
|
||||||
action="">
|
|
||||||
<span id="nick"/>
|
|
||||||
<textarea
|
|
||||||
id="input"
|
|
||||||
class="mousetrap"/>
|
|
||||||
<span
|
|
||||||
id="submit-tooltip"
|
|
||||||
class="tooltipped tooltipped-w tooltipped-no-touch"
|
|
||||||
aria-label="Send message">
|
|
||||||
<button
|
|
||||||
id="submit"
|
|
||||||
type="submit"
|
|
||||||
aria-label="Send message"/>
|
|
||||||
</span>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
id="sign-in"
|
id="sign-in"
|
||||||
class="window"
|
class="window"
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div id="chat-container" class="window">
|
||||||
|
<div id="chat">
|
||||||
<div
|
<div
|
||||||
:id="'chan-' + channel.id"
|
:id="'chan-' + channel.id"
|
||||||
:class="[channel.type, 'chan']"
|
:class="[channel.type, 'chan', 'active']"
|
||||||
:data-id="channel.id"
|
:data-id="channel.id"
|
||||||
:data-type="channel.type"
|
:data-type="channel.type"
|
||||||
:aria-label="channel.name"
|
:aria-label="channel.name"
|
||||||
@ -33,7 +35,7 @@
|
|||||||
<div class="chat">
|
<div class="chat">
|
||||||
<div
|
<div
|
||||||
v-if="channel.messages.length > 0"
|
v-if="channel.messages.length > 0"
|
||||||
class="show-more">
|
class="show-more show">
|
||||||
<button
|
<button
|
||||||
:data-id="channel.id"
|
:data-id="channel.id"
|
||||||
class="btn"
|
class="btn"
|
||||||
@ -59,7 +61,13 @@
|
|||||||
class="date-marker-text"/>
|
class="date-marker-text"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="shouldDisplayUnreadMarker(message.id)"
|
||||||
|
:key="message.id + '-unread'"
|
||||||
|
class="unread-marker"
|
||||||
|
>
|
||||||
|
<span class="unread-marker-text"/>
|
||||||
|
</div>
|
||||||
<Message
|
<Message
|
||||||
:message="message"
|
:message="message"
|
||||||
:key="message.id"/>
|
:key="message.id"/>
|
||||||
@ -71,29 +79,86 @@
|
|||||||
class="userlist">
|
class="userlist">
|
||||||
<div class="count">
|
<div class="count">
|
||||||
<input
|
<input
|
||||||
|
:placeholder="channel.users.length + ' user' + (channel.users.length === 1 ? '' : 's')"
|
||||||
type="search"
|
type="search"
|
||||||
class="search"
|
class="search"
|
||||||
aria-label="Search among the user list"
|
aria-label="Search among the user list"
|
||||||
tabindex="-1">
|
tabindex="-1">
|
||||||
</div>
|
</div>
|
||||||
<div class="names names-filtered"/>
|
<div class="names">
|
||||||
<div class="names names-original"/>
|
<div v-for="(users, mode) in groupedUsers" :key="mode" :class="['user-mode', getModeClass(mode)]">
|
||||||
|
<Username v-for="user in users" :key="user.nick" :user="user"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="connection-error"/>
|
||||||
|
<form
|
||||||
|
id="form"
|
||||||
|
method="post"
|
||||||
|
action="">
|
||||||
|
<span id="nick">{{network.nick}}</span>
|
||||||
|
<textarea
|
||||||
|
id="input"
|
||||||
|
class="mousetrap"
|
||||||
|
v-model="channel.pendingMessage"
|
||||||
|
:placeholder="getInputPlaceholder(channel)"
|
||||||
|
:aria-label="getInputPlaceholder(channel)"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
id="submit-tooltip"
|
||||||
|
class="tooltipped tooltipped-w tooltipped-no-touch"
|
||||||
|
aria-label="Send message">
|
||||||
|
<button
|
||||||
|
id="submit"
|
||||||
|
type="submit"
|
||||||
|
aria-label="Send message"/>
|
||||||
|
</span>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Message from "./Message.vue";
|
import Message from "./Message.vue";
|
||||||
|
import Username from "./Username.vue";
|
||||||
|
|
||||||
|
const modes = {
|
||||||
|
"~": "owner",
|
||||||
|
"&": "admin",
|
||||||
|
"!": "admin",
|
||||||
|
"@": "op",
|
||||||
|
"%": "half-op",
|
||||||
|
"+": "voice",
|
||||||
|
"": "normal",
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Chat",
|
name: "Chat",
|
||||||
components: {
|
components: {
|
||||||
Message,
|
Message,
|
||||||
|
Username,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
network: Object,
|
||||||
channel: Object,
|
channel: Object,
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
groupedUsers() {
|
||||||
|
const groups = {};
|
||||||
|
|
||||||
|
for (const user of this.channel.users) {
|
||||||
|
if (!groups[user.mode]) {
|
||||||
|
groups[user.mode] = [user];
|
||||||
|
} else {
|
||||||
|
groups[user.mode].push(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
shouldDisplayDateMarker(id) {
|
shouldDisplayDateMarker(id) {
|
||||||
const previousTime = this.channel.messages[id - 1];
|
const previousTime = this.channel.messages[id - 1];
|
||||||
@ -106,6 +171,25 @@ export default {
|
|||||||
|
|
||||||
return (new Date(previousTime.time)).getDay() !== (new Date(currentTime.time)).getDay();
|
return (new Date(previousTime.time)).getDay() !== (new Date(currentTime.time)).getDay();
|
||||||
},
|
},
|
||||||
|
shouldDisplayUnreadMarker(msgId) {
|
||||||
|
if (this.channel.firstUnread < msgId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channel.firstUnread = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
getInputPlaceholder(channel) {
|
||||||
|
if (channel.type === "channel" || channel.type === "query") {
|
||||||
|
return `Write to ${channel.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
getModeClass(mode) {
|
||||||
|
return modes[mode];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
|
|
||||||
const modes = {
|
|
||||||
"~": "owner",
|
|
||||||
"&": "admin",
|
|
||||||
"!": "admin",
|
|
||||||
"@": "op",
|
|
||||||
"%": "half-op",
|
|
||||||
"+": "voice",
|
|
||||||
"": "normal",
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = function(mode) {
|
|
||||||
return modes[mode];
|
|
||||||
};
|
|
@ -164,6 +164,7 @@ $(function() {
|
|||||||
// sidebar specifically. Needs to be done better when window management gets
|
// sidebar specifically. Needs to be done better when window management gets
|
||||||
// refactored.
|
// refactored.
|
||||||
const inSidebar = self.parents("#sidebar, #footer").length > 0;
|
const inSidebar = self.parents("#sidebar, #footer").length > 0;
|
||||||
|
let channel;
|
||||||
|
|
||||||
if (inSidebar) {
|
if (inSidebar) {
|
||||||
chat.data(
|
chat.data(
|
||||||
@ -175,7 +176,7 @@ $(function() {
|
|||||||
self.data("id")
|
self.data("id")
|
||||||
);
|
);
|
||||||
|
|
||||||
const channel = findChannel(self.data("id"));
|
channel = findChannel(self.data("id"));
|
||||||
|
|
||||||
vueApp.activeChannel = channel;
|
vueApp.activeChannel = channel;
|
||||||
|
|
||||||
@ -207,11 +208,9 @@ $(function() {
|
|||||||
const lastActive = $("#windows > .active");
|
const lastActive = $("#windows > .active");
|
||||||
|
|
||||||
lastActive
|
lastActive
|
||||||
.removeClass("active")
|
.removeClass("active");
|
||||||
.find(".chat")
|
|
||||||
.unsticky();
|
|
||||||
|
|
||||||
const lastActiveChan = lastActive.find(".chan.active");
|
/*const lastActiveChan = lastActive.find(".chan.active");
|
||||||
|
|
||||||
if (lastActiveChan.length > 0) {
|
if (lastActiveChan.length > 0) {
|
||||||
lastActiveChan
|
lastActiveChan
|
||||||
@ -221,7 +220,7 @@ $(function() {
|
|||||||
.appendTo(lastActiveChan.find(".messages"));
|
.appendTo(lastActiveChan.find(".messages"));
|
||||||
|
|
||||||
render.trimMessageInChannel(lastActiveChan, 100);
|
render.trimMessageInChannel(lastActiveChan, 100);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
const chan = $(target)
|
const chan = $(target)
|
||||||
.addClass("active")
|
.addClass("active")
|
||||||
@ -231,19 +230,9 @@ $(function() {
|
|||||||
utils.updateTitle();
|
utils.updateTitle();
|
||||||
|
|
||||||
const type = chan.data("type");
|
const type = chan.data("type");
|
||||||
let placeholder = "";
|
|
||||||
|
|
||||||
if (type === "channel" || type === "query") {
|
|
||||||
placeholder = `Write to ${chan.attr("aria-label")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
input
|
|
||||||
.prop("placeholder", placeholder)
|
|
||||||
.attr("aria-label", placeholder);
|
|
||||||
|
|
||||||
if (self.hasClass("chan")) {
|
if (self.hasClass("chan")) {
|
||||||
$("#chat-container").addClass("active");
|
$("#chat-container").addClass("active");
|
||||||
$("#nick").text(self.closest(".network").attr("data-nick"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const chanChat = chan.find(".chat");
|
const chanChat = chan.find(".chat");
|
||||||
@ -257,9 +246,12 @@ $(function() {
|
|||||||
input.trigger("ontouchstart" in window ? "blur" : "focus");
|
input.trigger("ontouchstart" in window ? "blur" : "focus");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chan.data("needsNamesRefresh") === true) {
|
if (channel && channel.channel.usersOutdated) {
|
||||||
chan.data("needsNamesRefresh", false);
|
channel.channel.usersOutdated = false;
|
||||||
socket.emit("names", {target: self.data("id")});
|
|
||||||
|
socket.emit("names", {
|
||||||
|
target: channel.channel.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes states to history web API when clicking elements with a data-target attribute.
|
// Pushes states to history web API when clicking elements with a data-target attribute.
|
||||||
|
@ -24,26 +24,18 @@ const historyObserver = window.IntersectionObserver ?
|
|||||||
}) : null;
|
}) : null;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
appendMessage,
|
|
||||||
buildChannelMessages,
|
|
||||||
renderChannel,
|
renderChannel,
|
||||||
renderChannelUsers,
|
|
||||||
renderNetworks,
|
renderNetworks,
|
||||||
trimMessageInChannel,
|
trimMessageInChannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildChannelMessages(container, chanId, chanType, messages) {
|
|
||||||
return messages.reduce((docFragment, message) => {
|
|
||||||
appendMessage(docFragment, chanId, chanType, message);
|
|
||||||
return docFragment;
|
|
||||||
}, container);
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendMessage(container, chanId, chanType, msg) {
|
function appendMessage(container, chanId, chanType, msg) {
|
||||||
if (utils.lastMessageId < msg.id) {
|
if (utils.lastMessageId < msg.id) {
|
||||||
utils.lastMessageId = msg.id;
|
utils.lastMessageId = msg.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
let lastChild = container.children(".msg, .date-marker-container").last();
|
let lastChild = container.children(".msg, .date-marker-container").last();
|
||||||
const renderedMessage = buildChatMessage(msg);
|
const renderedMessage = buildChatMessage(msg);
|
||||||
|
|
||||||
@ -136,80 +128,18 @@ function renderChannel(data) {
|
|||||||
renderChannelMessages(data);
|
renderChannelMessages(data);
|
||||||
|
|
||||||
if (data.type === "channel") {
|
if (data.type === "channel") {
|
||||||
const users = renderChannelUsers(data);
|
//const users = renderChannelUsers(data);
|
||||||
|
|
||||||
Userlist.handleKeybinds(users.find(".search"));
|
//Userlist.handleKeybinds(users.find(".search"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (historyObserver) {
|
if (historyObserver) {
|
||||||
historyObserver.observe(chat.find("#chan-" + data.id + " .show-more").get(0));
|
//historyObserver.observe(chat.find("#chan-" + data.id + " .show-more").get(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderChannelMessages(data) {
|
function renderChannelMessages(data) {
|
||||||
const documentFragment = buildChannelMessages($(document.createDocumentFragment()), data.id, data.type, data.messages);
|
|
||||||
const channel = chat.find("#chan-" + data.id + " .messages");
|
const channel = chat.find("#chan-" + data.id + " .messages");
|
||||||
|
|
||||||
renderUnreadMarker($(templates.unread_marker()), data.firstUnread, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderUnreadMarker(template, firstUnread, channel) {
|
|
||||||
if (firstUnread > 0) {
|
|
||||||
let first = channel.find("#msg-" + firstUnread);
|
|
||||||
|
|
||||||
if (!first.length) {
|
|
||||||
template.data("unread-id", firstUnread);
|
|
||||||
channel.prepend(template);
|
|
||||||
} else {
|
|
||||||
const parent = first.parent();
|
|
||||||
|
|
||||||
if (parent.hasClass("condensed")) {
|
|
||||||
first = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
first.before(template);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
channel.append(template);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderChannelUsers(data) {
|
|
||||||
const users = chat.find("#chan-" + data.id).find(".userlist");
|
|
||||||
const nicks = data.users
|
|
||||||
.concat() // Make a copy of the user list, sort is applied in-place
|
|
||||||
.sort((a, b) => b.lastMessage - a.lastMessage)
|
|
||||||
.map((a) => a.nick);
|
|
||||||
|
|
||||||
// Before re-rendering the list of names, there might have been an entry
|
|
||||||
// marked as active (i.e. that was highlighted by keyboard navigation).
|
|
||||||
// It is `undefined` if there was none.
|
|
||||||
const previouslyActive = users.find(".active");
|
|
||||||
|
|
||||||
const search = users
|
|
||||||
.find(".search")
|
|
||||||
.prop("placeholder", nicks.length + " " + (nicks.length === 1 ? "user" : "users"));
|
|
||||||
|
|
||||||
users
|
|
||||||
.data("nicks", nicks)
|
|
||||||
.find(".names-original")
|
|
||||||
.html(templates.user(data));
|
|
||||||
|
|
||||||
// Refresh user search
|
|
||||||
if (search.val().length) {
|
|
||||||
search.trigger("input");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a nick was highlighted before re-rendering the lists, re-highlight it in
|
|
||||||
// the newly-rendered list.
|
|
||||||
if (previouslyActive.length > 0) {
|
|
||||||
// We need to un-highlight everything first because triggering `input` with
|
|
||||||
// a value highlights the first entry.
|
|
||||||
users.find(".user").removeClass("active");
|
|
||||||
users.find(`.user[data-name="${previouslyActive.attr("data-name")}"]`).addClass("active");
|
|
||||||
}
|
|
||||||
|
|
||||||
return users;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNetworks(data, singleNetwork) {
|
function renderNetworks(data, singleNetwork) {
|
||||||
@ -234,20 +164,12 @@ function renderNetworks(data, singleNetwork) {
|
|||||||
const chan = $("#chan-" + channel.id);
|
const chan = $("#chan-" + channel.id);
|
||||||
|
|
||||||
if (chan.length > 0) {
|
if (chan.length > 0) {
|
||||||
if (chan.data("type") === "channel") {
|
if (channel.type === "channel") {
|
||||||
chan
|
channel.usersOutdated = true;
|
||||||
.data("needsNamesRefresh", true)
|
|
||||||
.find(".header .topic")
|
|
||||||
.html(helpers_parse(channel.topic))
|
|
||||||
.prop("title", channel.topic);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel.messages.length > 0) {
|
if (channel.messages.length > 0) {
|
||||||
const container = chan.find(".messages");
|
const container = chan.find(".messages");
|
||||||
buildChannelMessages(container, channel.id, channel.type, channel.messages);
|
|
||||||
|
|
||||||
const unreadMarker = container.find(".unread-marker").data("unread-id", 0);
|
|
||||||
renderUnreadMarker(unreadMarker, channel.firstUnread, container);
|
|
||||||
|
|
||||||
if (container.find(".msg").length >= 100) {
|
if (container.find(".msg").length >= 100) {
|
||||||
container.find(".show-more").addClass("show");
|
container.find(".show-more").addClass("show");
|
||||||
@ -268,7 +190,7 @@ function renderNetworks(data, singleNetwork) {
|
|||||||
renderChannel(channel);
|
renderChannel(channel);
|
||||||
|
|
||||||
if (channel.type === "channel") {
|
if (channel.type === "channel") {
|
||||||
chat.find("#chan-" + channel.id).data("needsNamesRefresh", true);
|
channel.usersOutdated = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ const socket = require("../socket");
|
|||||||
const render = require("../render");
|
const render = require("../render");
|
||||||
const condensed = require("../condensed");
|
const condensed = require("../condensed");
|
||||||
const chat = $("#chat");
|
const chat = $("#chat");
|
||||||
|
const {Vue, vueApp, findChannel} = require("../vue");
|
||||||
|
|
||||||
socket.on("more", function(data) {
|
socket.on("more", function(data) {
|
||||||
let chan = chat.find("#chan-" + data.chan);
|
let chan = chat.find("#chan-" + data.chan);
|
||||||
@ -21,51 +22,30 @@ socket.on("more", function(data) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the date marker at the top if date does not change
|
const channel = findChannel(data.chan);
|
||||||
const children = $(chan).children();
|
|
||||||
|
|
||||||
// Check the top-most element and the one after because
|
if (!channel) {
|
||||||
// unread and date markers may switch positions
|
return;
|
||||||
for (let i = 0; i <= 1; i++) {
|
|
||||||
const marker = children.eq(i);
|
|
||||||
|
|
||||||
if (marker.hasClass("date-marker-container")) {
|
|
||||||
const msgTime = new Date(data.messages[data.messages.length - 1].time);
|
|
||||||
const prevMsgTime = new Date(marker.data("time"));
|
|
||||||
|
|
||||||
if (prevMsgTime.toDateString() === msgTime.toDateString()) {
|
|
||||||
marker.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
channel.channel.messages.unshift(...data.messages);
|
||||||
}
|
|
||||||
|
Vue.nextTick(() => {
|
||||||
|
// restore scroll position
|
||||||
|
const position = chan.height() - heightOld;
|
||||||
|
scrollable.finish().scrollTop(position);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.messages.length !== 100) {
|
||||||
|
scrollable.find(".show-more").removeClass("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the older messages
|
// Swap button text back from its alternative label
|
||||||
const documentFragment = render.buildChannelMessages($(document.createDocumentFragment()), data.chan, type, data.messages);
|
const showMoreBtn = scrollable.find(".show-more button");
|
||||||
chan.prepend(documentFragment);
|
swapText(showMoreBtn);
|
||||||
|
showMoreBtn.prop("disabled", false);
|
||||||
|
|
||||||
// Move unread marker to correct spot if needed
|
return;
|
||||||
const unreadMarker = chan.find(".unread-marker");
|
|
||||||
const firstUnread = unreadMarker.data("unread-id");
|
|
||||||
|
|
||||||
if (firstUnread > 0) {
|
|
||||||
let first = chan.find("#msg-" + firstUnread);
|
|
||||||
|
|
||||||
if (!first.length) {
|
|
||||||
chan.prepend(unreadMarker);
|
|
||||||
} else {
|
|
||||||
const parent = first.parent();
|
|
||||||
|
|
||||||
if (parent.hasClass("condensed")) {
|
|
||||||
first = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
unreadMarker.data("unread-id", 0);
|
|
||||||
|
|
||||||
first.before(unreadMarker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Join duplicate condensed messages together
|
// Join duplicate condensed messages together
|
||||||
const condensedDuplicate = chan.find(".msg.condensed + .msg.condensed");
|
const condensedDuplicate = chan.find(".msg.condensed + .msg.condensed");
|
||||||
@ -81,25 +61,6 @@ socket.on("more", function(data) {
|
|||||||
|
|
||||||
condensedDuplicate.remove();
|
condensedDuplicate.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore scroll position
|
|
||||||
const position = chan.height() - heightOld;
|
|
||||||
scrollable.finish().scrollTop(position);
|
|
||||||
|
|
||||||
// We have to do this hack due to smooth scrolling in browsers,
|
|
||||||
// as scrollTop does not apply correctly
|
|
||||||
if (window.requestAnimationFrame) {
|
|
||||||
window.requestAnimationFrame(() => scrollable.scrollTop(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.messages.length !== 100) {
|
|
||||||
scrollable.find(".show-more").removeClass("show");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap button text back from its alternative label
|
|
||||||
const showMoreBtn = scrollable.find(".show-more button");
|
|
||||||
swapText(showMoreBtn);
|
|
||||||
showMoreBtn.prop("disabled", false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
chat.on("click", ".show-more button", function() {
|
chat.on("click", ".show-more button", function() {
|
||||||
|
@ -60,13 +60,7 @@ function processReceivedMessage(data) {
|
|||||||
$(container).empty();
|
$(container).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add message to the container
|
channel.channel.messages.push(data.msg);
|
||||||
render.appendMessage(
|
|
||||||
container,
|
|
||||||
targetId,
|
|
||||||
channelContainer.data("type"),
|
|
||||||
data.msg
|
|
||||||
);
|
|
||||||
|
|
||||||
if (activeChannelId === targetId) {
|
if (activeChannelId === targetId) {
|
||||||
scrollContainer.trigger("keepToBottom");
|
scrollContainer.trigger("keepToBottom");
|
||||||
|
@ -2,5 +2,12 @@
|
|||||||
|
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const render = require("../render");
|
const render = require("../render");
|
||||||
|
const {vueApp, findChannel} = require("../vue");
|
||||||
|
|
||||||
socket.on("names", render.renderChannelUsers);
|
socket.on("names", function(data) {
|
||||||
|
const channel = findChannel(data.id);
|
||||||
|
|
||||||
|
if (channel) {
|
||||||
|
channel.channel.users = data.users;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const $ = require("jquery");
|
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
|
const {vueApp} = require("../vue");
|
||||||
|
|
||||||
socket.on("nick", function(data) {
|
socket.on("nick", function(data) {
|
||||||
const id = data.network;
|
const network = vueApp.networks.find((n) => n.uuid === data.network);
|
||||||
const nick = data.nick;
|
|
||||||
const network = $(`#sidebar .network[data-uuid="${id}"]`).attr("data-nick", nick);
|
|
||||||
|
|
||||||
if (network.find(".active").length) {
|
if (network) {
|
||||||
$("#nick").text(nick);
|
network.nick = data.nick;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2,16 +2,18 @@
|
|||||||
|
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const chat = $("#chat");
|
const {vueApp, findChannel} = require("../vue");
|
||||||
|
|
||||||
socket.on("users", function(data) {
|
socket.on("users", function(data) {
|
||||||
const chan = chat.find("#chan-" + data.chan);
|
if (vueApp.activeChannel && vueApp.activeChannel.channel.id === data.chan) {
|
||||||
|
return socket.emit("names", {
|
||||||
if (chan.hasClass("active")) {
|
|
||||||
socket.emit("names", {
|
|
||||||
target: data.chan,
|
target: data.chan,
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
chan.data("needsNamesRefresh", true);
|
|
||||||
|
const channel = findChannel(data.chan);
|
||||||
|
|
||||||
|
if (channel) {
|
||||||
|
channel.channel.usersOutdated = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
<div class="unread-marker">
|
|
||||||
<span class="unread-marker-text"></span>
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue
Block a user