|
@@ -167,6 +167,8 @@ function onRoomSelected() {
|
|
|
}
|
|
}
|
|
|
var roomLi = document.getElementById("room_" +SELECTED_ROOM.id);
|
|
var roomLi = document.getElementById("room_" +SELECTED_ROOM.id);
|
|
|
document.getElementById(R.id.currentRoom.title).textContent = name;
|
|
document.getElementById(R.id.currentRoom.title).textContent = name;
|
|
|
|
|
+ document.getElementById(R.id.currentRoom.content).textContent = '';
|
|
|
|
|
+ MSG_GROUPS = [];
|
|
|
onRoomUpdated();
|
|
onRoomUpdated();
|
|
|
focusInput();
|
|
focusInput();
|
|
|
|
|
|
|
@@ -307,90 +309,171 @@ function spawnNotification() {
|
|
|
Notification.requestPermission();
|
|
Notification.requestPermission();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function isSameGroup(group, msg) {
|
|
|
|
|
+ return group &&
|
|
|
|
|
+ isSameDay(msg.ts, group.ts) &&
|
|
|
|
|
+ !(msg instanceof MeMessage) &&
|
|
|
|
|
+ !group.meMessage &&
|
|
|
|
|
+ msg.userId === group.userId;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function onRoomUpdated() {
|
|
function onRoomUpdated() {
|
|
|
- var chatFrag = document.createDocumentFragment(),
|
|
|
|
|
- currentRoomId = SELECTED_ROOM.id,
|
|
|
|
|
|
|
+ var chatFrag = document.getElementById(R.id.currentRoom.content),
|
|
|
prevMsg = null,
|
|
prevMsg = null,
|
|
|
firstTsCombo = 0,
|
|
firstTsCombo = 0,
|
|
|
- prevMsgDom = null,
|
|
|
|
|
- prevTs = 0,
|
|
|
|
|
- currentMsgGroupDom;
|
|
|
|
|
|
|
+ currentGroup = 0,
|
|
|
|
|
+ currentMsgInGroup = 0;
|
|
|
|
|
|
|
|
if (SELECTED_ROOM.starred)
|
|
if (SELECTED_ROOM.starred)
|
|
|
document.getElementById(R.id.mainSection).classList.add(R.klass.starred);
|
|
document.getElementById(R.id.mainSection).classList.add(R.klass.starred);
|
|
|
else
|
|
else
|
|
|
document.getElementById(R.id.mainSection).classList.remove(R.klass.starred);
|
|
document.getElementById(R.id.mainSection).classList.remove(R.klass.starred);
|
|
|
- MSG_GROUPS = [];
|
|
|
|
|
- if (DATA.history[currentRoomId])
|
|
|
|
|
- DATA.history[currentRoomId].messages.forEach(function(msg) {
|
|
|
|
|
- if (!msg.removed) {
|
|
|
|
|
- var dom = msg.getDom(),
|
|
|
|
|
- newGroupDom = false,
|
|
|
|
|
- sameDayThanPrevious = isSameDay(msg.ts, prevTs);
|
|
|
|
|
-
|
|
|
|
|
- if (prevMsg && prevMsg.userId === msg.userId && msg.userId && sameDayThanPrevious) {
|
|
|
|
|
- if (Math.abs(firstTsCombo -msg.ts) < 30 && !(msg instanceof MeMessage))
|
|
|
|
|
- prevMsgDom.classList.add(R.klass.msg.sameTs);
|
|
|
|
|
- else
|
|
|
|
|
|
|
+ DATA.history[SELECTED_ROOM.id] && DATA.history[SELECTED_ROOM.id].messages.forEach(function(msg) {
|
|
|
|
|
+ var groupDomCreated = false;
|
|
|
|
|
+ if (MSG_GROUPS[currentGroup]) {
|
|
|
|
|
+ if (currentMsgInGroup >= MSG_GROUPS[currentGroup].messages.length) {
|
|
|
|
|
+ if (!msg.removed) {
|
|
|
|
|
+ if (isSameGroup(MSG_GROUPS[currentGroup], msg)) {
|
|
|
|
|
+ // New message belonging in this group
|
|
|
|
|
+ var dom = msg.getDom();
|
|
|
|
|
+ if (Math.abs(firstTsCombo -msg.ts) < 30 && prevMsg)
|
|
|
|
|
+ prevMsg.getDom().classList.add(R.klass.msg.sameTs);
|
|
|
firstTsCombo = msg.ts;
|
|
firstTsCombo = msg.ts;
|
|
|
- } else {
|
|
|
|
|
|
|
+ if ((!prevMsg || prevMsg.ts <= SELECTED_ROOM.lastRead) && msg.ts > SELECTED_ROOM.lastRead)
|
|
|
|
|
+ dom.classList.add(R.klass.msg.firstUnread);
|
|
|
|
|
+ else
|
|
|
|
|
+ dom.classList.remove(R.klass.msg.firstUnread);
|
|
|
|
|
+ MSG_GROUPS[currentGroup].content.appendChild(dom);
|
|
|
|
|
+ MSG_GROUPS[currentGroup].messages.push(msg.id);
|
|
|
|
|
+ ++currentMsgInGroup;
|
|
|
|
|
+ prevMsg = msg;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (msg.ts < MSG_GROUPS[currentGroup].ts) {
|
|
|
|
|
+ while (currentGroup > 0 && msg.ts < MSG_GROUPS[currentGroup].ts) {
|
|
|
|
|
+ // New message in previous group, move group cursor before the group to be inserted
|
|
|
|
|
+ --currentGroup;
|
|
|
|
|
+ }
|
|
|
|
|
+ while (MSG_GROUPS[currentGroup]) {
|
|
|
|
|
+ MSG_GROUPS[currentGroup].remove();
|
|
|
|
|
+ MSG_GROUPS.splice(currentGroup, 1);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // New message in next group
|
|
|
|
|
+ while (MSG_GROUPS[currentGroup].content.children[currentMsgInGroup])
|
|
|
|
|
+ MSG_GROUPS[currentGroup].content.children[currentMsgInGroup].remove();
|
|
|
|
|
+ MSG_GROUPS[currentGroup].messages.splice(currentMsgInGroup);
|
|
|
|
|
+ if (!MSG_GROUPS[currentGroup].messages.length) {
|
|
|
|
|
+ MSG_GROUPS[currentGroup].remove();
|
|
|
|
|
+ MSG_GROUPS[currentGroup] = null;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ currentGroup++;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ currentMsgInGroup = 0;
|
|
|
firstTsCombo = msg.ts;
|
|
firstTsCombo = msg.ts;
|
|
|
- newGroupDom = true;
|
|
|
|
|
- }
|
|
|
|
|
- if (SELECTED_ROOM_UNREAD > -1 &&
|
|
|
|
|
- (!prevMsg || prevMsg.ts <= SELECTED_ROOM_UNREAD) &&
|
|
|
|
|
- msg.ts > SELECTED_ROOM_UNREAD) {
|
|
|
|
|
- dom.classList.add(R.klass.msg.firstUnread);
|
|
|
|
|
- dom.insertBefore(UNREAD_INDICATOR_DOM, dom.firstChild);
|
|
|
|
|
- } else {
|
|
|
|
|
- dom.classList.remove(R.klass.msg.firstUnread);
|
|
|
|
|
}
|
|
}
|
|
|
- if (msg instanceof MeMessage) {
|
|
|
|
|
- prevMsg = null;
|
|
|
|
|
- prevMsgDom = null;
|
|
|
|
|
- firstTsCombo = 0;
|
|
|
|
|
- newGroupDom = true;
|
|
|
|
|
- chatFrag.appendChild(dom);
|
|
|
|
|
- currentMsgGroupDom = null;
|
|
|
|
|
- } else {
|
|
|
|
|
- if (newGroupDom || !currentMsgGroupDom) {
|
|
|
|
|
- currentMsgGroupDom = createMessageGroupDom(DATA.context.getUser(msg.userId), msg.username);
|
|
|
|
|
- MSG_GROUPS.push(currentMsgGroupDom);
|
|
|
|
|
- chatFrag.appendChild(currentMsgGroupDom);
|
|
|
|
|
|
|
+ // else message got removed
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isSameGroup(MSG_GROUPS[currentGroup], msg)) {
|
|
|
|
|
+ if (MSG_GROUPS[currentGroup].messages[currentMsgInGroup] === msg.id) {
|
|
|
|
|
+ // same message
|
|
|
|
|
+ var dom = MSG_GROUPS[currentGroup].content.children[currentMsgInGroup];
|
|
|
|
|
+ if (msg.removed) {
|
|
|
|
|
+ // Check removed
|
|
|
|
|
+ dom.remove();
|
|
|
|
|
+ MSG_GROUPS[currentGroup].messages.splice(currentMsgInGroup, 1);
|
|
|
|
|
+ } else if (msg.uiNeedRefresh) {
|
|
|
|
|
+ // Check invalidated
|
|
|
|
|
+ var newDom = msg.getDom();
|
|
|
|
|
+ MSG_GROUPS[currentGroup].content.replaceChild(dom, newDom);
|
|
|
|
|
+ dom = newDom;
|
|
|
}
|
|
}
|
|
|
|
|
+ if ((!prevMsg || prevMsg.ts <= SELECTED_ROOM.lastRead) && msg.ts > SELECTED_ROOM.lastRead)
|
|
|
|
|
+ dom.classList.add(R.klass.msg.firstUnread);
|
|
|
|
|
+ else
|
|
|
|
|
+ dom.classList.remove(R.klass.msg.firstUnread);
|
|
|
|
|
+ currentMsgInGroup++;
|
|
|
|
|
+ prevMsg = msg;
|
|
|
|
|
+ } else if (!msg.removed) {
|
|
|
|
|
+ // Another message, insert it before
|
|
|
|
|
+ var dom = msg.getDom();
|
|
|
|
|
+ MSG_GROUPS[currentGroup].content.insertBefore(dom, MSG_GROUPS[currentGroup].content.children[currentMsgInGroup]);
|
|
|
|
|
+ MSG_GROUPS[currentGroup].messages.splice(currentMsgInGroup, 0, msg.id);
|
|
|
|
|
+ if ((!prevMsg || prevMsg.ts <= SELECTED_ROOM.lastRead) && msg.ts > SELECTED_ROOM.lastRead)
|
|
|
|
|
+ dom.classList.add(R.klass.msg.firstUnread);
|
|
|
|
|
+ else
|
|
|
|
|
+ dom.classList.remove(R.klass.msg.firstUnread);
|
|
|
|
|
+ // Check invalidated
|
|
|
|
|
+ currentMsgInGroup++;
|
|
|
prevMsg = msg;
|
|
prevMsg = msg;
|
|
|
- prevMsgDom = dom;
|
|
|
|
|
- currentMsgGroupDom.content.appendChild(dom);
|
|
|
|
|
- }
|
|
|
|
|
- if (!currentMsgGroupDom || currentMsgGroupDom.content.firstChild === dom) {
|
|
|
|
|
- if (sameDayThanPrevious) {
|
|
|
|
|
- (currentMsgGroupDom || dom).classList.remove(R.klass.msg.firstOfDay);
|
|
|
|
|
- } else {
|
|
|
|
|
- (currentMsgGroupDom || dom).classList.add(R.klass.msg.firstOfDay);
|
|
|
|
|
- (currentMsgGroupDom || dom).dataset["date"] = locale.formatDay(msg.ts);
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
- prevTs = msg.ts;
|
|
|
|
|
- } else {
|
|
|
|
|
- msg.removeDom();
|
|
|
|
|
|
|
+ return;
|
|
|
}
|
|
}
|
|
|
- });
|
|
|
|
|
- var isFirstPending = true;
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+ if (msg.removed)
|
|
|
|
|
+ return;
|
|
|
|
|
+ if (MSG_GROUPS[currentGroup] && !isSameGroup(MSG_GROUPS[currentGroup], msg)) { // Next group is not the good one, insert a new group
|
|
|
|
|
+ MSG_GROUPS.splice(currentGroup, 0, null);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!MSG_GROUPS[currentGroup]) {
|
|
|
|
|
+ MSG_GROUPS[currentGroup] = createMessageGroupDom(DATA.context.getUser(msg.userId), msg.username, msg instanceof MeMessage, msg.ts);
|
|
|
|
|
+ groupDomCreated = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ /** @type {Element} */
|
|
|
|
|
+ var dom = msg.getDom();
|
|
|
|
|
+ firstTsCombo = msg.ts;
|
|
|
|
|
+ if ((!prevMsg || prevMsg.ts <= SELECTED_ROOM.lastRead) && msg.ts > SELECTED_ROOM.lastRead) {
|
|
|
|
|
+ dom.classList.add(R.klass.msg.firstUnread);
|
|
|
|
|
+ dom.insertBefore(UNREAD_INDICATOR_DOM, dom.firstChild);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ dom.classList.remove(R.klass.msg.firstUnread);
|
|
|
|
|
+ }
|
|
|
|
|
+ MSG_GROUPS[currentGroup].content.appendChild(dom);
|
|
|
|
|
+ MSG_GROUPS[currentGroup].messages.push(msg.id);
|
|
|
|
|
+ prevMsg = msg;
|
|
|
|
|
+
|
|
|
|
|
+ if (groupDomCreated) {
|
|
|
|
|
+ if (MSG_GROUPS[currentGroup +1])
|
|
|
|
|
+ chatFrag.insertBefore(MSG_GROUPS[currentGroup], MSG_GROUPS[currentGroup +1]);
|
|
|
|
|
+ else
|
|
|
|
|
+ chatFrag.appendChild(MSG_GROUPS[currentGroup]);
|
|
|
|
|
+ currentMsgInGroup = 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ return;
|
|
|
|
|
+ });
|
|
|
|
|
+ if (MSG_GROUPS[currentGroup]) {
|
|
|
|
|
+ while (MSG_GROUPS[currentGroup].content.children[currentMsgInGroup])
|
|
|
|
|
+ MSG_GROUPS[currentGroup].content.children[currentMsgInGroup].remove();
|
|
|
|
|
+ MSG_GROUPS[currentGroup].messages.splice(currentMsgInGroup);
|
|
|
|
|
+ if (!MSG_GROUPS[currentGroup].messages.length)
|
|
|
|
|
+ --currentGroup;
|
|
|
|
|
+ for (var i = currentGroup +1;MSG_GROUPS[i]; i++) {
|
|
|
|
|
+ MSG_GROUPS[i].remove();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for (var i =0, len =MSG_GROUPS.length; i < len; ++i) {
|
|
|
|
|
+ if (MSG_GROUPS[i -1] && isSameDay(MSG_GROUPS[i -1].ts, MSG_GROUPS[i].ts)) {
|
|
|
|
|
+ MSG_GROUPS[i].classList.remove(R.klass.msg.firstOfDay);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ MSG_GROUPS[i].classList.add(R.klass.msg.firstOfDay);
|
|
|
|
|
+ MSG_GROUPS[i].dataset["date"] = MSG_GROUPS[i].dataset["date"] || locale.formatDay(MSG_GROUPS[i].ts);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ currentGroup = MSG_GROUPS.length;
|
|
|
PENDING_MESSAGES.forEach(function(msg) {
|
|
PENDING_MESSAGES.forEach(function(msg) {
|
|
|
if (msg.channel === SELECTED_ROOM.id) {
|
|
if (msg.channel === SELECTED_ROOM.id) {
|
|
|
- if (isFirstPending) {
|
|
|
|
|
- currentMsgGroupDom = createMessageGroupDom(SELECTED_CONTEXT.self, "");
|
|
|
|
|
- chatFrag.appendChild(currentMsgGroupDom);
|
|
|
|
|
- isFirstPending = false;
|
|
|
|
|
|
|
+ if (!MSG_GROUPS[currentGroup]) {
|
|
|
|
|
+ MSG_GROUPS[currentGroup] = createMessageGroupDom(SELECTED_CONTEXT.self, SELECTED_CONTEXT.self.getName(), msg instanceof MeMessage, msg.ts);
|
|
|
|
|
+ chatFrag.appendChild(MSG_GROUPS[currentGroup]);
|
|
|
}
|
|
}
|
|
|
- currentMsgGroupDom.content.appendChild(msg.dom);
|
|
|
|
|
|
|
+ MSG_GROUPS[currentGroup].classList.add("pending");
|
|
|
|
|
+ MSG_GROUPS[currentGroup].content.appendChild(msg.dom);
|
|
|
|
|
+ MSG_GROUPS[currentGroup].messages.push(msg.id);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
- var content = document.getElementById(R.id.currentRoom.content);
|
|
|
|
|
- //TODO lazy add dom if needed
|
|
|
|
|
- content.textContent = "";
|
|
|
|
|
- content.appendChild(chatFrag);
|
|
|
|
|
//TODO scroll lock
|
|
//TODO scroll lock
|
|
|
|
|
+ var content = document.getElementById(R.id.currentRoom.content);
|
|
|
content.scrollTop = content.scrollHeight -content.clientHeight;
|
|
content.scrollTop = content.scrollHeight -content.clientHeight;
|
|
|
updateTitle();
|
|
updateTitle();
|
|
|
if (window.hasFocus)
|
|
if (window.hasFocus)
|
|
@@ -457,7 +540,9 @@ function displayTmpMessage(channel, input, isMeMessage) {
|
|
|
channel: channel.id,
|
|
channel: channel.id,
|
|
|
text: input.trim(),
|
|
text: input.trim(),
|
|
|
isMe: isMeMessage,
|
|
isMe: isMeMessage,
|
|
|
- dom: createTmpMsgDom(input, isMeMessage)
|
|
|
|
|
|
|
+ dom: createTmpMsgDom(input, isMeMessage),
|
|
|
|
|
+ ts: Date.now(),
|
|
|
|
|
+ id: "pending" +channel.id +Date.now()
|
|
|
};
|
|
};
|
|
|
PENDING_MESSAGES.push(obj);
|
|
PENDING_MESSAGES.push(obj);
|
|
|
if (channel === SELECTED_ROOM)
|
|
if (channel === SELECTED_ROOM)
|