-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient.js
147 lines (110 loc) · 4.42 KB
/
client.js
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
/**
* Manages client states.
*/
import { Commands, Engine, Events } from "./engine.js"
import { renderMessage, renderMarkdown } from "./render-message.js"
engine = new Engine(location.search.split('@', 2).length > 1 ? location.search.split('@', 2)[1] : "wss://hack.chat/chat-ws")
/**
* @param {Msg} args
* @param {object} options
* @param {HTMLElement?} options.target
* @param {RenderMode} options.renderMode
*/
const pushMessage = (args, { target, renderMode } = {}) => {
let id = messages.length
messages[id] = {
id,
args,
}
let messageEl = renderMessage(args, { target, renderMode })
messageEl.id = `message_${id}`
messages[id].element = messageEl
const wasAtBottom = isAtBottom()
messagesEl.appendChild(messageEl)
if (myChannel !== '' && wasAtBottom) {
scrollToBottom()
}
}
globalThis.pushMessage = pushMessage
engine.on(Commands.chat, (/**@type {Msg}*/args) => {
pushMessage(args)
})
engine.on(Commands.info, (/**@type {Msg}*/args) => {
pushMessage({ ...args, 'nick': '*' })
})
engine.on(Commands.emote, (/**@type {Msg}*/args) => {
pushMessage({ ...args, 'nick': '*' })
})
engine.on(Commands.warn, (/**@type {Msg}*/args) => {
pushMessage({ ...args, 'nick': '!' })
})
engine.on(Commands.onlineAdd, (/**@type {Msg}*/args) => {
$onlineUsers.$.push(args)
pushMessage({ ...args, 'nick': '*', text: `${args.nick} joined` })
})
engine.on(Commands.onlineRemove, (/**@type {Msg}*/args) => {
$onlineUsers.$ = $onlineUsers.$.filter(user => user.nick !== args.nick)
pushMessage({ ...args, 'nick': '*', text: `${args.nick} left` })
})
engine.on(Commands.captcha, (/**@type {Msg}*/args) => {
const NS = 'http://www.w3.org/2000/svg'
let messageEl = document.createElement('div');
messageEl.classList.add('message', 'info');
let nickSpanEl = document.createElement('span');
nickSpanEl.classList.add('nick');
messageEl.appendChild(nickSpanEl);
let nickLinkEl = document.createElement('a');
nickLinkEl.textContent = '#';
nickSpanEl.appendChild(nickLinkEl);
let pEl = document.createElement('p')
pEl.classList.add('text')
let lines = args.text.split(/\n/g)
// Core principle: In SVG text can be smaller than 12px even in Chrome.
let svgEl = document.createElementNS(NS, 'svg')
svgEl.setAttribute('white-space', 'pre')
svgEl.style.backgroundColor = '#4e4e4e'
svgEl.style.width = '100%'
// In order to make 40em work right.
svgEl.style.fontSize = `${messageEl.clientWidth / lines[0].length * 1.5}px`
// Captcha text is about 41 lines.
svgEl.style.height = '41em'
// I have tried `white-space: pre` but it didn't work, so I write each line in individual text tags.
for (let i = 0; i < lines.length; i++) {
let line = lines[i]
let textEl = document.createElementNS(NS, 'text')
textEl.innerHTML = line
// In order to make it in the right position.
textEl.setAttribute('y', `${i + 1}em`)
// Captcha text shouldn't overflow #messages element, so I divide the width of the messages container with the overvalued length of each line in order to get an undervalued max width of each character, and than multiply it by 2 (The overvalued aspect ratio of a character) because the font-size attribute means the height of a character.
textEl.setAttribute('font-size', `${messageEl.clientWidth / lines[0].length * 1.5}px`)
textEl.setAttribute('fill', 'white')
// Preserve spaces.
textEl.style.whiteSpace = 'pre'
svgEl.appendChild(textEl)
}
pEl.appendChild(svgEl)
messageEl.appendChild(pEl);
messagesEl.appendChild(messageEl);
scrollToBottom()
})
engine.on(Commands.onlineSet, (/**@type {Msg}*/args) => {
$onlineUsers.$ = args.users
const nicksHTML = args.nicks.map((nick) => {
if (nick.match(/^_+$/)) {
return nick // such nicknames made up of only underlines will be rendered into a horizontal rule.
}
const div = document.createElement('div')
div.innerHTML = renderMarkdown(nick)
return div.firstChild.innerHTML
})
pushMessage({ nick: '*', "text": "Users online: " + nicksHTML.join(", ") }, { renderMode: "onlyHTML" })
})
engine.on(Commands.updateUser, (/**@type {Msg}*/args) => {
let index = $onlineUsers.$.findIndex(user => user.nick === args.nick)
$onlineUsers.$[index] = { ...$onlineUsers[index], ...args }
})
if (isDebug) {
engine.on(Events.rawMessage, (msg) => {
pushMessage({ nick: '*', text: `<details><summary>Raw Data</summary>\n\`\`\`json\n${msg}\n\`\`\`\n</details>` }, { renderMode: "allowHTML" })
})
}