From fe3d7cd4e0fc3977f24d49e7366e971b8b140b87 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Mon, 25 May 2026 12:33:42 +0000 Subject: [PATCH] Preact minor updates batch 30 - Fix starting tournaments with custom name - Fix error when joining private rooms - Improve colors for command syntax highlighting - Improve performance by not updating background rooms - Rename a bunch of room visibility functions for clarity - Default language to English - Add timestamps to disconnect/reconnect messages - Fix server teams not getting fully downloaded --- play.pokemonshowdown.com/src/battle-log.ts | 17 +++---- play.pokemonshowdown.com/src/client-main.ts | 51 ++++++++++--------- .../src/panel-chat-tournament.tsx | 4 +- play.pokemonshowdown.com/src/panel-chat.tsx | 20 +++++--- .../src/panel-mainmenu.tsx | 1 + play.pokemonshowdown.com/src/panel-popups.tsx | 6 +-- play.pokemonshowdown.com/src/panel-rooms.tsx | 2 +- play.pokemonshowdown.com/src/panel-topbar.tsx | 2 +- play.pokemonshowdown.com/src/panels.tsx | 9 +++- play.pokemonshowdown.com/style/battle-log.css | 6 +++ play.pokemonshowdown.com/style/battle.css | 2 +- 11 files changed, 74 insertions(+), 46 deletions(-) diff --git a/play.pokemonshowdown.com/src/battle-log.ts b/play.pokemonshowdown.com/src/battle-log.ts index cc4de6e61..adc8664c3 100644 --- a/play.pokemonshowdown.com/src/battle-log.ts +++ b/play.pokemonshowdown.com/src/battle-log.ts @@ -122,6 +122,13 @@ export class BattleLog { }); this.addNode(el); } + static renderTimestamp(timestamp: number | null, showTimestamps?: 'minutes' | 'seconds' | null) { + if (!showTimestamps) return ''; + const date = timestamp && !isNaN(timestamp) ? new Date(timestamp * 1000) : new Date(); + const components = [date.getHours(), date.getMinutes()]; + if (showTimestamps === 'seconds') components.push(date.getSeconds()); + return `[${components.map(x => x < 10 ? `0${x}` : x).join(':')}] `; + } add(args: Args, kwArgs?: KWArgs, preempt?: boolean, showTimestamps?: 'minutes' | 'seconds') { if (kwArgs?.silent) return; const battle = this.scene?.battle; @@ -169,15 +176,7 @@ export class BattleLog { } const ignoreList = window.app?.ignore || window.PS?.prefs?.ignore; if (ignoreList?.[toUserid(name)] && ' +^\u2605\u2606'.includes(rank)) return; - let timestampHtml = ''; - if (showTimestamps) { - const date = timestamp && !isNaN(timestamp) ? new Date(timestamp * 1000) : new Date(); - const components = [date.getHours(), date.getMinutes()]; - if (showTimestamps === 'seconds') { - components.push(date.getSeconds()); - } - timestampHtml = `[${components.map(x => x < 10 ? `0${x}` : x).join(':')}] `; - } + const timestampHtml = BattleLog.renderTimestamp(timestamp, showTimestamps); const isHighlighted = window.app?.rooms?.[battle!.roomid].getHighlight(message) || this.getHighlight?.(args); [divClass, divHTML, noNotify] = this.parseChatMessage(message, name, timestampHtml, isHighlighted); if (!noNotify && isHighlighted) { diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index 4032d626a..e1a83f6f0 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -531,7 +531,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> { } localTeam.uploaded = { teamid: team.teamid, - notLoaded: false, + notLoaded: this.unloadedPackedTeam(team.team) === localTeam.packedTeam, private: team.private, }; delete teams[localTeam.teamid]; @@ -554,22 +554,22 @@ class PSTeams extends PSStreamModel<'team' | 'format'> { localTeam.teamid = team.teamid; localTeam.uploaded = { teamid: team.teamid, - notLoaded: false, + notLoaded: this.unloadedPackedTeam(team.team) === localTeam.packedTeam, private: team.private, }; break; } } if (!matched) { - const mons = team.team.split(',').map((m: string) => ({ species: m, moves: [] })); const newTeam: Team = { name: team.name, format: team.format, folder: '', - packedTeam: Teams.pack(mons), + packedTeam: this.unloadedPackedTeam(team.team), iconCache: null, isBox: false, key: this.getKey(team.name), + teamid: team.teamid, uploaded: { teamid: team.teamid, notLoaded: true, @@ -581,6 +581,10 @@ class PSTeams extends PSStreamModel<'team' | 'format'> { } }); } + unloadedPackedTeam(uploadedTeam: string) { + const mons = uploadedTeam.split(',').map((m: string) => ({ species: m, moves: [] })); + return Teams.pack(mons); + } loadTeam(team: Team | undefined | null, ifNeeded: true): void | Promise; loadTeam(team: Team | undefined | null): Promise; loadTeam(team: Team | undefined | null, ifNeeded?: boolean): void | Promise { @@ -1059,7 +1063,7 @@ export class PSRoom extends PSStreamModel implements RoomOptions { } notify(options: { title: string, body?: string, noAutoDismiss?: boolean, id?: string }) { let desktopNotification: Notification | null = null; - const roomIsFocused = document.hasFocus?.() && PS.isVisible(this); + const roomIsFocused = document.hasFocus?.() && PS.isVisiblePanel(this); if (roomIsFocused && !options.noAutoDismiss) return; if (!roomIsFocused) { PS.playNotificationSound(); @@ -1092,7 +1096,7 @@ export class PSRoom extends PSStreamModel implements RoomOptions { PS.update(); } subtleNotify() { - if (PS.isVisible(this)) return; + if (PS.isVisiblePanel(this)) return; const room = PS.rooms[this.id] as ChatRoom; const lastSeenTimestamp = PS.prefs.logtimes?.[PS.server.id]?.[this.id] || 0; const lastMessageTime = +(room.lastMessage?.[1] || 0); @@ -1245,7 +1249,7 @@ export class PSRoom extends PSStreamModel implements RoomOptions { const roomid = /[^a-z0-9-]/.test(target) ? toID(target) as any as RoomID : target as RoomID; const targetRoom = roomid ? PS.rooms[roomid] : this; if (!targetRoom) return this.errorReply(`Room '${roomid}' not found.`); - if (PS.isNormalRoom(targetRoom)) { + if (PS.isPanel(targetRoom)) { this.errorReply(`'${roomid}' is already maximized.`); } else if (!PS.isPopup(targetRoom)) { PS.moveRoom(targetRoom, 'left', false, 0); @@ -2180,7 +2184,7 @@ export const PS = new class extends PSModel { if (!PS.user.initializing) { room.receiveLine(['error', args[2]]); } - } else if (args[1] === 'nonexistent') { + } else if (args[1] === 'nonexistent' || args[1] === 'joinfailed') { // sometimes we assume a room is a chatroom when it's not // when that happens, just ignore this error if (room.type === 'chat' || room.type === 'battle') room.receiveLine(args); @@ -2222,7 +2226,17 @@ export const PS = new class extends PSModel { } this.connection.send(`${roomid || ''}|${msg}`); } - isVisible(room: PSRoom) { + isVisible(room: PSRoom): boolean { + if (PS.isPanel(room)) { + return !this.leftPanelWidth ? room === this.panel : room === this.leftPanel || room === this.rightPanel; + } + if (room.location === 'mini-window') { + return !this.leftPanelWidth ? this.mainmenu === this.panel : this.mainmenu === this.leftPanel; + } + // some kind of popup + return true; + } + isVisiblePanel(room: PSRoom) { if (!this.leftPanelWidth) { // one panel visible return room === this.panel || room === this.room; @@ -2357,10 +2371,10 @@ export const PS = new class extends PSModel { return true; } this.closePopupsAbove(room, true); - if (!this.isVisible(room)) { + if (!this.isVisiblePanel(room)) { room.focusNextUpdate = true; } - if (PS.isNormalRoom(room)) { + if (PS.isPanel(room)) { if (room.location === 'right') { this.rightPanel = room; } else { @@ -2583,16 +2597,6 @@ export const PS = new class extends PSModel { PS.focusRoom(PS.leftPanel.id); } } - roomVisible(room: PSRoom): boolean { - if (PS.isNormalRoom(room)) { - return !this.leftPanelWidth ? room === this.panel : room === this.leftPanel || room === this.rightPanel; - } - if (room.location === 'mini-window') { - return !this.leftPanelWidth ? this.mainmenu === this.panel : this.mainmenu === this.leftPanel; - } - // some kind of popup - return true; - } renameRoom(room: PSRoom, id: RoomID) { // should never happen if (this.rooms[id]) this.removeRoom(this.rooms[id]); @@ -2617,7 +2621,8 @@ export const PS = new class extends PSModel { if (!room) return false; return room.location === 'popup' || room.location === 'modal-popup'; } - isNormalRoom(room: PSRoom | undefined | null) { + /** this isn't just !isPopup. you forgot about mini windows again. */ + isPanel(room: PSRoom | undefined | null) { if (!room) return false; return room.location === 'left' || room.location === 'right'; } @@ -2641,7 +2646,7 @@ export const PS = new class extends PSModel { return; } - background ??= !this.roomVisible(room); + background ??= !this.isVisible(room); if (room.location === 'mini-window') { const miniRoomIndex = this.miniRoomList.indexOf(room.id); diff --git a/play.pokemonshowdown.com/src/panel-chat-tournament.tsx b/play.pokemonshowdown.com/src/panel-chat-tournament.tsx index c18f075ee..efb2212e4 100644 --- a/play.pokemonshowdown.com/src/panel-chat-tournament.tsx +++ b/play.pokemonshowdown.com/src/panel-chat-tournament.tsx @@ -53,6 +53,7 @@ interface TournamentTableBracketData { scores: number[]; } type TournamentInfo = { + /** nickname will override this */ format?: string, teambuilderFormat?: string, generator?: string, @@ -148,7 +149,8 @@ export class ChatTournament extends PSModel { } else if (args[0] === 'tournament') { switch (cmd) { case 'create': { - this.info.format = args[2]; + this.info.format = args[5] || args[2]; + this.info.teambuilderFormat = args[2]; this.info.generator = args[3]; const formatName = BattleLog.formatName(args[2]); const type = args[3]; diff --git a/play.pokemonshowdown.com/src/panel-chat.tsx b/play.pokemonshowdown.com/src/panel-chat.tsx index 0d4f6ba96..1e3b26d57 100644 --- a/play.pokemonshowdown.com/src/panel-chat.tsx +++ b/play.pokemonshowdown.com/src/panel-chat.tsx @@ -107,12 +107,18 @@ export class ChatRoom extends PSRoom { return; case 'noinit': - if (this.battle) { + if (this.battle && args[1] === 'joinfailed') { + this.receiveLine(['bigerror', args[2]]); + this.receiveLine(['html', + `

`, + ]); + } else if (this.battle) { // check the Replays database (this as any as BattleRoom).loadReplay(); } else { + const message = args[2] ? BattleLog.escapeHTML(args[2]) : `Chatroom "${BattleLog.escapeHTML(this.title)}" not found`; this.receiveLine(['html', - `

Chatroom "${BattleLog.escapeHTML(this.title)}" not found

`, + `

${message}

`, ]); } return; @@ -199,12 +205,13 @@ export class ChatRoom extends PSRoom { if (time < cutOffTime) cutOffStart = i; } if (lines[i].startsWith('|raw|
You joined ')) { - reconnectMessage = `|raw|
You reconnected to ${lines[i].slice(38)}`; + const timestamp = BattleLog.renderTimestamp(Date.now() / 1000, PS.prefs.timestamps?.chatrooms); + reconnectMessage = `|raw|
${timestamp}You reconnected to ${lines[i].slice(38)}`; cutOffEnd = i; if (!lines[i - 1]) cutOffEnd = i - 1; } } - console.log("Reconnection log splice:"); + console.log(`Reconnection log splice: (cutoff: ${cutOffTime})`); console.log([ ...lines.slice(0, cutOffStart), '====================', @@ -215,7 +222,8 @@ export class ChatRoom extends PSRoom { lines = lines.slice(cutOffStart, cutOffEnd); if (lines.length) { - this.receiveLine([`raw`, `
You disconnected.
`]); + const timestamp = BattleLog.renderTimestamp(cutOffTime, PS.prefs.timestamps?.chatrooms); + this.receiveLine([`raw`, `
${timestamp}You disconnected.
`]); for (const line of lines) this.receiveLine(BattleTextParser.parseLine(line)); this.receiveLine(BattleTextParser.parseLine(reconnectMessage)); } @@ -307,7 +315,7 @@ export class ChatRoom extends PSRoom { 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 const time = serverTime - (this.timeOffset || 0); - if (PS.isVisible(this)) { + if (PS.isVisiblePanel(this)) { this.lastMessageTime = null; lastMessageDates[PS.server.id][this.id] = time; PS.prefs.set('logtimes', lastMessageDates); diff --git a/play.pokemonshowdown.com/src/panel-mainmenu.tsx b/play.pokemonshowdown.com/src/panel-mainmenu.tsx index 5bbe8e6b2..624fab6d1 100644 --- a/play.pokemonshowdown.com/src/panel-mainmenu.tsx +++ b/play.pokemonshowdown.com/src/panel-mainmenu.tsx @@ -458,6 +458,7 @@ export class MainMenuRoom extends PSRoom { case 'teamupload': if (PS.teams.uploading) { const team = PS.teams.uploading; + team.teamid = response.teamid; team.uploaded = { teamid: response.teamid, notLoaded: false, diff --git a/play.pokemonshowdown.com/src/panel-popups.tsx b/play.pokemonshowdown.com/src/panel-popups.tsx index aa9b7f5ab..f0bdbbea3 100644 --- a/play.pokemonshowdown.com/src/panel-popups.tsx +++ b/play.pokemonshowdown.com/src/panel-popups.tsx @@ -666,7 +666,7 @@ class OptionsPanel extends PSRoomPanel { {this.state.showStatusInput ? (

- +

) : ( @@ -758,9 +758,9 @@ class OptionsPanel extends PSRoomPanel {