Preact minor update batch 10
Some checks are pending
Node.js CI / build (22.x) (push) Waiting to run

Minor
- Fix crashes/bugs in backported elim tournament redesign
- Support opting into Preact client via cookie
- Fix popup positioning when parent element is unmounted (i.e. when you
  switch layouts in the Options menu)

Trivial
- Focus Main Menu and not Rooms when loading home page in single panel
  mode
- Correctly set user.registered
  ( Fixes https://github.com/smogon/pokemon-showdown/pull/11031 )
- Fix some text in the Options menu
This commit is contained in:
Guangcong Luo 2025-04-17 05:01:04 +00:00
parent ac13bb2861
commit b0d565a30b
8 changed files with 103 additions and 65 deletions

View File

@ -108,6 +108,8 @@ RewriteCond %{DOCUMENT_ROOT}/$1 !-d
RewriteRule ^([A-Za-z0-9][A-Za-z0-9-]*)/$ /$1 [R=301,L]
# Anything that looks like a roomid: connect to the server
RewriteCond %{HTTP_COOKIE} ^.*preactalpha=1.*$
RewriteRule ^(|[A-Za-z0-9][A-Za-z0-9-]*)$ ./preactalpha.html [L,E=INDEX_PAGE:1]
RewriteRule ^$ - [E=INDEX_PAGE:1]
RewriteRule ^(preactalpha|login|users|(dm|challenge|user|viewuser|ladder)-[a-z0-9-]*)$ ./preactalpha.html [L,E=INDEX_PAGE:1]
RewriteRule ^([A-Za-z0-9][A-Za-z0-9-]*)$ ./ [L,E=INDEX_PAGE:1]

View File

@ -603,7 +603,8 @@
TournamentBox.prototype.cloneTree = function (node) {
var clonedNode = Object.assign({}, node);
if (node.children) {
clonedNode.children = node.children.map(function (child) { return this.cloneTree(child); });
var self = this;
clonedNode.children = node.children.map(function (child) { return self.cloneTree(child); });
}
return clonedNode;
};
@ -631,7 +632,6 @@
}
var name = app.user.get('name');
var nodeSize = this.nodeSize;
var newTree = this.cloneTree(data.rootNode);
if (newTree.team) newTree.highlightLink = true;
@ -685,7 +685,7 @@
var maxBreadth = numLeaves - (depthsWithLeaves - 1) / breadthCompression;
var maxDepth = hasLeafAtDepth.length;
var nodeSize = Object.assign({}, this.nodeSize);
var nodeSize = Object.assign({}, TournamentBox.nodeSize);
nodeSize.realWidth = nodeSize.width;
nodeSize.realHeight = nodeSize.height;
nodeSize.smallRealHeight = nodeSize.height / 2;
@ -747,7 +747,7 @@
if (ev.cmdKey || ev.metaKey || ev.ctrlKey) return;
ev.preventDefault();
ev.stopPropagation();
var roomid = $(ev.currentTarget).getAttribute('href');
var roomid = ev.currentTarget.getAttribute('href');
app.tryJoinRoom(roomid);
});
}
@ -759,7 +759,7 @@
var rect = elem.append('svg:rect').classed('tournament-bracket-tree-draw', true)
.attr('rx', nodeSize.radius)
.attr('x', -nodeSize.realWidth / 2).attr('width', nodeSize.realWidth);
rect.attr('y', -nodeSize.smallRealHeight / 2).attr('height', node.smallRealHeight);
rect.attr('y', -nodeSize.smallRealHeight / 2).attr('height', nodeSize.smallRealHeight);
if (node.team === name) rect.attr('stroke-dasharray', '5,5').attr('stroke-width', 2);
elem.append('svg:text').classed('tournament-bracket-tree-node-team', true)

View File

