From 5971e5151a5e9424fbe3890784c3703a092d01d7 Mon Sep 17 00:00:00 2001 From: Guangcong Luo Date: Thu, 17 Apr 2025 11:09:12 +0000 Subject: [PATCH] Preact minor update batch 11 Minor - Unhide right panel when choosing "Two panels" layout option - Refactor focusing - Correctly focus next room when closing currently active room - Correctly focus room when joining new room - Use strict mode on all compiled files - Fix router when started on `/` (it previously required starting on a non-empty room ID, which wasn't noticeable back when the URL needed to be `/preactalpha`) - Update teambuilder sidebar CSS, to make it easier to add regular text - This is mainly for the "Tournaments" button in the main menu, which shares the CSS - Fix new tournament elim tree text in Safari - Update new tournament elim tree highlighted links to reliably link every still-playing game - Remove latest gen from format name displays everywhere - Previously, they would only be removed from the format dropdown, but now they're also gone from the Ladder tab, battle tabs, and `/rank` - Support async d3 loading - This allows chatrooms to be loaded way before all our dependencies are fully downloaded - Remove "[Gen 9]" from format names everywhere (previously it was only removed from the format dropdown) - Also add "[Gen 6]" to unlabeled formats in `/rank` (Gen 6 was the last time we didn't have format generation as part of format names) Trivial - Stricter JSX linting - (unfortunately, most of the JSX style enforcement I actually want isn't possible in @stylistic) - Make room.subscribeTo's second parameter optional - Rearrange and comment loading order - Rename hiddenInit -> focusNextUpdate (clarity) - Rename PSMain -> PSView (clarity) - Fix button spacing in Change Password - Add `touch-action: manipulation` to tags - Refactor `nodeSize` in elim tour trees --- .babelrc | 4 +- .editorconfig | 3 + eslint-ps-standard.mjs | 1 + package-lock.json | 16 ++ package.json | 1 + .../js/client-chat-tournament.js | 15 +- play.pokemonshowdown.com/js/client-endload.js | 4 + .../js/client-mainmenu.js | 5 - play.pokemonshowdown.com/js/client-topbar.js | 2 +- .../preactalpha.template.html | 15 +- play.pokemonshowdown.com/src/battle-log.ts | 34 ++- .../src/client-endload.ts | 3 + play.pokemonshowdown.com/src/client-main.ts | 76 +++--- .../src/panel-chat-tournament.tsx | 72 +++--- play.pokemonshowdown.com/src/panel-chat.tsx | 21 +- play.pokemonshowdown.com/src/panel-ladder.tsx | 4 +- play.pokemonshowdown.com/src/panel-popups.tsx | 244 +++++++----------- .../src/panel-teambuilder-team.tsx | 2 +- play.pokemonshowdown.com/src/panel-topbar.tsx | 12 +- play.pokemonshowdown.com/src/panels.tsx | 85 ++++-- play.pokemonshowdown.com/style/battle-log.css | 2 +- play.pokemonshowdown.com/style/client.css | 11 +- play.pokemonshowdown.com/style/client2.css | 104 +------- .../style/teambuilder.css | 106 ++++++++ play.pokemonshowdown.com/testclient-beta.html | 12 +- .../src/replays-battle.tsx | 2 +- 26 files changed, 453 insertions(+), 403 deletions(-) create mode 100644 play.pokemonshowdown.com/js/client-endload.js create mode 100644 play.pokemonshowdown.com/src/client-endload.ts diff --git a/.babelrc b/.babelrc index 7e241e236..bc045ca84 100644 --- a/.babelrc +++ b/.babelrc @@ -29,7 +29,9 @@ // ES3 "@babel/plugin-transform-member-expression-literals", - "@babel/plugin-transform-property-literals" + "@babel/plugin-transform-property-literals", + + "@babel/plugin-transform-strict-mode" ], "ignore": [ "src/globals.d.ts" diff --git a/.editorconfig b/.editorconfig index b42aef9cc..de0730e27 100644 --- a/.editorconfig +++ b/.editorconfig @@ -25,3 +25,6 @@ trim_trailing_whitespace = false indent_style = tab indent_size = 8 trim_trailing_whitespace = false + +[COMMIT_EDITMSG] +indent_style = space diff --git a/eslint-ps-standard.mjs b/eslint-ps-standard.mjs index 038b29472..6041a6974 100644 --- a/eslint-ps-standard.mjs +++ b/eslint-ps-standard.mjs @@ -174,6 +174,7 @@ export const defaultRules = { "@stylistic/jsx-one-expression-per-line": "off", "@stylistic/jsx-max-props-per-line": "off", "@stylistic/jsx-function-call-newline": "off", + "@stylistic/jsx-child-element-spacing": "error", "no-restricted-syntax": ["error", { selector: "CallExpression[callee.name='Symbol']", message: "Annoying to serialize, just use a string" }, ], diff --git a/package-lock.json b/package-lock.json index 84bf6d032..9e1c2316b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-strict-mode": "^7.25.9", "@babel/preset-env": "^7.26.9", "@babel/preset-typescript": "^7.27.0", "babel-plugin-remove-import-export": "^1.1.1", @@ -1264,6 +1265,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-strict-mode": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-strict-mode/-/plugin-transform-strict-mode-7.25.9.tgz", + "integrity": "sha512-DplEwkN9xt6XCz/4oC9l8FJGn7LnOGPU7v08plq+OclMT55zAR9lkX7QIbQ9XscvvJNYpLUfYO4IYz/7JGkbXQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-template-literals": { "version": "7.26.8", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", diff --git a/package.json b/package.json index ac1e49134..6f0423407 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx": "^7.25.9", + "@babel/plugin-transform-strict-mode": "^7.25.9", "@babel/preset-env": "^7.26.9", "@babel/preset-typescript": "^7.27.0", "babel-plugin-remove-import-export": "^1.1.1", diff --git a/play.pokemonshowdown.com/js/client-chat-tournament.js b/play.pokemonshowdown.com/js/client-chat-tournament.js index 665cfc818..d5f0b1879 100644 --- a/play.pokemonshowdown.com/js/client-chat-tournament.js +++ b/play.pokemonshowdown.com/js/client-chat-tournament.js @@ -612,8 +612,8 @@ TournamentBox.nodeSize = { width: 160, height: 30, radius: 5, - separationX: 20, separationY: 20, - textOffset: -1 + separationX: 20, separationY: 10, + textOffset: 4 }; TournamentBox.prototype.generateBracket = function (data, abbreviated) { @@ -655,9 +655,15 @@ child.highlightLink = true; } } - } else if (node.state === 'inprogress' || node.state === 'available' || node.state === 'challenging') { + } else if ( + node.state === 'inprogress' || node.state === 'available' || node.state === 'challenging' || + node.state === 'unavailable' + ) { for (var i = 0; i < node.children.length; i++) { - node.children[i].highlightLink = true; + var child = node.children[i]; + if (child.team && !child.team.startsWith('(')) { + child.highlightLink = true; + } } } else if (highlightName) { for (var i = 0; i < node.children.length; i++) { @@ -763,6 +769,7 @@ 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) + .attr('y', nodeSize.textOffset) .classed('tournament-bracket-tree-node-team-draw', true) .text(node.team || ''); } else { diff --git a/play.pokemonshowdown.com/js/client-endload.js b/play.pokemonshowdown.com/js/client-endload.js new file mode 100644 index 000000000..5df4649cd --- /dev/null +++ b/play.pokemonshowdown.com/js/client-endload.js @@ -0,0 +1,4 @@ +"use strict"; + +PS.libsLoaded.loaded(); +//# sourceMappingURL=client-endload.js.map \ No newline at end of file diff --git a/play.pokemonshowdown.com/js/client-mainmenu.js b/play.pokemonshowdown.com/js/client-mainmenu.js index 7532efb2b..66ce03f76 100644 --- a/play.pokemonshowdown.com/js/client-mainmenu.js +++ b/play.pokemonshowdown.com/js/client-mainmenu.js @@ -1339,11 +1339,6 @@ bufs[curBuf] += BattleLog.escapeHTML(curSection) + ''; } var formatName = BattleLog.escapeFormat(format.id); - if (formatName.charAt(0) !== '[') formatName = '[Gen 6] ' + formatName; - formatName = formatName.replace('[Gen 9] ', ''); - formatName = formatName.replace('[Gen 9 ', '['); - formatName = formatName.replace('[Gen 8 ', '['); - formatName = formatName.replace('[Gen 7 ', '['); bufs[curBuf] += ( '
  • '; + return buf + ' draggable="true">' + BattleLog.escapeHTML(room.title.slice(1, closeBracketIndex)) + '' + BattleLog.escapeHTML(room.title.slice(closeBracketIndex + 1)) + ' {} - +

    ) : !PS.user.named ? (

    {} - + {} +

    ) : (

    - {} - {} - + {} + {} +

    )); if (isSelf) { @@ -158,13 +155,13 @@ class UserPanel extends PSRoomPanel { } } + const avatar = user.avatar !== '[loading]' ? Dex.resolveAvatar(`${user.avatar || 'unknown'}`) : null; return [
    - {user.avatar !== '[loading]' && - } + {avatar && (room.isSelf ? ( + + ) : ( + + ))} { return
    {showLookup &&
    {!room.userid &&

    @@ -218,7 +215,7 @@ class UserPanel extends PSRoomPanel { class UserOptionsPanel extends PSRoomPanel { static readonly id = 'useroptions'; - static readonly routes = ['useroptions']; + static readonly routes = ['useroptions-*']; static readonly location = 'popup'; static readonly noURL = true; declare state: { @@ -228,6 +225,12 @@ class UserOptionsPanel extends PSRoomPanel { requestSent?: boolean, data?: Record, }; + getTargets() { + const [, targetUser, targetRoomid] = this.props.room.id.split('-'); + let targetRoom = (PS.rooms[targetRoomid] || null) as ChatRoom | null; + if (targetRoom?.type !== 'chat') targetRoom = null; + return { targetUser: targetUser as ID, targetRoomid: targetRoomid as RoomID, targetRoom }; + } handleMute = (ev: Event) => { this.setState({ showMuteInput: true, showBanInput: false }); @@ -247,52 +250,42 @@ class UserOptionsPanel extends PSRoomPanel { }; handleConfirm = (ev: Event) => { - let data = this.state.data; + const data = this.state.data; if (!data) return; - let roomid = toRoomid(data.room); - let room = PS.rooms[roomid]; + const { targetUser, targetRoom } = this.getTargets(); let cmd = ''; if (data.action === "Mute") { cmd += data.duration === "1 hour" ? "/hourmute " : "/mute "; - cmd += `${data.targetUser} ${data.reason ? ',' + data.reason : ''}`; + cmd += `${targetUser} ${data.reason ? ',' + data.reason : ''}`; } else { cmd += data.duration === "1 week" ? "/weekban " : "/ban "; - cmd += `${data.targetUser} ${data.reason ? ',' + data.reason : ''}`; + cmd += `${targetUser} ${data.reason ? ',' + data.reason : ''}`; } - room?.send(cmd); + targetRoom?.send(cmd); this.close(); }; handleAddFriend = (ev: Event) => { - let args = (this.props.room?.parentElem as HTMLInputElement).value.split(","); - let [targetUser, roomid] = args; - PS.rooms[roomid]?.send(`/friend add ${targetUser}`); + const { targetUser, targetRoom } = this.getTargets(); + targetRoom?.send(`/friend add ${targetUser}`); this.setState({ requestSent: true }); ev.preventDefault(); ev.stopImmediatePropagation(); }; handleIgnore = () => { - let args = (this.props.room?.parentElem as HTMLInputElement).value.split(","); - let [targetUser, roomid] = args; - let room = PS.rooms[roomid]; - room?.send(`/ignore ${targetUser}`); + const { targetUser, targetRoom } = this.getTargets(); + targetRoom?.send(`/ignore ${targetUser}`); this.close(); }; muteUser = (ev: Event) => { this.setState({ showMuteInput: false }); - let hrMute = (ev.currentTarget as HTMLButtonElement).value === "1hr"; - let args = (this.props.room?.parentElem as HTMLInputElement).value.split(","); - let [targetUser, roomid] = args; - let room = PS.rooms[roomid]; - if (room?.type !== "chat") return; // should never happen - let reason = this.base?.querySelector("input[name=mutereason]")?.value; - let data = { + const hrMute = (ev.currentTarget as HTMLButtonElement).value === "1hr"; + const reason = this.base?.querySelector("input[name=mutereason]")?.value; + const data = { action: 'Mute', - targetUser, - room: room?.title, reason, duration: hrMute ? "1 hour" : "7 minutes", }; @@ -303,16 +296,10 @@ class UserOptionsPanel extends PSRoomPanel { banUser = (ev: Event) => { this.setState({ showBanInput: false }); - let weekBan = (ev.currentTarget as HTMLButtonElement).value === "1wk"; - let args = (this.props.room?.parentElem as HTMLInputElement).value.split(","); - let [targetUser, roomid] = args; - let room = PS.rooms[roomid]; - if (room?.type !== "chat") return; // should never happen - let reason = this.base?.querySelector("input[name=banreason]")?.value; - let data = { + const weekBan = (ev.currentTarget as HTMLButtonElement).value === "1wk"; + const reason = this.base?.querySelector("input[name=banreason]")?.value; + const data = { action: 'Ban', - targetUser, - room: room?.title, reason, duration: weekBan ? "1 week" : "2 days", }; @@ -321,20 +308,16 @@ class UserOptionsPanel extends PSRoomPanel { ev.stopImmediatePropagation(); }; - update = () => { - this.forceUpdate(); - }; - override render() { const room = this.props.room; - const parentRoom = PS.rooms[this.props.room.parentRoomid! || ''] as ChatRoom; let canMute = false; let canBan = false; - if (parentRoom?.type === "chat") { - let banPerms = ["@", "#", "~"]; - let mutePerms = ["%", ...banPerms]; - canMute = mutePerms.includes(parentRoom.users[PS.user.userid].charAt(0)); - canBan = banPerms.includes(parentRoom.users[PS.user.userid].charAt(0)); + const { targetUser, targetRoom } = this.getTargets(); + if (targetRoom) { + const banPerms = ["@", "#", "~"]; + const mutePerms = ["%", ...banPerms]; + canMute = mutePerms.includes(targetRoom.users[PS.user.userid]?.charAt(0)); + canBan = banPerms.includes(targetRoom.users[PS.user.userid]?.charAt(0)); } return

    @@ -344,16 +327,15 @@ class UserOptionsPanel extends PSRoomPanel {

    -

    {this.state.requestSent ? ( - + ) : ( {} @@ -392,9 +375,10 @@ class UserOptionsPanel extends PSRoomPanel { ))} {} {canBan && !this.state.showMuteInput && !this.state.showConfirm && (this.state.showBanInput ? (

    -
    {} {} @@ -483,6 +467,10 @@ class OptionsPanel extends PSRoomPanel { static readonly location = 'popup'; declare state: { showStatusInput?: boolean, showStatusUpdated?: boolean }; + override componentDidMount() { + super.componentDidMount(); + this.subscribeTo(PS.user); + } setTheme = (e: Event) => { const theme = (e.currentTarget as HTMLSelectElement).value as 'light' | 'dark' | 'system'; PS.prefs.set('theme', theme); @@ -493,6 +481,7 @@ class OptionsPanel extends PSRoomPanel { switch (layout) { case '': PS.prefs.set('onepanel', null); + PS.rightPanel ||= PS.rooms['rooms'] || null; break; case 'onepanel': PS.prefs.set('onepanel', true); @@ -580,7 +569,7 @@ class OptionsPanel extends PSRoomPanel {

    )} - {PS.user.named && (PS.user.registered ? + {PS.user.named && (PS.user.registered?.userid === PS.user.userid ? : )} @@ -767,7 +756,8 @@ class LoginPanel extends PSRoomPanel {

    } {loginState?.needsPassword &&

    if you registered this name: -

    - {} + {} @@ -1017,58 +1009,36 @@ class ChangePasswordPanel extends PSRoomPanel {

    }

    Change your password:

    - +

    - +

    - +

    - +

    + {}

    -
    ; } @@ -1083,10 +1053,6 @@ class RegisterPanel extends PSRoomPanel { declare state: { errorMsg: string }; - update = () => { - this.forceUpdate(); - }; - handleRegisterUser = (ev: Event) => { ev.preventDefault(); let captcha = this.base?.querySelector('input[name=captcha]')?.value; @@ -1130,55 +1096,37 @@ class RegisterPanel extends PSRoomPanel {

    }

    Register your account:

    - +

    - +

    - +

    -

    - +

    - + {}

    diff --git a/play.pokemonshowdown.com/src/panel-teambuilder-team.tsx b/play.pokemonshowdown.com/src/panel-teambuilder-team.tsx index 86f505a65..7917583af 100644 --- a/play.pokemonshowdown.com/src/panel-teambuilder-team.tsx +++ b/play.pokemonshowdown.com/src/panel-teambuilder-team.tsx @@ -233,7 +233,7 @@ class TeamPanel extends PSRoomPanel { List