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
This commit is contained in:
Guangcong Luo 2025-05-30 05:42:29 +00:00
parent c20898479f
commit ae69319e63
11 changed files with 108 additions and 98 deletions

View File

@ -56,8 +56,8 @@ require v20 or later) and Git, and run `node build` (on Windows) or `./build`
(on other OSes) to build. (on other OSes) to build.
You can make and test client changes simply by building after each change, 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 and opening `play.pokemonshowdown.com/testclient.html`. This will allow you
client without setting up your own login server. to test changes to the client without setting up your own login server.
### Test keys ### Test keys
@ -78,7 +78,7 @@ grab it from:
Make sure to put it in `config/` and not `play.pokemonshowdown.com/config/`. 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 [5]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

View File

@ -137,7 +137,7 @@ if (!compileOpts.ignore) {
const diff = process.hrtime(compileStartTime); const diff = process.hrtime(compileStartTime);
console.log( 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`
); );
/********************************************************* /*********************************************************

View File

@ -183,16 +183,7 @@ export class BattleLog {
[divClass, divHTML, noNotify] = this.parseChatMessage(message, name, timestampHtml, isHighlighted); [divClass, divHTML, noNotify] = this.parseChatMessage(message, name, timestampHtml, isHighlighted);
if (!noNotify && isHighlighted) { if (!noNotify && isHighlighted) {
const notifyTitle = "Mentioned by " + name + " in " + (battle?.roomid || ''); const notifyTitle = "Mentioned by " + name + " in " + (battle?.roomid || '');
if (window.PS) { window.app?.rooms[battle?.roomid || '']?.notifyOnce(notifyTitle, "\"" + message + "\"", 'highlight');
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');
}
} }
break; break;
@ -1238,9 +1229,9 @@ export class BattleLog {
} }
const colorStyle = ` style="color:${BattleLog.usernameColor(toID(name))}"`; const colorStyle = ` style="color:${BattleLog.usernameColor(toID(name))}"`;
const clickableName = `<small class="groupsymbol">${BattleLog.escapeHTML(group)}</small><span class="username">${BattleLog.escapeHTML(name)}</span>`; const clickableName = `<small class="groupsymbol">${BattleLog.escapeHTML(group)}</small><span class="username">${BattleLog.escapeHTML(name)}</span>`;
let hlClass = isHighlighted ? ' highlighted' : ''; const isMine = (window.app?.user?.get('name') === name) || (window.PS?.user.name === name);
let isMine = (window.app?.user?.get('name') === name) || (window.PS?.user.name === name); const hlClass = isHighlighted ? ' highlighted' : '';
let mineClass = isMine ? ' mine' : ''; const mineClass = isMine ? ' mine' : '';
let cmd = ''; let cmd = '';
let target = ''; let target = '';

View File

@ -50,6 +50,12 @@ export class PSConnection {
tryConnectInWorker(): boolean { tryConnectInWorker(): boolean {
if (this.socket) return false; // must be one or the other 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 { try {
const worker = new Worker('/js/client-connection-worker.js'); const worker = new Worker('/js/client-connection-worker.js');
@ -63,7 +69,6 @@ export class PSConnection {
case 'connected': case 'connected':
console.log('\u2705 (CONNECTED via worker)'); console.log('\u2705 (CONNECTED via worker)');
this.connected = true; this.connected = true;
PS.connected = true;
this.queue.forEach(msg => worker.postMessage({ type: 'send', data: msg })); this.queue.forEach(msg => worker.postMessage({ type: 'send', data: msg }));
this.queue = []; this.queue = [];
PS.update(); PS.update();
@ -84,8 +89,8 @@ export class PSConnection {
} }
}; };
worker.onerror = (e: ErrorEvent) => { worker.onerror = (ev: ErrorEvent) => {
console.warn('Worker connection error:', e); console.warn('Worker connection error:', ev);
this.worker = null; this.worker = null;
this.directConnect(); // fallback this.directConnect(); // fallback
}; };
@ -116,7 +121,6 @@ export class PSConnection {
socket.onopen = () => { socket.onopen = () => {
console.log('\u2705 (CONNECTED)'); console.log('\u2705 (CONNECTED)');
this.connected = true; this.connected = true;
PS.connected = true;
this.reconnectDelay = 1000; this.reconnectDelay = 1000;
this.queue.forEach(msg => socket.send(msg)); this.queue.forEach(msg => socket.send(msg));
this.queue = []; this.queue = [];
@ -132,7 +136,6 @@ export class PSConnection {
this.handleDisconnect(); this.handleDisconnect();
console.log('\u2705 (DISCONNECTED)'); console.log('\u2705 (DISCONNECTED)');
this.connected = false; this.connected = false;
PS.connected = false;
PS.isOffline = true; PS.isOffline = true;
for (const roomid in PS.rooms) { for (const roomid in PS.rooms) {
const room = PS.rooms[roomid]!; const room = PS.rooms[roomid]!;
@ -143,10 +146,8 @@ export class PSConnection {
}; };
socket.onerror = (ev: Event) => { socket.onerror = (ev: Event) => {
PS.connected = false;
PS.isOffline = true; PS.isOffline = true;
// no useful info to print from the event // no useful info to print from the event
PS.alert(`Connection error`);
this.retryConnection(); this.retryConnection();
PS.update(); PS.update();
}; };
@ -154,7 +155,6 @@ export class PSConnection {
private handleDisconnect() { private handleDisconnect() {
this.connected = false; this.connected = false;
PS.connected = false;
PS.isOffline = true; PS.isOffline = true;
this.socket = null; this.socket = null;
for (const roomid in PS.rooms) { for (const roomid in PS.rooms) {
@ -168,12 +168,14 @@ export class PSConnection {
private retryConnection() { private retryConnection() {
if (!this.canReconnect()) return; if (!this.canReconnect()) return;
if (this.reconnectTimer) return; if (this.reconnectTimer) return;
this.reconnectTimer = setTimeout(() => { this.reconnectTimer = setTimeout(() => {
this.reconnectTimer = null; this.reconnectTimer = null;
if (!this.connected && this.canReconnect()) { if (!this.connected && this.canReconnect()) {
PS.mainmenu.send('/reconnect'); PS.mainmenu.send('/reconnect');
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.reconnectCap); this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.reconnectCap);
} }
PS.update();
}, this.reconnectDelay); }, this.reconnectDelay);
} }
@ -182,17 +184,13 @@ export class PSConnection {
this.socket?.close(); this.socket?.close();
this.worker?.terminate(); this.worker?.terminate();
this.worker = null; this.worker = null;
PS.connection = null; this.handleDisconnect();
PS.connected = false; PS.update();
PS.isOffline = true;
} }
reconnect() {
reconnectTest() { if (this.connected) return;
this.socket?.close(); if (this.worker && this.tryConnectInWorker()) return;
this.worker?.postMessage({ type: 'disconnect' }); this.directConnect();
this.worker = null;
PS.connected = false;
PS.isOffline = true;
} }
send(msg: string) { send(msg: string) {
@ -213,7 +211,7 @@ export class PSConnection {
if (!PS.connection) { if (!PS.connection) {
PS.connection = new PSConnection(); PS.connection = new PSConnection();
} else { } else {
PS.connection.directConnect(); PS.connection.reconnect();
} }
PS.prefs.doAutojoin(); PS.prefs.doAutojoin();
} }

View File

@ -1042,13 +1042,16 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
this.isSubtleNotifying = true; this.isSubtleNotifying = true;
PS.update(); PS.update();
} }
dismissNotificationAt(i: number) {
try {
this.notifications[i].notification?.close();
} catch {}
this.notifications.splice(i, 1);
}
dismissNotification(id: string) { dismissNotification(id: string) {
const index = this.notifications.findIndex(n => n.id === id); const index = this.notifications.findIndex(n => n.id === id);
if (index !== -1) { if (index !== -1) {
try { this.dismissNotificationAt(index);
this.notifications[index].notification?.close();
} catch {}
this.notifications.splice(index, 1);
} }
PS.update(); PS.update();
} }
@ -1061,7 +1064,11 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
lastMessageDates[PS.server.id][room.id] = room.lastMessageTime || 0; lastMessageDates[PS.server.id][room.id] = room.lastMessageTime || 0;
PS.prefs.set('logtimes', lastMessageDates); 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; this.isSubtleNotifying = false;
} }
connect(): void { connect(): void {
@ -1184,10 +1191,22 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
'logout'() { 'logout'() {
PS.user.logOut(); PS.user.logOut();
}, },
'reconnect'() { 'reconnect,connect'() {
if (!PS.isOffline) { if (this.connected && this.connected !== 'autoreconnect') {
return this.add(`|error|You are already connected.`); 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; const uptime = Date.now() - PS.startTime;
if (uptime > 24 * 60 * 60 * 1000) { if (uptime > 24 * 60 * 60 * 1000) {
PS.confirm(`It's been over a day since you first connected. Please refresh.`, { PS.confirm(`It's been over a day since you first connected. Please refresh.`, {
@ -1207,17 +1226,6 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
return this.add(`|error|You are already offline.`); return this.add(`|error|You are already offline.`);
} }
PS.connection?.disconnect(); 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'() { 'cancelsearch'() {
if (PS.mainmenu.cancelSearch()) { if (PS.mainmenu.cancelSearch()) {
@ -1730,7 +1738,6 @@ export const PS = new class extends PSModel {
user = new PSUser(); user = new PSUser();
server = new PSServer(); server = new PSServer();
connection: PSConnection | null = null; connection: PSConnection | null = null;
connected = false;
/** /**
* While PS is technically disconnected while it's trying to connect, * While PS is technically disconnected while it's trying to connect,
* it still shows UI like it's connected, so you can click buttons * it still shows UI like it's connected, so you can click buttons

View File

@ -250,31 +250,42 @@ export class ChatRoom extends PSRoom {
handleHighlight = (args: Args) => { handleHighlight = (args: Args) => {
let name; let name;
let message; let message;
let msgTime = 0; let serverTime = 0;
if (args[0] === 'c:') { if (args[0] === 'c:') {
msgTime = parseInt(args[1]); serverTime = parseInt(args[1]);
name = args[2]; name = args[2];
message = args[3]; message = args[3];
} else { } else {
name = args[1]; name = args[1];
message = args[2]; 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] = {}; 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 // 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); const time = serverTime - (this.timeOffset || 0);
let mayNotify = serverMsgTime > lastMessageDate && name !== PS.user.userid;
if (PS.isVisible(this)) { if (PS.isVisible(this)) {
this.lastMessageTime = null; this.lastMessageTime = null;
lastMessageDates[PS.server.id][this.id] = serverMsgTime; lastMessageDates[PS.server.id][this.id] = time;
PS.prefs.set('logtimes', lastMessageDates); PS.prefs.set('logtimes', lastMessageDates);
} else { } else {
// To be saved on focus // To be saved on focus
let lastMessageTime = this.lastMessageTime || 0; const lastMessageTime = this.lastMessageTime || 0;
if (lastMessageTime < serverMsgTime) this.lastMessageTime = serverMsgTime; 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({ override clientCommands = this.parseClientCommands({
'chall,challenge'(target) { 'chall,challenge'(target) {

View File

@ -437,7 +437,7 @@ class NewsPanel extends PSRoomPanel {
change = (ev: Event) => { change = (ev: Event) => {
const target = ev.currentTarget as HTMLInputElement; const target = ev.currentTarget as HTMLInputElement;
if (target.value === '1') { 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 { } else {
document.cookie = "preactalpha=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; document.cookie = "preactalpha=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
} }

View File

@ -217,7 +217,7 @@ export class PSHeader extends preact.Component {
this.handleResize(); this.handleResize();
} }
renderUser() { renderUser() {
if (!PS.connected) { if (!PS.connection?.connected) {
return <button class="button" disabled><em>Offline</em></button>; return <button class="button" disabled><em>Offline</em></button>;
} }
if (PS.user.initializing) { if (PS.user.initializing) {

View File

@ -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 * Everything else
*********************************************************/ *********************************************************/

View File

@ -5,7 +5,7 @@ License: GPLv2
*/ */
@import url(./battle-log.css?v9.8); @import url(./battle-log.css?v10);
.battle { .battle {
position: absolute; position: absolute;

View File

@ -566,6 +566,9 @@ summary:hover .expandbutton,
border-left: 1px solid #AAAAAA; border-left: 1px solid #AAAAAA;
margin-left: -1px; margin-left: -1px;
} }
.dark .ps-room {
border-color: #34373b;
}
.scrollable { .scrollable {
overflow: auto; overflow: auto;
-webkit-overflow-scrolling: touch; -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: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%)); } .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 * Room list
*********************************************************/ *********************************************************/