@ -47,7 +47,7 @@ export class BattleTextParser {
switch (cmd) {
case 'chatmsg': case 'chatmsg-raw': case 'raw': case 'error': case 'html':
case 'inactive': case 'inactiveoff': case 'warning':
case 'fieldhtml': case 'controlshtml': case 'bigerror':
case 'fieldhtml': case 'controlshtml': case 'pagehtml': case 'bigerror':
case 'debug': case 'tier': case 'challstr': case 'popup': case '':
return [cmd, line.slice(index + 1)];
case 'c': case 'chat': case 'uhtml': case 'uhtmlchange': case 'queryresponse': case 'showteam':

View File

@ -376,7 +376,7 @@ class PSUser extends PSStreamModel<PSLoginState | null> {
group = '';
userid = "" as ID;
named = false;
registered = false;
registered: { name: string, userid: ID } | null = null;
avatar = "1";
challstr = '';
loggingIn: string | null = null;
@ -464,7 +464,8 @@ class PSUser extends PSStreamModel<PSLoginState | null> {
this.loggingIn = null;
if (data?.curuser?.loggedin) {
// success!
this.registered = true;
const username = data.curuser.loggedin.username;
this.registered = { name: username, userid: toID(username) };
this.handleAssertion(name, data.assertion);
} else {
// wrong password
@ -530,7 +531,7 @@ class PSUser extends PSStreamModel<PSLoginState | null> {
this.group = '';
this.userid = "" as ID;
this.named = false;
this.registered = false;
this.registered = null;
this.update(null);
}
@ -1144,7 +1145,8 @@ export const PS = new class extends PSModel {
this.addRoom({
id: 'rooms' as RoomID,
title: "Rooms",
});
}, true);
this.rightPanel = this.rooms['rooms']!;
if (this.newsHTML) {
this.addRoom({

View File

@ -96,10 +96,15 @@ export class MainMenuRoom extends PSRoom {
PSLoginServer.query(
'upkeep', { challstr }
).then(res => {
if (!res?.loggedin) {
if (!res?.username) {
PS.user.initializing = false;
return;
}
// | , ; are not valid characters in names
res.username = res.username.replace(/[|,;]+/g, '');
if (res.loggedin) {
PS.user.registered = { name: res.username, userid: toID(res.username) };
}
PS.user.handleAssertion(res.username, res.assertion);
});
return;
@ -377,8 +382,37 @@ class NewsPanel extends PSRoomPanel {
static readonly routes = ['news'];
static readonly title = 'News';
static readonly location = 'mini-window';
change = (ev: Event) => {
const target = ev.currentTarget as HTMLInputElement;
if (target.value === '1') {
document.cookie = "preactalpha=1; expires=Thu, 1 May 2025 12:00:00 UTC; path=/";
} else {
document.cookie = "preactalpha=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
}
if (target.value === 'leave') {
document.location.href = `/`;
}
};
override render() {
const cookieSet = document.cookie.includes('preactalpha=1');
return <PSPanelWrapper room={this.props.room} scrollable>
<div class="construction"><div class="construction-inner">
This is the Preact client alpha test.
<form>
<label class="checkbox">
<input type="radio" name="preactalpha" value="1" onChange={this.change} checked={cookieSet} /> {}
Use Preact always
</label>
<label class="checkbox">
<input type="radio" name="preactalpha" value="0" onChange={this.change} checked={!cookieSet} /> {}
Use Preact with URL
</label>
<label class="checkbox">
<input type="radio" name="preactalpha" value="leave" onChange={this.change} /> {}
Back to the old client
</label>
</form>
</div></div>
<div class="mini-window-body" style="max-height:none" dangerouslySetInnerHTML={{ __html: PS.newsHTML }}></div>
</PSPanelWrapper>;
}

View File

@ -559,12 +559,12 @@ class OptionsPanel extends PSRoomPanel {
<p>
<img
class="trainersprite yours" width="40" height="40" style={{ verticalAlign: 'middle' }}
src={Dex.resolveAvatar(`${PS.user.avatar}`)}
src={Dex.resolveAvatar(`${PS.user.avatar}`)} data-href="avatars"
/> {}
<strong>{PS.user.name}</strong>
</p>
<p>
<button class="button" data-href="avatars"> Avatar..</button>
<button class="button" data-href="avatars"> Avatar...</button>
</p>
{this.state.showStatusInput ? (
@ -575,12 +575,12 @@ class OptionsPanel extends PSRoomPanel {
) : (
<p>
<button class="button" onClick={this.handleShowStatusInput} disabled={this.state.showStatusUpdated}>
{this.state.showStatusUpdated ? 'Status Updated' : 'Status..'}</button>
{this.state.showStatusUpdated ? 'Status Updated' : 'Status...'}</button>
</p>
)}
{PS.user.named && (PS.user.registered ?
<button className="button" data-href="changepassword">Change Password</button> :
<button className="button" data-href="changepassword">Password...</button> :
<button className="button" data-href="register">Register</button>)}
<hr />
@ -600,65 +600,41 @@ class OptionsPanel extends PSRoomPanel {
</select></label>
</p>
<p>
<label class="checkbox">
<input
type="checkbox" name="noanim"
checked={PS.prefs.noanim || false} onChange={this.handleOnChange}
/> Disable animations
</label>
</p>
<p>
<label class="checkbox">
<input
type="checkbox"
name="bwgfx"
onChange={this.handleOnChange}
checked={PS.prefs.bwgfx || false}
/> Use 2D sprites instead of 3D models</label>
<label class="checkbox"> <input
name="noanim" checked={PS.prefs.noanim || false} type="checkbox" onChange={this.handleOnChange}
/> Disable animations</label>
</p>
<p>
<label class="checkbox"><input
type="checkbox"
name="nopastgens"
onChange={this.handleOnChange}
checked={PS.prefs.nopastgens || false}
name="bwgfx" checked={PS.prefs.bwgfx || false} type="checkbox" onChange={this.handleOnChange}
/> Use 2D sprites instead of 3D models</label>
</p>
<p>
<label class="checkbox"><input
name="nopastgens" checked={PS.prefs.nopastgens || false} type="checkbox" onChange={this.handleOnChange}
/> Use modern sprites for past generations</label>
</p>
<hr />
<h3>Chat</h3>
<p>
<label class="checkbox">
<input type="checkbox" onChange={this.handleOnChange} name="blockPMs" checked={PS.prefs.blockPMs || false}>
</input> Block PMs
</label>
<label class="checkbox"><input
name="blockPMs" checked={PS.prefs.blockPMs || false} type="checkbox" onChange={this.handleOnChange}
/> Block PMs</label>
</p>
<p>
<label class="checkbox">
<input
type="checkbox"
name="blockChallenges"
onChange={this.handleOnChange}
checked={PS.prefs.blockChallenges || false}
>
</input> Block Challenges</label>
<label class="checkbox"><input
name="blockChallenges" checked={PS.prefs.blockChallenges || false} type="checkbox" onChange={this.handleOnChange}
/> Block challenges</label>
</p>
<p>
<label class="checkbox">
<input
type="checkbox"
name="inchatpm"
onChange={this.handleOnChange}
checked={PS.prefs.inchatpm || false}
/> Show PMs in chatrooms</label>
<label class="checkbox"><input
name="inchatpm" checked={PS.prefs.inchatpm || false} type="checkbox" onChange={this.handleOnChange}
/> Show PMs in chatrooms</label>
</p>
<p>
<label class="checkbox">
<input
type="checkbox"
name="noselfhighlight"
onChange={this.handleOnChange}
checked={PS.prefs.noselfhighlight || false}
/> Do Not Highlight when your name is said in chat</label>
<label class="checkbox"><input
name="noselfhighlight" checked={PS.prefs.noselfhighlight || false} type="checkbox" onChange={this.handleOnChange}
/> Do not highlight when your name is said in chat</label>
</p>
<p>
<label class="optlabel">Timestamps: <select name="layout" class="button" onChange={this.setChatroomTimestamp}>
@ -668,7 +644,7 @@ class OptionsPanel extends PSRoomPanel {
</select></label>
</p>
<p>
<label class="optlabel">Timestamps in PMs: <select name="layout" class="button" onChange={this.setPMsTimestamp}>
<label class="optlabel">Timestamps in DMs: <select name="layout" class="button" onChange={this.setPMsTimestamp}>
<option value="" selected={!PS.prefs.timestamps.pms}>Off</option>
<option value="minutes" selected={PS.prefs.timestamps.pms === "minutes"}>[HH:MM]</option>
<option value="seconds" selected={PS.prefs.timestamps.pms === "seconds"}>[HH:MM:SS]</option>
@ -869,7 +845,7 @@ class AvatarsPanel extends PSRoomPanel {
avatars.push(i);
}
return <PSPanelWrapper room={room} width={480}><div class="pad">
return <PSPanelWrapper room={room} width={1210}><div class="pad">
<label class="optlabel"><strong>Choose an avatar or </strong>
<button class="button" onClick={() => this.close()}> Cancel</button>
</label>
@ -1129,8 +1105,8 @@ class RegisterPanel extends PSRoomPanel {
console.log(data);
if (data?.actionerror) this.setState({ errorMsg: data?.actionerror });
if (data?.curuser?.loggedin) {
PS.user.registered = true;
let name = data.curuser.username;
PS.user.registered = { name, userid: toID(name) };
if (data?.assertion) PS.user.handleAssertion(name, data?.assertion);
this.close();
PS.alert("You have been successfully registered.");

View File

@ -588,7 +588,15 @@ export class PSMain extends preact.Component {
if (fullSize) {
return { width: '90%', maxHeight: '90%', maxWidth: 'none', position: 'relative', margin: '5vh auto 0' };
}
if (room.location === 'modal-popup' || !room.parentElem) {
const source = room.parentElem?.getBoundingClientRect();
if (source && !source.width && !source.height && !source.top && !source.left) {
// parent elem has been unmounted
room.parentElem = null;
PS.update();
}
if (room.location === 'modal-popup' || !room.parentElem || !source) {
return { maxWidth: width || 480 };
}
if (!room.width || !room.height) {
@ -616,7 +624,6 @@ export class PSMain extends preact.Component {
const availableWidth = document.documentElement.clientWidth + offsetLeft;
const availableHeight = document.documentElement.clientHeight;
const source = room.parentElem.getBoundingClientRect();
const sourceWidth = source.width;
const sourceHeight = source.height;
const sourceTop = source.top + offsetTop;

View File

@ -55,6 +55,23 @@ pre {
word-wrap: break-word;
}
.construction {
background: repeating-linear-gradient(
-45deg,
#d9ca28,
#d9ca28 10px,
#292824 10px,
#292824 20px
);
padding: 15px;
}
.construction-inner {
background: #d9ca28;
padding: 5px 10px;
font-weight: bold;
color: black;
}
/*********************************************************
* Header
*********************************************************/