diff --git a/README.md b/README.md index 814f022be..97de6f928 100644 --- a/README.md +++ b/README.md @@ -25,16 +25,17 @@ Browser support Pokémon Showdown currently supports, in order of preference: - Chrome + - Chromium browsers (Edge, Vivaldi, Brave, Opera...) - Firefox - - Opera - Safari 5+ - - IE11+ + - IE11+ and Edge - Chrome/Firefox/Safari for various mobile devices + - any remotely modern browser -Pokémon Showdown is usable, but expect degraded performance and certain features not to work in: +Pokémon Showdown is usable, but expect degraded performance and certain features not to work in extremely legacy browsers like: - - Safari 4+ - - IE9+ + - Safari 4 + - IE9-10 Pokémon Showdown is mostly developed on Chrome, and Chrome or the desktop client is required for certain features like dragging-and-dropping teams from PS to your computer. However, bugs reported on any supported browser will usually be fixed pretty quickly. diff --git a/eslint-ps-standard.mjs b/eslint-ps-standard.mjs index 40d574bf2..38c7965cf 100644 --- a/eslint-ps-standard.mjs +++ b/eslint-ps-standard.mjs @@ -100,7 +100,7 @@ export const defaultRules = { "no-return-assign": "error", "no-self-compare": "error", "no-sequences": "error", - "no-shadow": "error", + "no-shadow": "warn", "no-template-curly-in-string": "error", "no-throw-literal": "warn", "no-unmodified-loop-condition": "error", diff --git a/play.pokemonshowdown.com/src/client-main.ts b/play.pokemonshowdown.com/src/client-main.ts index b38304caf..c8feb7ecb 100644 --- a/play.pokemonshowdown.com/src/client-main.ts +++ b/play.pokemonshowdown.com/src/client-main.ts @@ -1227,6 +1227,8 @@ export class PSRoom extends PSStreamModel implements RoomOptions { this.add('||/afd off - Disable April Fools\' Day jokes until the next refresh, and set /afd default.'); this.add('||/afd never - Disable April Fools\' Day jokes permanently.'); return false; + default: + return true; } }, 'autojoin,cmd,crq,query'() { diff --git a/play.pokemonshowdown.com/src/panel-battle.tsx b/play.pokemonshowdown.com/src/panel-battle.tsx index ef9ff6322..8c9fb34d7 100644 --- a/play.pokemonshowdown.com/src/panel-battle.tsx +++ b/play.pokemonshowdown.com/src/panel-battle.tsx @@ -70,7 +70,7 @@ class BattlesPanel extends PSRoomPanel { renderBattleLink(battle: BattleDesc) { const format = battle.id.split('-')[1]; const minEloMessage = typeof battle.minElo === 'number' ? `rated ${battle.minElo}` : battle.minElo; - return
+ return
{minEloMessage && ({minEloMessage})} [{format}]
{battle.p1} vs. {battle.p2} diff --git a/play.pokemonshowdown.com/src/panel-chat.tsx b/play.pokemonshowdown.com/src/panel-chat.tsx index 2eebc2203..0de6b5386 100644 --- a/play.pokemonshowdown.com/src/panel-chat.tsx +++ b/play.pokemonshowdown.com/src/panel-chat.tsx @@ -489,7 +489,8 @@ export class ChatRoom extends PSRoom { } override sendDirect(line: string) { if (this.pmTarget) { - PS.send(`|/pm ${this.pmTarget}, ${line}`); + line = line.split('\n').filter(Boolean).map(row => `/pm ${this.pmTarget!}, ${row}`).join('\n'); + PS.send(`|${line}`); return; } super.sendDirect(line); @@ -1057,49 +1058,59 @@ class ChatPanel extends PSRoomPanel { } export class ChatUserList extends preact.Component<{ - room: ChatRoom, left?: number, top?: number, minimized?: boolean, + room: ChatRoom, left?: number, top?: number, minimized?: boolean, static?: boolean, }> { - override state = { - expanded: false, - }; - toggleExpanded = () => { - this.setState({ expanded: !this.state.expanded }); - }; render() { const room = this.props.room; let userList = Object.entries(room.users) as [ID, string][]; PSUtils.sortBy(userList, ([id, name]) => ( [PS.server.getGroup(name.charAt(0)).order, !name.endsWith('@!'), id] )); - return
    -
  • {room.userCount} users
  • - {userList.map(([userid, name]) => { - const groupSymbol = name.charAt(0); - const group = PS.server.groups[groupSymbol] || { type: 'user', order: 0 }; - let color; - if (name.endsWith('@!')) { - name = name.slice(0, -2); - color = '#888888'; - } else { - color = BattleLog.usernameColor(userid); - } - return
  • ; - })} -
; + {!this.props.minimized ? ( +
{room.userCount} users
+ ) : room.id === 'dm-' ? ( + <> + + + ) : pmTargetid ? ( + <> + + + + ) : ( + + )} +
    + {userList.map(([userid, name]) => { + const groupSymbol = name.charAt(0); + const group = PS.server.groups[groupSymbol] || { type: 'user', order: 0 }; + let color; + if (name.endsWith('@!')) { + name = name.slice(0, -2); + color = '#888888'; + } else { + color = BattleLog.usernameColor(userid); + } + return
  • ; + })} +
+
; } } diff --git a/play.pokemonshowdown.com/src/panel-mainmenu.tsx b/play.pokemonshowdown.com/src/panel-mainmenu.tsx index c0a148c7c..215959c10 100644 --- a/play.pokemonshowdown.com/src/panel-mainmenu.tsx +++ b/play.pokemonshowdown.com/src/panel-mainmenu.tsx @@ -546,7 +546,10 @@ class MainMenuPanel extends PSRoomPanel { if (!PS.user.userid || PS.isOffline) { return {PS.isOffline &&

@@ -591,22 +594,22 @@ class MainMenuPanel extends PSRoomPanel { {this.renderSearchButton()}

diff --git a/play.pokemonshowdown.com/src/panel-popups.tsx b/play.pokemonshowdown.com/src/panel-popups.tsx index f384032ae..029f499fc 100644 --- a/play.pokemonshowdown.com/src/panel-popups.tsx +++ b/play.pokemonshowdown.com/src/panel-popups.tsx @@ -6,8 +6,8 @@ import { PSLoginServer } from "./client-connection"; import { PSBackground } from "./client-core"; import { PSRoom, type RoomOptions, PS, type PSLoginState, type RoomID, type TimestampOptions } from "./client-main"; import { type BattleRoom } from "./panel-battle"; -import type { ChatRoom } from "./panel-chat"; -import { PSRoomPanel, PSPanelWrapper } from "./panels"; +import { ChatUserList, type ChatRoom } from "./panel-chat"; +import { PSRoomPanel, PSPanelWrapper, PSView } from "./panels"; import { PSHeader } from "./panel-topbar"; /** @@ -412,6 +412,24 @@ class UserOptionsPanel extends PSRoomPanel { } } +class UserListPanel extends PSRoomPanel { + static readonly id = 'userlist'; + static readonly routes = ['userlist']; + static readonly location = 'semimodal-popup'; + static readonly noURL = true; + override render() { + const room = this.props.room; + const parentRoom = room.getParent() as ChatRoom; + if (parentRoom.type !== 'chat' && parentRoom.type !== 'battle') { + throw new Error(`UserListPanel: ${room.id} is not a chat room`); + } + + return
+ +
; + } +} + class VolumePanel extends PSRoomPanel { static readonly id = 'volume'; static readonly routes = ['volume']; @@ -1181,12 +1199,12 @@ class RegisterPanel extends PSRoomPanel {

@@ -1262,27 +1280,28 @@ class BackgroundListPanel extends PSRoomPanel {

Official

@@ -1318,11 +1337,12 @@ class ChatFormattingPanel extends PSRoomPanel { override render() { const room = this.props.room; + const ctrl = PSView.isMac ? 'Cmd' : 'Ctrl'; return

Usable formatting:

-

**bold** (Ctrl + B)

-

__italics__ (Ctrl + I)

-

``code formatting`` (Ctrl + `)

+

**bold** ({ctrl}+B)

+

__italics__ ({ctrl}+I)

+

``code formatting`` (Ctrl+`)

~~strikethrough~~

^^superscript^^

\\subscript\\

@@ -1333,8 +1353,7 @@ class ChatFormattingPanel extends PSRoomPanel { type="checkbox" name="greentext" checked={PS.prefs.chatformatting.hidegreentext} - /> Suppress{' '} - >greentext + /> Suppress >greentext

@@ -1355,8 +1374,7 @@ class ChatFormattingPanel extends PSRoomPanel { type="checkbox" name="spoiler" checked={PS.prefs.chatformatting.hidespoiler} - /> Auto-show spoilers:{' '} - these things + /> Auto-show spoilers: these things

@@ -1366,8 +1384,7 @@ class ChatFormattingPanel extends PSRoomPanel { type="checkbox" name="links" checked={PS.prefs.chatformatting.hidelinks} - /> Make [[clickable links]] - unclickable + /> Make [[clickable links]] unclickable

@@ -1499,6 +1516,7 @@ class RoomTabListPanel extends PSRoomPanel { PS.addRoomType( UserPanel, UserOptionsPanel, + UserListPanel, VolumePanel, OptionsPanel, LoginPanel, diff --git a/play.pokemonshowdown.com/src/panel-rooms.tsx b/play.pokemonshowdown.com/src/panel-rooms.tsx index 3919da039..d8af03eb8 100644 --- a/play.pokemonshowdown.com/src/panel-rooms.tsx +++ b/play.pokemonshowdown.com/src/panel-rooms.tsx @@ -157,20 +157,20 @@ class RoomsPanel extends PSRoomPanel { Hide

(b.userCount || 0) - (a.userCount || 0)); return

{title}

- {sortedRooms.map(roomInfo =>
+ {sortedRooms.map(roomInfo =>
{roomInfo.userCount !== undefined && ({roomInfo.userCount} users)} {roomInfo.title}
diff --git a/play.pokemonshowdown.com/src/panel-topbar.tsx b/play.pokemonshowdown.com/src/panel-topbar.tsx index 6ee0da138..f9d5d6d46 100644 --- a/play.pokemonshowdown.com/src/panel-topbar.tsx +++ b/play.pokemonshowdown.com/src/panel-topbar.tsx @@ -152,40 +152,27 @@ export class PSHeader extends preact.Component<{ style: object }> { ; } const ariaLabel = id === 'rooms' ? { "aria-label": "Join chat" } : {}; - return
  • + return
  • - {icon} {roomTitle} + {icon} {roomTitle} {closeButton}
  • ; } - sideRoomMargin = 0; handleRoomTabOverflow = () => { if (PS.leftPanelWidth === null || !this.base) return; const userbarLeft = this.base.querySelector('div.userbar')?.getBoundingClientRect()?.left; - const plusTab = this.base.querySelector('a.roomtab[aria-label="Join chat"]'); - const plusTabRight = plusTab?.getBoundingClientRect()?.right; + const plusTabRight = this.base.querySelector('a.roomtab[aria-label="Join chat"]')?.getBoundingClientRect()?.right; const overflow = this.base.querySelector('.overflow'); - const siderooms = this.base.querySelector('.siderooms'); - if (!overflow || !userbarLeft || !plusTab || !plusTabRight || !siderooms) return; + if (!overflow || !userbarLeft || !plusTabRight) return; - // this has too much perf impact and bugs... just use vertical tabs - // if (PS.leftPanelWidth && Math.abs((userbarLeft - 5) - plusTabRight) > 0.5) { - // this.sideRoomMargin += (userbarLeft - 5) - plusTabRight; - // if (this.sideRoomMargin < 0) this.sideRoomMargin = 0; - // this.sideRoomMargin = Math.min(Math.max(PS.leftPanelWidth - 52, 0), this.sideRoomMargin); - - // siderooms.style.marginLeft = `${this.sideRoomMargin}px`; - // plusTabRight = plusTab.getBoundingClientRect().right; - // } - - if (plusTabRight > userbarLeft) { + if (plusTabRight > userbarLeft - 3) { overflow.style.display = 'block'; } else { overflow.style.display = 'none'; @@ -260,32 +247,31 @@ export class PSHeader extends preact.Component<{ style: object }> { } else { document.documentElement.classList?.remove('scroll-snap-enabled'); } - this.sideRoomMargin = Math.max(PS.leftPanelWidth - 52, 0); return