From ae69319e630f590d158349cadca47e5be7e6b0e6 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Fri, 30 May 2025 05:42:29 +0000 Subject: [PATCH] Preact minor updates batch 25 - Fix various reconnect bugs - Move table styling to battle-log - Fix highlighting bugs - Bump cookie expiration another month Trivial - Fix rounding in build time - Fix left border in vertical tabs dark mode - Improve README wording --- README.md | 6 +-- build-tools/update | 2 +- play.pokemonshowdown.com/src/battle-log.ts | 17 ++----- .../src/client-connection.ts | 36 +++++++------- play.pokemonshowdown.com/src/client-main.ts | 47 +++++++++++-------- play.pokemonshowdown.com/src/panel-chat.tsx | 31 ++++++++---- .../src/panel-mainmenu.tsx | 2 +- play.pokemonshowdown.com/src/panel-topbar.tsx | 2 +- play.pokemonshowdown.com/style/battle-log.css | 29 ++++++++++++ play.pokemonshowdown.com/style/battle.css | 2 +- play.pokemonshowdown.com/style/client2.css | 32 ++----------- 11 files changed, 108 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 2b3122697..1c5c67c29 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,8 @@ require v20 or later) and Git, and run `node build` (on Windows) or `./build` (on other OSes) to build. You can make and test client changes simply by building after each change, -and opening `testclient.html`. This will allow you to test changes to the -client without setting up your own login server. +and opening `play.pokemonshowdown.com/testclient.html`. This will allow you +to test changes to the client without setting up your own login server. ### Test keys @@ -78,7 +78,7 @@ grab it from: Make sure to put it in `config/` and not `play.pokemonshowdown.com/config/`. -(This is the only supported method of logging in on the beta Preact testclient.) +(This is the only supported method of logging in on the beta testclient.) [5]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS diff --git a/build-tools/update b/build-tools/update index 25393f95b..18733bcd5 100755 --- a/build-tools/update +++ b/build-tools/update @@ -137,7 +137,7 @@ if (!compileOpts.ignore) { const diff = process.hrtime(compileStartTime); console.log( - `(${compiledFiles} ${compiledFiles !== 1 ? "files" : "file"} in ${diff[0] + Math.round(diff[1] / 1e6) / 1e3}s) DONE` + `(${compiledFiles} ${compiledFiles !== 1 ? "files" : "file"} in ${(diff[0] + diff[1] / 1e9).toFixed(3)}s) DONE` ); /********************************************************* diff --git a/play.pokemonshowdown.com/src/battle-log.ts b/play.pokemonshowdown.com/src/battle-log.ts index 0473da95d..393628767 100644 --- a/play.pokemonshowdown.com/src/battle-log.ts +++ b/play.pokemonshowdown.com/src/battle-log.ts @@ -183,16 +183,7 @@ export class BattleLog { [divClass, divHTML, noNotify] = this.parseChatMessage(message, name, timestampHtml, isHighlighted); if (!noNotify && isHighlighted) { const notifyTitle = "Mentioned by " + name + " in " + (battle?.roomid || ''); - if (window.PS) { - const room = window.PS.rooms[battle?.roomid || '']; - room.notify({ - title: notifyTitle, - body: `"${message}"`, - id: 'highlight', - }); - } else { - window.app?.rooms[battle?.roomid || '']?.notifyOnce(notifyTitle, "\"" + message + "\"", 'highlight'); - } + window.app?.rooms[battle?.roomid || '']?.notifyOnce(notifyTitle, "\"" + message + "\"", 'highlight'); } break; @@ -1238,9 +1229,9 @@ export class BattleLog { } const colorStyle = ` style="color:${BattleLog.usernameColor(toID(name))}"`; const clickableName = `${BattleLog.escapeHTML(group)}${BattleLog.escapeHTML(name)}`; - let hlClass = isHighlighted ? ' highlighted' : ''; - let isMine = (window.app?.user?.get('name') === name) || (window.PS?.user.name === name); - let mineClass = isMine ? ' mine' : ''; + const isMine = (window.app?.user?.get('name') === name) || (window.PS?.user.name === name); + const hlClass = isHighlighted ? ' highlighted' : ''; + const mineClass = isMine ? ' mine' : ''; let cmd = ''; let target = ''; diff --git a/play.pokemonshowdown.com/src/client-connection.ts b/play.pokemonshowdown.com/src/client-connection.ts index 488276487..e673789d6 100644 --- a/play.pokemonshowdown.com/src/client-connection.ts +++ b/play.pokemonshowdown.com/src/client-connection.ts @@ -50,6 +50,12 @@ export class PSConnection { tryConnectInWorker(): boolean { if (this.socket) return false; // must be one or the other + if (this.connected) return true; + + if (this.worker) { + this.worker.postMessage({ type: 'connect', server: PS.server }); + return true; + } try { const worker = new Worker('/js/client-connection-worker.js'); @@ -63,7 +69,6 @@ export class PSConnection { case 'connected': console.log('\u2705 (CONNECTED via worker)'); this.connected = true; - PS.connected = true; this.queue.forEach(msg => worker.postMessage({ type: 'send', data: msg })); this.queue = []; PS.update(); @@ -84,8 +89,8 @@ export class PSConnection { } }; - worker.onerror = (e: ErrorEvent) => { - console.warn('Worker connection error:', e); + worker.onerror = (ev: ErrorEvent) => { + console.warn('Worker connection error:', ev); this.worker = null; this.directConnect(); // fallback }; @@ -116,7 +121,6 @@ export class PSConnection { socket.onopen = () => { console.log('\u2705 (CONNECTED)'); this.connected = true; - PS.connected = true; this.reconnectDelay = 1000; this.queue.forEach(msg => socket.send(msg)); this.queue = []; @@ -132,7 +136,6 @@ export class PSConnection { this.handleDisconnect(); console.log('\u2705 (DISCONNECTED)'); this.connected = false; - PS.connected = false; PS.isOffline = true; for (const roomid in PS.rooms) { const room = PS.rooms[roomid]!; @@ -143,10 +146,8 @@ export class PSConnection { }; socket.onerror = (ev: Event) => { - PS.connected = false; PS.isOffline = true; // no useful info to print from the event - PS.alert(`Connection error`); this.retryConnection(); PS.update(); }; @@ -154,7 +155,6 @@ export class PSConnection { private handleDisconnect() { this.connected = false; - PS.connected = false; PS.isOffline = true; this.socket = null; for (const roomid in PS.rooms) { @@ -168,12 +168,14 @@ export class PSConnection { private retryConnection() { if (!this.canReconnect()) return; if (this.reconnectTimer) return; + this.reconnectTimer = setTimeout(() => { this.reconnectTimer = null; if (!this.connected && this.canReconnect()) { PS.mainmenu.send('/reconnect'); this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.reconnectCap); } + PS.update(); }, this.reconnectDelay); } @@ -182,17 +184,13 @@ export class PSConnection { this.socket?.close(); this.worker?.terminate(); this.worker = null; - PS.connection = null; - PS.connected = false; - PS.isOffline = true; + this.handleDisconnect(); + PS.update(); } - - reconnectTest() { - this.socket?.close(); - this.worker?.postMessage({ type: 'disconnect' }); - this.worker = null; - PS.connected = false; - PS.isOffline = true; + reconnect() { + if (this.connected) return; + if (this.worker && this.tryConnectInWorker()) return; + this.directConnect(); } send(msg: string) { @@ -213,7 +211,7 @@ export class PSConnection { if (!PS.connection) { PS.connection = new PSConnection(); } else { - PS.connection.directConnect(); + PS.connection.reconnect(); } PS.prefs.doAutojoin(); } diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index b44c387b4..1be1f2d45 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -1042,13 +1042,16 @@ export class PSRoom extends PSStreamModel implements RoomOptions { this.isSubtleNotifying = true; PS.update(); } + dismissNotificationAt(i: number) { + try { + this.notifications[i].notification?.close(); + } catch {} + this.notifications.splice(i, 1); + } dismissNotification(id: string) { const index = this.notifications.findIndex(n => n.id === id); if (index !== -1) { - try { - this.notifications[index].notification?.close(); - } catch {} - this.notifications.splice(index, 1); + this.dismissNotificationAt(index); } PS.update(); } @@ -1061,7 +1064,11 @@ export class PSRoom extends PSStreamModel implements RoomOptions { lastMessageDates[PS.server.id][room.id] = room.lastMessageTime || 0; PS.prefs.set('logtimes', lastMessageDates); } - this.notifications = this.notifications.filter(notification => notification.noAutoDismiss); + for (let i = this.notifications.length - 1; i >= 0; i--) { + if (!this.notifications[i].noAutoDismiss) { + this.dismissNotificationAt(i); + } + } this.isSubtleNotifying = false; } connect(): void { @@ -1184,10 +1191,22 @@ export class PSRoom extends PSStreamModel implements RoomOptions { 'logout'() { PS.user.logOut(); }, - 'reconnect'() { - if (!PS.isOffline) { - return this.add(`|error|You are already connected.`); + 'reconnect,connect'() { + if (this.connected && this.connected !== 'autoreconnect') { + return this.errorReply(`You are already connected.`); } + + if (!PS.isOffline) { + // connect to room + try { + this.connect(); + } catch (err: any) { + this.errorReply(err.message); + } + return; + } + + // connect to server const uptime = Date.now() - PS.startTime; if (uptime > 24 * 60 * 60 * 1000) { PS.confirm(`It's been over a day since you first connected. Please refresh.`, { @@ -1207,17 +1226,6 @@ export class PSRoom extends PSStreamModel implements RoomOptions { return this.add(`|error|You are already offline.`); } PS.connection?.disconnect(); - this.add(`||You are now offline.`); - }, - 'connect'() { - if (this.connected && this.connected !== 'autoreconnect') { - return this.errorReply(`You are already connected.`); - } - try { - this.connect(); - } catch (err: any) { - this.errorReply(err.message); - } }, 'cancelsearch'() { if (PS.mainmenu.cancelSearch()) { @@ -1730,7 +1738,6 @@ export const PS = new class extends PSModel { user = new PSUser(); server = new PSServer(); connection: PSConnection | null = null; - connected = false; /** * While PS is technically disconnected while it's trying to connect, * it still shows UI like it's connected, so you can click buttons diff --git a/play.pokemonshowdown.com/src/panel-chat.tsx b/play.pokemonshowdown.com/src/panel-chat.tsx index 367d16e5f..c86425006 100644 --- a/play.pokemonshowdown.com/src/panel-chat.tsx +++ b/play.pokemonshowdown.com/src/panel-chat.tsx @@ -250,31 +250,42 @@ export class ChatRoom extends PSRoom { handleHighlight = (args: Args) => { let name; let message; - let msgTime = 0; + let serverTime = 0; if (args[0] === 'c:') { - msgTime = parseInt(args[1]); + serverTime = parseInt(args[1]); name = args[2]; message = args[3]; } else { name = args[1]; message = args[2]; } - let lastMessageDates = Dex.prefs('logtimes') || (PS.prefs.set('logtimes', {}), Dex.prefs('logtimes')); + if (toID(name) === PS.user.userid) return false; + if (message.startsWith(`/raw `)) return false; + + const lastMessageDates = Dex.prefs('logtimes') || (PS.prefs.set('logtimes', {}), Dex.prefs('logtimes')); if (!lastMessageDates[PS.server.id]) lastMessageDates[PS.server.id] = {}; - let lastMessageDate = lastMessageDates[PS.server.id][this.id] || 0; + const lastMessageDate = lastMessageDates[PS.server.id][this.id] || 0; // because the time offset to the server can vary slightly, subtract it to not have it affect comparisons between dates - let serverMsgTime = msgTime - (this.timeOffset || 0); - let mayNotify = serverMsgTime > lastMessageDate && name !== PS.user.userid; + const time = serverTime - (this.timeOffset || 0); if (PS.isVisible(this)) { this.lastMessageTime = null; - lastMessageDates[PS.server.id][this.id] = serverMsgTime; + lastMessageDates[PS.server.id][this.id] = time; PS.prefs.set('logtimes', lastMessageDates); } else { // To be saved on focus - let lastMessageTime = this.lastMessageTime || 0; - if (lastMessageTime < serverMsgTime) this.lastMessageTime = serverMsgTime; + const lastMessageTime = this.lastMessageTime || 0; + if (lastMessageTime < time) this.lastMessageTime = time; } - return !!(ChatRoom.getHighlight(message, this.id) && mayNotify); + if (ChatRoom.getHighlight(message, this.id)) { + const mayNotify = time > lastMessageDate; + if (mayNotify) this.notify({ + title: `Mentioned by ${name} in ${this.id}`, + body: `"${message}"`, + id: 'highlight', + }); + return true; + } + return false; }; override clientCommands = this.parseClientCommands({ 'chall,challenge'(target) { diff --git a/play.pokemonshowdown.com/src/panel-mainmenu.tsx b/play.pokemonshowdown.com/src/panel-mainmenu.tsx index 517e3cbd2..5916dbbcd 100644 --- a/play.pokemonshowdown.com/src/panel-mainmenu.tsx +++ b/play.pokemonshowdown.com/src/panel-mainmenu.tsx @@ -437,7 +437,7 @@ class NewsPanel extends PSRoomPanel { change = (ev: Event) => { const target = ev.currentTarget as HTMLInputElement; if (target.value === '1') { - document.cookie = "preactalpha=1; expires=Thu, 1 Jun 2025 12:00:00 UTC; path=/"; + document.cookie = "preactalpha=1; expires=Thu, 1 Jul 2025 12:00:00 UTC; path=/"; } else { document.cookie = "preactalpha=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; } diff --git a/play.pokemonshowdown.com/src/panel-topbar.tsx b/play.pokemonshowdown.com/src/panel-topbar.tsx index dca2ba993..e81a9e010 100644 --- a/play.pokemonshowdown.com/src/panel-topbar.tsx +++ b/play.pokemonshowdown.com/src/panel-topbar.tsx @@ -217,7 +217,7 @@ export class PSHeader extends preact.Component { this.handleResize(); } renderUser() { - if (!PS.connected) { + if (!PS.connection?.connected) { return ; } if (PS.user.initializing) { diff --git a/play.pokemonshowdown.com/style/battle-log.css b/play.pokemonshowdown.com/style/battle-log.css index b5ac18833..1b934b9f0 100644 --- a/play.pokemonshowdown.com/style/battle-log.css +++ b/play.pokemonshowdown.com/style/battle-log.css @@ -622,6 +622,35 @@ input[type=range]:active::-webkit-slider-thumb { } } +/********************************************************* + * Table + *********************************************************/ + +.table-header { + position: relative; + position: sticky; + top: 0; +} +.ladder table, .ladder td, .ladder th, +.table, .table td, .table th { + border-collapse: collapse; + border: 1px solid #BBBBBB; +} +.ladder td, .ladder th, +.table td, .table th { + padding: 3px 5px; +} +.ladder th, .table th { + text-align: left; + font-size: 9pt; + background: #EEEEEE; + color: #111111; +} + +.hidden { + display: none; +} + /********************************************************* * Everything else *********************************************************/ diff --git a/play.pokemonshowdown.com/style/battle.css b/play.pokemonshowdown.com/style/battle.css index 02f60eaa5..bf8bed318 100644 --- a/play.pokemonshowdown.com/style/battle.css +++ b/play.pokemonshowdown.com/style/battle.css @@ -5,7 +5,7 @@ License: GPLv2 */ -@import url(./battle-log.css?v9.8); +@import url(./battle-log.css?v10); .battle { position: absolute; diff --git a/play.pokemonshowdown.com/style/client2.css b/play.pokemonshowdown.com/style/client2.css index 55632cdec..7b9579a92 100644 --- a/play.pokemonshowdown.com/style/client2.css +++ b/play.pokemonshowdown.com/style/client2.css @@ -566,6 +566,9 @@ summary:hover .expandbutton, border-left: 1px solid #AAAAAA; margin-left: -1px; } +.dark .ps-room { + border-color: #34373b; +} .scrollable { overflow: auto; -webkit-overflow-scrolling: touch; @@ -1029,35 +1032,6 @@ form.menugroup { .button.mainmenu6:hover { background: linear-gradient(to bottom, hsl(270,40%,62%), hsl(270,40%,42%)); } .button.mainmenu6:active { background: linear-gradient(to bottom, hsl(270,40%,42%), hsl(300,40%,58%)); } -/********************************************************* - * Ladder - *********************************************************/ - -.table-header { - position: relative; - position: sticky; - top: 0; -} -.ladder table, .ladder td, .ladder th, -.table, .table td, .table th { - border-collapse: collapse; - border: 1px solid #BBBBBB; -} -.ladder td, .ladder th, -.table td, .table th { - padding: 3px 5px; -} -.ladder th, .table th { - text-align: left; - font-size: 9pt; - background: #EEEEEE; - color: #111111; -} - -.hidden { - display: none; -} - /********************************************************* * Room list *********************************************************/