-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathchat.js
More file actions
180 lines (161 loc) · 5.69 KB
/
chat.js
File metadata and controls
180 lines (161 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import { SOCKET_SCRIPT_CLOSED } from 'src/utils/1.variables.js';
import eventManager from 'src/utils/eventManager.js';
import { debug } from 'src/utils/debug.js';
import { global, globalSet } from 'src/utils/global.js';
import VarStore from 'src/utils/VarStore.js';
import { isActive, updateIfActive } from './session.js';
// TODO: Use Message object
// TODO: Better history management
let reconnectAttempts = 0;
const guestMode = VarStore(false);
const historyIds = new Set();
function handleClose(event) {
console.debug('Disconnected', event);
if (![1000, 1006].includes(event.code)) return;
setTimeout(reconnect, 1000 * reconnectAttempts);
}
function bind(socketChat) {
const oHandler = socketChat.onmessage;
socketChat.onmessage = (event) => {
const data = JSON.parse(event.data);
const { action } = data;
debug(data, `debugging.rawchat.${action}`);
if (action === 'getMessage' && data.idRoom) { // For new chat.
data.room = `chat-public-${data.idRoom}`;
data.open = global('openPublicChats').includes(data.idRoom);
} else if (action === 'getPrivateMessage' && data.idFriend) {
data.idRoom = data.idFriend;
data.room = `chat-private-${data.idRoom}`;
data.open = Array.isArray(global('privateChats')[data.idRoom]);
}
eventManager.emit('preChatMessage', data);
if (eventManager.cancelable.emit(`preChat:${action}`, data).canceled) return;
oHandler(event);
eventManager.emit('ChatMessage', data);
eventManager.emit(`Chat:${action}`, data);
if (action === 'getSelfInfos') {
eventManager.singleton.emit('Chat:Connected');
getMessages(data);
}
};
const oClose = socketChat.onclose;
socketChat.onclose = (e) => {
if (e.code !== SOCKET_SCRIPT_CLOSED) oClose();
eventManager.emit('Chat:Disconnected');
handleClose(e);
};
}
export default function reconnect(force = false) {
if (!force && (!isActive() || guestMode.isSet() || reconnectAttempts > 3 || global('socketChat', { throws: false })?.readyState !== WebSocket.CLOSED)) return;
reconnectAttempts += 1;
const socket = new WebSocket(`wss://${location.hostname}/chat`);
globalSet('socketChat', socket);
socket.onmessage = (event) => {
if (global('socketChat') !== socket) return;
const data = JSON.parse(event.data);
const { action } = data;
switch (action) {
case 'getSelfInfos': { // We're only connected if we get self info
// Reconnected
socket.onmessage = global('onMessageChat');
socket.onclose = global('onCloseChat');
bind(socket);
// Process Messages
const history = getMessages(data);
const append = global('appendMessage');
history.forEach((message) => {
if ($(`#message-${message.id}`).length) return;
append(message, message.idRoom, false);
});
eventManager.emit('Chat:Reconnected');
reconnectAttempts = 0;
break;
}
default: {
console.debug('Message:', action);
// Need to stop connecting?
socket.close(SOCKET_SCRIPT_CLOSED, 'reconnect');
}
}
};
socket.onclose = handleClose;
}
function getMessages({ discussionHistory, otherHistory }) {
if (!discussionHistory || !otherHistory) {
return [];
}
const history = [
...JSON.parse(discussionHistory),
...JSON.parse(otherHistory),
].filter(({ id }) => !historyIds.has(id));
history.forEach(({ id }) => historyIds.add(id));
return history;
}
function sendMessageWrapper(...args) {
if (global('socketChat').readyState !== WebSocket.OPEN) {
updateIfActive(); // TODO: Have a way to detect activity other than manually resetting it
reconnect();
eventManager.once('Chat:Reconnected', () => this.super(...args));
} else {
this.super(...args);
}
}
eventManager.on(':preload', () => {
if (typeof socketChat !== 'undefined') {
debug('Chat detected');
eventManager.singleton.emit('ChatDetected');
bind(global('socketChat'));
// Attempt to reconnect when sending messages
globalSet('sendMessage', sendMessageWrapper);
globalSet('sendPrivateMessage', sendMessageWrapper);
// Attempt to reconnect when coming back to this window
document.addEventListener('visibilitychange', () => reconnect());
// Simulate old getHistory
globalSet('appendChat', function appendChat(idRoom = '', chatMessages = [], isPrivate = true) {
const room = `chat-${isPrivate ? 'private' : 'public'}-${idRoom}`;
const newRoom = !document.querySelector(`#${room}`);
const data = {
idRoom,
room,
roomName: isPrivate ? '' : global('chatNames')[idRoom - 1] || '',
history: JSON.stringify(chatMessages), // TODO: Stop stringify
};
if (newRoom) {
eventManager.emit('preChat:getHistory', data);
}
this.super(idRoom, chatMessages, isPrivate);
if (newRoom) {
eventManager.emit('Chat:getHistory', data);
}
}, {
throws: false,
});
}
eventManager.on('Chat:getHistory', ({ room, roomName: name }) => {
// Send text hook
const messages = $(`#${room} .chat-messages`);
$(`#${room} input[type="text"]`).keydown(function sending(e) {
if (e.key !== 'Enter') return;
const data = {
room,
name,
messages,
input: this,
};
if (eventManager.cancelable.emit('Chat:send', data).canceled) {
debug('Canceled send');
$(this).val('');
e.preventDefault();
e.stopPropagation();
}
});
});
eventManager.on('GuestMode', () => {
console.debug('Guest Mode');
guestMode.set(true);
});
eventManager.on('Chat:Reconnected', () => {
console.debug('Reconnected');
$('.chat-messages').find('.red:last').remove();
});
});