Spaces:
Running
Running
Update static/script.js
Browse files- static/script.js +63 -11
static/script.js
CHANGED
|
@@ -124,7 +124,8 @@
|
|
| 124 |
convOptionsBtn: $('conv-options-btn'), convOptionsModal: $('conv-options-modal'),
|
| 125 |
convOptionsTitle: $('conv-options-title'),
|
| 126 |
renameConvBtn: $('rename-conv-btn'), addGroupMembersBtn: $('add-group-members-btn'),
|
| 127 |
-
|
|
|
|
| 128 |
refreshBlocksBtn: $('refresh-blocks-btn'), blockedUsersList: $('blocked-users-list'),
|
| 129 |
|
| 130 |
settingsDisplayName: $('settings-display-name'), settingsUsername: $('settings-username'),
|
|
@@ -758,10 +759,14 @@
|
|
| 758 |
State.isAtBottom = true;
|
| 759 |
|
| 760 |
const cache = getConv(convId);
|
|
|
|
| 761 |
renderConversationHeader(convId);
|
| 762 |
renderSidebar();
|
| 763 |
|
| 764 |
-
if (
|
|
|
|
|
|
|
|
|
|
| 765 |
cache.loading = true;
|
| 766 |
loadMessages(convId);
|
| 767 |
renderEmptyLoading(convId);
|
|
@@ -791,6 +796,18 @@
|
|
| 791 |
DOM.loadOlderBtn.classList.add('hidden');
|
| 792 |
}
|
| 793 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 794 |
function renderConversationHeader(convId) {
|
| 795 |
const conv = convById(convId);
|
| 796 |
if (!conv) return;
|
|
@@ -813,9 +830,15 @@
|
|
| 813 |
const online = peer.online || State.onlineUsers.has(peer.id);
|
| 814 |
DOM.convPresenceDot.classList.remove('hidden');
|
| 815 |
DOM.convPresenceDot.classList.toggle('offline', !online);
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 |
DOM.onlineBtn.style.display = '';
|
| 820 |
DOM.convAvatarPh.style.display = '';
|
| 821 |
DOM.convAvatar.style.display = '';
|
|
@@ -1018,6 +1041,9 @@
|
|
| 1018 |
if (msg.conversation.id === State.activeConvId) {
|
| 1019 |
renderConversationHeader(State.activeConvId);
|
| 1020 |
}
|
|
|
|
|
|
|
|
|
|
| 1021 |
break;
|
| 1022 |
|
| 1023 |
case 'conversation_deleted': {
|
|
@@ -1027,7 +1053,7 @@
|
|
| 1027 |
State.messages.forEach(m => { if (m.conversation_id === cid) State.messages.delete(m.id); });
|
| 1028 |
if (State.activeConvId === cid) switchConversation(GLOBAL_CONV);
|
| 1029 |
renderSidebar();
|
| 1030 |
-
showToast('
|
| 1031 |
break;
|
| 1032 |
}
|
| 1033 |
|
|
@@ -1039,6 +1065,10 @@
|
|
| 1039 |
if (State.activeConvId === cid) switchConversation(GLOBAL_CONV);
|
| 1040 |
} else if (msg.conversation) {
|
| 1041 |
upsertConversation(msg.conversation);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1042 |
}
|
| 1043 |
renderSidebar();
|
| 1044 |
break;
|
|
@@ -1066,7 +1096,7 @@
|
|
| 1066 |
upsertConversation(msg.conversation);
|
| 1067 |
const peer = msg.conversation.peer;
|
| 1068 |
if (peer && peer.id !== State.user?.id) {
|
| 1069 |
-
showToast(`Private chat with ${displayNameOf(peer)}
|
| 1070 |
switchConversation(msg.conversation.id);
|
| 1071 |
}
|
| 1072 |
break;
|
|
@@ -2068,7 +2098,7 @@
|
|
| 2068 |
if (!content && !State.pendingFile) return;
|
| 2069 |
if (!State.activeConvId) { showToast('No conversation selected', 'warning'); return; }
|
| 2070 |
const active = convById(State.activeConvId);
|
| 2071 |
-
if (active && active.is_pending) { showToast('Accept the
|
| 2072 |
if (!wsOpen()) {
|
| 2073 |
showToast('Not connected - message queued, sending when reconnected', 'warning');
|
| 2074 |
}
|
|
@@ -2224,7 +2254,7 @@
|
|
| 2224 |
upsertConversation(data.conversation);
|
| 2225 |
closeModal(DOM.convModal);
|
| 2226 |
switchConversation(data.conversation.id);
|
| 2227 |
-
showToast(`Private chat with ${peerName} started`, 'success');
|
| 2228 |
} catch (err) {
|
| 2229 |
showToast(err.message, 'error');
|
| 2230 |
finish();
|
|
@@ -2516,12 +2546,12 @@
|
|
| 2516 |
if (action === 'accept') {
|
| 2517 |
upsertConversation(data.conversation);
|
| 2518 |
switchConversation(cid);
|
| 2519 |
-
showToast('
|
| 2520 |
} else {
|
| 2521 |
State.conversations.delete(cid);
|
| 2522 |
State.convCache.delete(cid);
|
| 2523 |
renderSidebar();
|
| 2524 |
-
showToast('
|
| 2525 |
}
|
| 2526 |
} catch (err) {
|
| 2527 |
showToast(err.message, 'error');
|
|
@@ -2656,6 +2686,7 @@
|
|
| 2656 |
const pending = !!conv.is_pending;
|
| 2657 |
DOM.renameConvBtn.style.display = pending ? 'none' : '';
|
| 2658 |
DOM.addGroupMembersBtn.style.display = conv.is_group && !pending ? '' : 'none';
|
|
|
|
| 2659 |
DOM.blockConvUserBtn.style.display = (!conv.is_group && conv.peer) ? '' : 'none';
|
| 2660 |
DOM.leaveConvBtn.style.display = (conv.is_group && !pending) ? '' : 'none';
|
| 2661 |
DOM.convOptionsTitle.innerHTML = `<i class="fas fa-ellipsis-h"></i> ${escapeHtml(conv.custom_name || conv.title || 'Chat Options')}`;
|
|
@@ -2695,6 +2726,27 @@
|
|
| 2695 |
openModal(DOM.groupModal);
|
| 2696 |
});
|
| 2697 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2698 |
DOM.blockConvUserBtn.addEventListener('click', async () => {
|
| 2699 |
const conv = convById(State.activeConvId);
|
| 2700 |
closeModal(DOM.convOptionsModal);
|
|
|
|
| 124 |
convOptionsBtn: $('conv-options-btn'), convOptionsModal: $('conv-options-modal'),
|
| 125 |
convOptionsTitle: $('conv-options-title'),
|
| 126 |
renameConvBtn: $('rename-conv-btn'), addGroupMembersBtn: $('add-group-members-btn'),
|
| 127 |
+
deleteConvBtn: $('delete-conv-btn'), blockConvUserBtn: $('block-conv-user-btn'),
|
| 128 |
+
leaveConvBtn: $('leave-conv-btn'),
|
| 129 |
refreshBlocksBtn: $('refresh-blocks-btn'), blockedUsersList: $('blocked-users-list'),
|
| 130 |
|
| 131 |
settingsDisplayName: $('settings-display-name'), settingsUsername: $('settings-username'),
|
|
|
|
| 759 |
State.isAtBottom = true;
|
| 760 |
|
| 761 |
const cache = getConv(convId);
|
| 762 |
+
const conv = convById(convId);
|
| 763 |
renderConversationHeader(convId);
|
| 764 |
renderSidebar();
|
| 765 |
|
| 766 |
+
if (conv && conv.is_pending) {
|
| 767 |
+
cache.loading = false;
|
| 768 |
+
renderPendingInvite(convId);
|
| 769 |
+
} else if (cache.ids.length === 0) {
|
| 770 |
cache.loading = true;
|
| 771 |
loadMessages(convId);
|
| 772 |
renderEmptyLoading(convId);
|
|
|
|
| 796 |
DOM.loadOlderBtn.classList.add('hidden');
|
| 797 |
}
|
| 798 |
|
| 799 |
+
function renderPendingInvite(convId) {
|
| 800 |
+
DOM.messagesList.innerHTML = '';
|
| 801 |
+
if (DOM.emptyState.parentNode !== DOM.messagesList) DOM.messagesList.appendChild(DOM.emptyState);
|
| 802 |
+
const conv = convById(convId);
|
| 803 |
+
const name = conv ? (conv.custom_name || conv.title || (conv.peer ? displayNameOf(conv.peer) : 'this chat')) : 'this chat';
|
| 804 |
+
DOM.emptyIcon.innerHTML = '<i class="fas fa-envelope-open-text"></i>';
|
| 805 |
+
DOM.emptyTitle.textContent = 'Invite pending';
|
| 806 |
+
DOM.emptySub.textContent = `Accept the invite for ${name} from the sidebar to start chatting.`;
|
| 807 |
+
DOM.emptyState.style.display = 'flex';
|
| 808 |
+
DOM.loadOlderBtn.classList.add('hidden');
|
| 809 |
+
}
|
| 810 |
+
|
| 811 |
function renderConversationHeader(convId) {
|
| 812 |
const conv = convById(convId);
|
| 813 |
if (!conv) return;
|
|
|
|
| 830 |
const online = peer.online || State.onlineUsers.has(peer.id);
|
| 831 |
DOM.convPresenceDot.classList.remove('hidden');
|
| 832 |
DOM.convPresenceDot.classList.toggle('offline', !online);
|
| 833 |
+
if (conv.peer_removed) {
|
| 834 |
+
DOM.convSubtitle.innerHTML = '<span class="status-dot warn"></span> They no longer want to chat';
|
| 835 |
+
} else if (conv.is_pending) {
|
| 836 |
+
DOM.convSubtitle.innerHTML = '<span class="status-dot warn"></span> Invite pending — accept to start chatting';
|
| 837 |
+
} else {
|
| 838 |
+
DOM.convSubtitle.innerHTML = online
|
| 839 |
+
? '<span class="status-dot online"></span> Online'
|
| 840 |
+
: escapeHtml(formatLastSeen(peer.last_seen));
|
| 841 |
+
}
|
| 842 |
DOM.onlineBtn.style.display = '';
|
| 843 |
DOM.convAvatarPh.style.display = '';
|
| 844 |
DOM.convAvatar.style.display = '';
|
|
|
|
| 1041 |
if (msg.conversation.id === State.activeConvId) {
|
| 1042 |
renderConversationHeader(State.activeConvId);
|
| 1043 |
}
|
| 1044 |
+
if (msg.invite_rejected && msg.rejected_user_id && msg.rejected_user_id !== State.user?.id) {
|
| 1045 |
+
showToast('The invite was declined', 'info');
|
| 1046 |
+
}
|
| 1047 |
break;
|
| 1048 |
|
| 1049 |
case 'conversation_deleted': {
|
|
|
|
| 1053 |
State.messages.forEach(m => { if (m.conversation_id === cid) State.messages.delete(m.id); });
|
| 1054 |
if (State.activeConvId === cid) switchConversation(GLOBAL_CONV);
|
| 1055 |
renderSidebar();
|
| 1056 |
+
showToast('The chat was deleted', 'info');
|
| 1057 |
break;
|
| 1058 |
}
|
| 1059 |
|
|
|
|
| 1065 |
if (State.activeConvId === cid) switchConversation(GLOBAL_CONV);
|
| 1066 |
} else if (msg.conversation) {
|
| 1067 |
upsertConversation(msg.conversation);
|
| 1068 |
+
if (msg.reason === 'deleted') {
|
| 1069 |
+
const peer = msg.conversation.peer;
|
| 1070 |
+
showToast(`${peer ? displayNameOf(peer) : 'The other person'} no longer wants to chat`, 'info');
|
| 1071 |
+
}
|
| 1072 |
}
|
| 1073 |
renderSidebar();
|
| 1074 |
break;
|
|
|
|
| 1096 |
upsertConversation(msg.conversation);
|
| 1097 |
const peer = msg.conversation.peer;
|
| 1098 |
if (peer && peer.id !== State.user?.id) {
|
| 1099 |
+
showToast(`Private chat with ${displayNameOf(peer)} started — waiting for them to accept`, 'success');
|
| 1100 |
switchConversation(msg.conversation.id);
|
| 1101 |
}
|
| 1102 |
break;
|
|
|
|
| 2098 |
if (!content && !State.pendingFile) return;
|
| 2099 |
if (!State.activeConvId) { showToast('No conversation selected', 'warning'); return; }
|
| 2100 |
const active = convById(State.activeConvId);
|
| 2101 |
+
if (active && active.is_pending) { showToast('Accept the invite first', 'warning'); return; }
|
| 2102 |
if (!wsOpen()) {
|
| 2103 |
showToast('Not connected - message queued, sending when reconnected', 'warning');
|
| 2104 |
}
|
|
|
|
| 2254 |
upsertConversation(data.conversation);
|
| 2255 |
closeModal(DOM.convModal);
|
| 2256 |
switchConversation(data.conversation.id);
|
| 2257 |
+
showToast(`Private chat with ${peerName} started — waiting for them to accept`, 'success');
|
| 2258 |
} catch (err) {
|
| 2259 |
showToast(err.message, 'error');
|
| 2260 |
finish();
|
|
|
|
| 2546 |
if (action === 'accept') {
|
| 2547 |
upsertConversation(data.conversation);
|
| 2548 |
switchConversation(cid);
|
| 2549 |
+
showToast('Invite accepted', 'success');
|
| 2550 |
} else {
|
| 2551 |
State.conversations.delete(cid);
|
| 2552 |
State.convCache.delete(cid);
|
| 2553 |
renderSidebar();
|
| 2554 |
+
showToast('Invite declined', 'info');
|
| 2555 |
}
|
| 2556 |
} catch (err) {
|
| 2557 |
showToast(err.message, 'error');
|
|
|
|
| 2686 |
const pending = !!conv.is_pending;
|
| 2687 |
DOM.renameConvBtn.style.display = pending ? 'none' : '';
|
| 2688 |
DOM.addGroupMembersBtn.style.display = conv.is_group && !pending ? '' : 'none';
|
| 2689 |
+
DOM.deleteConvBtn.style.display = (!conv.is_group && conv.peer && !pending) ? '' : 'none';
|
| 2690 |
DOM.blockConvUserBtn.style.display = (!conv.is_group && conv.peer) ? '' : 'none';
|
| 2691 |
DOM.leaveConvBtn.style.display = (conv.is_group && !pending) ? '' : 'none';
|
| 2692 |
DOM.convOptionsTitle.innerHTML = `<i class="fas fa-ellipsis-h"></i> ${escapeHtml(conv.custom_name || conv.title || 'Chat Options')}`;
|
|
|
|
| 2726 |
openModal(DOM.groupModal);
|
| 2727 |
});
|
| 2728 |
|
| 2729 |
+
DOM.deleteConvBtn.addEventListener('click', async () => {
|
| 2730 |
+
const conv = convById(State.activeConvId);
|
| 2731 |
+
closeModal(DOM.convOptionsModal);
|
| 2732 |
+
if (!conv || conv.is_group || !conv.peer) return;
|
| 2733 |
+
const ok = await showConfirm({
|
| 2734 |
+
title: 'Delete private chat',
|
| 2735 |
+
message: `Delete this chat with ${displayNameOf(conv.peer)}? It will be hidden for you. If they also delete it, it is removed for everyone. They will be told that you no longer want to chat.`,
|
| 2736 |
+
okText: 'Delete Chat'
|
| 2737 |
+
});
|
| 2738 |
+
if (!ok) return;
|
| 2739 |
+
try {
|
| 2740 |
+
const data = await apiDelete(`/api/conversations/${conv.id}`);
|
| 2741 |
+
State.conversations.delete(conv.id);
|
| 2742 |
+
State.convCache.delete(conv.id);
|
| 2743 |
+
if (State.activeConvId === conv.id) switchConversation(GLOBAL_CONV);
|
| 2744 |
+
renderSidebar();
|
| 2745 |
+
showToast(data.status === 'deleted' ? 'Chat deleted' : 'Chat hidden for you', 'success');
|
| 2746 |
+
requestConversations();
|
| 2747 |
+
} catch (err) { showToast(err.message, 'error'); }
|
| 2748 |
+
});
|
| 2749 |
+
|
| 2750 |
DOM.blockConvUserBtn.addEventListener('click', async () => {
|
| 2751 |
const conv = convById(State.activeConvId);
|
| 2752 |
closeModal(DOM.convOptionsModal);
|