mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
Preact: Refactor room args
- custom room info is now in `room.args` - rooms not compatible with URLs can be marked with `noURL` - PMs renamed to DMs, and are now `dm-[userid]` instead of `pm-[userid1]-[userid2]` - custom popups now supported - find-user popup now supported - popups no longer come with padding, making them behave more like other panels
This commit is contained in:
parent
1572b07cb4
commit
610c33dfa4
|
|
@ -102,7 +102,7 @@ RewriteRule ^([A-Za-z0-9][A-Za-z0-9-]*)/$ /$1 [R=301,L]
|
|||
|
||||
# Anything that looks like a roomid: connect to the server
|
||||
RewriteRule ^$ - [E=INDEX_PAGE:1]
|
||||
RewriteRule ^(preactalpha|login|(pm|challenge|user|viewuser)-[a-z0-9-]*)$ ./preactalpha.html [L,E=INDEX_PAGE:1]
|
||||
RewriteRule ^(preactalpha|login|(dm|challenge|user|viewuser)-[a-z0-9-]*)$ ./preactalpha.html [L,E=INDEX_PAGE:1]
|
||||
RewriteRule ^([A-Za-z0-9][A-Za-z0-9-]*)$ ./ [L,E=INDEX_PAGE:1]
|
||||
|
||||
# RewriteRule ^crossprotocol\.html$ - [E=CROSSPROTOCOL:1]
|
||||
|
|
|
|||
|
|
@ -290,6 +290,7 @@ class PSUser extends PSStreamModel<PSLoginState | null> {
|
|||
avatar = "1";
|
||||
challstr = '';
|
||||
loggingIn: string | null = null;
|
||||
initializing = true;
|
||||
gapiLoaded = false;
|
||||
setName(fullName: string, named: boolean, avatar: string) {
|
||||
const loggingIn = (!this.named && named);
|
||||
|
|
@ -390,13 +391,13 @@ class PSUser extends PSStreamModel<PSLoginState | null> {
|
|||
updateLogin(update: PSLoginState) {
|
||||
this.update(update);
|
||||
if (!PS.rooms['login']) {
|
||||
PS.addRoom({ id: 'login' as RoomID, loginState: update });
|
||||
PS.addRoom({ id: 'login' as RoomID, args: update });
|
||||
PS.update();
|
||||
}
|
||||
}
|
||||
handleAssertion(name: string, assertion?: string | null) {
|
||||
if (!assertion) {
|
||||
alert("Error logging in.");
|
||||
PS.alert("Error logging in.");
|
||||
return;
|
||||
}
|
||||
this.loggingIn = null;
|
||||
|
|
@ -408,7 +409,7 @@ class PSUser extends PSStreamModel<PSLoginState | null> {
|
|||
if (assertion.startsWith('\r')) assertion = assertion.slice(1);
|
||||
if (assertion.startsWith('\n')) assertion = assertion.slice(1);
|
||||
if (assertion.includes('<')) {
|
||||
alert("Something is interfering with our connection to the login server. Most likely, your internet provider needs you to re-log-in, or your internet provider is blocking Pokémon Showdown.");
|
||||
PS.alert("Something is interfering with our connection to the login server. Most likely, your internet provider needs you to re-log-in, or your internet provider is blocking Pokémon Showdown.");
|
||||
return;
|
||||
}
|
||||
if (assertion === ';') {
|
||||
|
|
@ -418,8 +419,11 @@ class PSUser extends PSStreamModel<PSLoginState | null> {
|
|||
} else if (assertion.startsWith(';;')) {
|
||||
this.updateLogin({ error: assertion.slice(2) });
|
||||
} else if (assertion.includes('\n') || !assertion) {
|
||||
alert("Something is interfering with our connection to the login server.");
|
||||
PS.alert("Something is interfering with our connection to the login server.");
|
||||
} else {
|
||||
// we're getting a little ahead of ourselves
|
||||
this.name = name;
|
||||
this.named = true;
|
||||
PS.send(`|/trn ${name},0,${assertion}`);
|
||||
this.update({ success: true });
|
||||
}
|
||||
|
|
@ -559,7 +563,9 @@ export interface RoomOptions {
|
|||
/** Opens the popup to the right of its parent, instead of the default above/below (for userlists) */
|
||||
rightPopup?: boolean;
|
||||
connected?: boolean;
|
||||
[k: string]: unknown;
|
||||
/** @see {RoomType#noURL} */
|
||||
noURL?: boolean;
|
||||
args?: Record<string, unknown> | null;
|
||||
}
|
||||
|
||||
interface PSNotificationState {
|
||||
|
|
@ -601,6 +607,12 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
|||
|
||||
width = 0;
|
||||
height = 0;
|
||||
/**
|
||||
* popups sometimes initialize hidden, to calculate their position from their
|
||||
* width/height without flickering. But hidden popups can't be focused, so
|
||||
* we need to track their focus timing here.
|
||||
*/
|
||||
hiddenInit = false;
|
||||
parentElem: HTMLElement | null = null;
|
||||
rightPopup = false;
|
||||
|
||||
|
|
@ -610,10 +622,9 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
|||
/** only affects mini-windows */
|
||||
minimized = false;
|
||||
caughtError: string | undefined;
|
||||
/** only on login */
|
||||
loginState?: PSLoginState | null;
|
||||
// for compatibility with RoomOptions
|
||||
[k: string]: unknown;
|
||||
/** @see {RoomType#noURL} */
|
||||
noURL: boolean;
|
||||
args: Record<string, unknown> | null;
|
||||
|
||||
constructor(options: RoomOptions) {
|
||||
super();
|
||||
|
|
@ -625,7 +636,8 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
|||
if (this.location !== 'popup' && this.location !== 'semimodal-popup') this.parentElem = null;
|
||||
if (options.rightPopup) this.rightPopup = true;
|
||||
if (options.connected) this.connected = true;
|
||||
this.loginState = options.loginState || null;
|
||||
this.noURL = options.noURL || false;
|
||||
this.args = options.args || null;
|
||||
}
|
||||
notify(options: { title: string, body?: string, noAutoDismiss?: boolean, id?: string }) {
|
||||
if (options.noAutoDismiss && !options.id) {
|
||||
|
|
@ -724,8 +736,11 @@ class PlaceholderRoom extends PSRoom {
|
|||
type RoomType = (new () => PSRoomPanel) & {
|
||||
readonly id: string,
|
||||
readonly routes: string[],
|
||||
/** optional Room class */
|
||||
readonly Model?: typeof PSRoom,
|
||||
readonly location?: PSRoomLocation,
|
||||
/** do not put the roomid into the URL */
|
||||
noURL?: boolean,
|
||||
icon?: preact.ComponentChildren,
|
||||
title?: string,
|
||||
};
|
||||
|
|
@ -772,7 +787,7 @@ export const PS = new class extends PSModel {
|
|||
// locations cached here because it needs to be guessed before roomTypes is filled in
|
||||
// this cache is optional, but prevents some flickering during loading
|
||||
// to update:
|
||||
// console.log('\t\t' + JSON.stringify(Object.fromEntries(Object.entries(PS.routes).filter(([k, v]) => k !== 'pm-*').map(([k, v]) => [k, '*' + (PS.roomTypes[v].location || '')]))).replaceAll(',', ',\n\t\t').replaceAll('":"', '": "').slice(1, -1) + ',')
|
||||
// console.log('\t\t' + JSON.stringify(Object.fromEntries(Object.entries(PS.routes).filter(([k, v]) => k !== 'dm-*').map(([k, v]) => [k, '*' + (PS.roomTypes[v].location || '')]))).replaceAll(',', ',\n\t\t').replaceAll('":"', '": "').slice(1, -1) + ',')
|
||||
"teambuilder": "*",
|
||||
"news": "*mini-window",
|
||||
"": "*",
|
||||
|
|
@ -1113,6 +1128,7 @@ export const PS = new class extends PSModel {
|
|||
options.location ||= this.getRouteLocation(options.id);
|
||||
options.type ||= this.getRoute(options.id) || '';
|
||||
const RoomType = this.roomTypes[options.type];
|
||||
options.noURL ??= RoomType?.noURL;
|
||||
if (RoomType?.title) options.title = RoomType.title;
|
||||
const Model = RoomType ? (RoomType.Model || PSRoom) : PlaceholderRoom;
|
||||
return new Model(options);
|
||||
|
|
@ -1127,7 +1143,7 @@ export const PS = new class extends PSModel {
|
|||
}
|
||||
getRouteLocation(roomid: RoomID): PSRoomLocation {
|
||||
// must be hardcoded here to have a different loc while also being a ChatRoom
|
||||
if (roomid.startsWith('pm-')) return 'mini-window';
|
||||
if (roomid.startsWith('dm-')) return 'mini-window';
|
||||
const routeInfo = this.getRouteInfo(roomid);
|
||||
if (!routeInfo) return 'left';
|
||||
if (routeInfo.startsWith('*')) return routeInfo.slice(1) as PSRoomLocation;
|
||||
|
|
@ -1249,7 +1265,11 @@ export const PS = new class extends PSModel {
|
|||
return buf;
|
||||
}
|
||||
alert(message: string) {
|
||||
alert(message);
|
||||
this.addRoom({
|
||||
id: `popup-${this.popups.length}` as RoomID,
|
||||
args: { message },
|
||||
});
|
||||
this.update();
|
||||
}
|
||||
prompt(message: string, defaultValue?: string, opts?: {
|
||||
okButton?: string, type?: 'text' | 'password' | 'number',
|
||||
|
|
@ -1261,7 +1281,7 @@ export const PS = new class extends PSModel {
|
|||
}
|
||||
getPMRoom(userid: ID): ChatRoom {
|
||||
const myUserid = PS.user.userid;
|
||||
const roomid = `pm-${[userid, myUserid].sort().join('-')}` as RoomID;
|
||||
const roomid = `dm-${[userid, myUserid].sort().join('-')}` as RoomID;
|
||||
if (this.rooms[roomid]) return this.rooms[roomid] as ChatRoom;
|
||||
this.join(roomid);
|
||||
return this.rooms[roomid]! as ChatRoom;
|
||||
|
|
@ -1269,18 +1289,13 @@ export const PS = new class extends PSModel {
|
|||
addRoom(options: RoomOptions, noFocus = false) {
|
||||
// support hardcoded PM room-IDs
|
||||
if (options.id.startsWith('challenge-')) {
|
||||
options.id = `pm-${options.id.slice(10)}` as RoomID;
|
||||
options.challengeMenuOpen = true;
|
||||
options.id = `dm-${options.id.slice(10)}` as RoomID;
|
||||
options.args = { challengeMenuOpen: true };
|
||||
}
|
||||
if (options.id.startsWith('pm-')) {
|
||||
if (options.id.startsWith('dm-')) {
|
||||
if (options.id.length >= 5 && options.id.endsWith('--')) {
|
||||
options.id = options.id.slice(0, -2) as RoomID;
|
||||
options.initialSlash = true;
|
||||
}
|
||||
if (options.id !== 'pm-' && !options.id.includes('-', 3)) {
|
||||
const userid1 = PS.user.userid;
|
||||
const userid2 = options.id.slice(3);
|
||||
options.id = `pm-${[userid1, userid2].sort().join('-')}` as RoomID;
|
||||
options.args = { initialSlash: true };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1297,7 +1312,7 @@ export const PS = new class extends PSModel {
|
|||
}
|
||||
}
|
||||
if (!noFocus) {
|
||||
if (options.challengeMenuOpen) {
|
||||
if (options.args?.challengeMenuOpen) {
|
||||
(this.rooms[options.id] as ChatRoom).openChallenge();
|
||||
}
|
||||
this.focusRoom(options.id);
|
||||
|
|
@ -1464,9 +1479,9 @@ export const PS = new class extends PSModel {
|
|||
this.leave(this.popups[this.popups.length - 1]);
|
||||
if (!skipUpdate) this.update();
|
||||
}
|
||||
join(roomid: RoomID, side?: PSRoomLocation | null, noFocus?: boolean) {
|
||||
join(roomid: RoomID, location?: PSRoomLocation | null, noFocus?: boolean) {
|
||||
if (this.room.id === roomid) return;
|
||||
this.addRoom({ id: roomid, side }, noFocus);
|
||||
this.addRoom({ id: roomid, location }, noFocus);
|
||||
this.update();
|
||||
}
|
||||
leave(roomid: RoomID) {
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class BattleRoom extends ChatRoom {
|
|||
declare challengingFormat: null;
|
||||
declare challengedFormat: null;
|
||||
|
||||
battle: Battle = null!;
|
||||
override battle: Battle = null!;
|
||||
/** null if spectator, otherwise current player's info */
|
||||
side: BattleRequestSideInfo | null = null;
|
||||
request: BattleRequest | null = null;
|
||||
|
|
|
|||
|
|
@ -29,44 +29,38 @@ export class ChatRoom extends PSRoom {
|
|||
initialSlash = false;
|
||||
challengingFormat: string | null = null;
|
||||
challengedFormat: string | null = null;
|
||||
/** n.b. this will be null outside of battle rooms */
|
||||
battle: Battle | null = null;
|
||||
|
||||
constructor(options: RoomOptions) {
|
||||
super(options);
|
||||
if (options.pmTarget) this.pmTarget = options.pmTarget as string;
|
||||
if (options.challengeMenuOpen) this.challengeMenuOpen = true;
|
||||
if (options.initialSlash) this.initialSlash = true;
|
||||
if (options.args?.pmTarget) this.pmTarget = options.args.pmTarget as string;
|
||||
if (options.args?.challengeMenuOpen) this.challengeMenuOpen = true;
|
||||
if (options.args?.initialSlash) this.initialSlash = true;
|
||||
this.updateTarget(true);
|
||||
this.connect();
|
||||
}
|
||||
override connect() {
|
||||
if (!this.connected) {
|
||||
if (!this.pmTarget) PS.send(`|/join ${this.id}`);
|
||||
if (this.pmTarget === null) PS.send(`|/join ${this.id}`);
|
||||
this.connected = true;
|
||||
this.connectWhenLoggedIn = false;
|
||||
}
|
||||
}
|
||||
updateTarget(force?: boolean) {
|
||||
if (this.id === 'pm-') {
|
||||
if (this.id === 'dm-') {
|
||||
this.pmTarget = PS.user.userid;
|
||||
if (!this.userCount) {
|
||||
this.setUsers(1, [` ${PS.user.userid}`]);
|
||||
}
|
||||
this.title = `[Console]`;
|
||||
} else if (this.id.startsWith('pm-')) {
|
||||
const [id1, id2] = this.id.slice(3).split('-');
|
||||
if (id1 === PS.user.userid && toID(this.pmTarget) !== id2) {
|
||||
this.pmTarget = id2;
|
||||
} else if (id2 === PS.user.userid && toID(this.pmTarget) !== id1) {
|
||||
this.pmTarget = id1;
|
||||
} else if (!force) {
|
||||
return;
|
||||
} else {
|
||||
this.pmTarget = id1;
|
||||
}
|
||||
} else if (this.id.startsWith('dm-')) {
|
||||
const id = this.id.slice(3);
|
||||
this.pmTarget = id;
|
||||
if (!this.userCount) {
|
||||
this.setUsers(2, [` ${id1}`, ` ${id2}`]);
|
||||
this.setUsers(2, [` ${id}`, ` ${PS.user.userid}`]);
|
||||
}
|
||||
this.title = `[PM] ${this.pmTarget}`;
|
||||
this.title = `[DM] ${this.pmTarget}`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
|
@ -191,8 +185,8 @@ export class ChatTextEntry extends preact.Component<{
|
|||
},
|
||||
onKeyDown: this.onKeyDown,
|
||||
});
|
||||
if (this.props.room.initialSlash) {
|
||||
this.props.room.initialSlash = false;
|
||||
if (this.props.room.args?.initialSlash) {
|
||||
this.props.room.args.initialSlash = false;
|
||||
this.miniedit.setValue('/', { start: 1, end: 1 });
|
||||
}
|
||||
if (this.base) this.update();
|
||||
|
|
@ -389,7 +383,7 @@ class ChatTextBox extends preact.Component<{ placeholder: string, class: string
|
|||
|
||||
class ChatPanel extends PSRoomPanel<ChatRoom> {
|
||||
static readonly id = 'chat';
|
||||
static readonly routes = ['pm-*', '*'];
|
||||
static readonly routes = ['dm-*', '*'];
|
||||
static readonly Model = ChatRoom;
|
||||
static readonly location = 'right';
|
||||
static readonly icon = <i class="fa fa-comment-o"></i>;
|
||||
|
|
@ -596,7 +590,7 @@ export class ChatLog extends preact.Component<{
|
|||
if (this.log) {
|
||||
this.log.updateScroll();
|
||||
} else if (this.props.room.battle) {
|
||||
this.log = (this.props.room.battle as Battle).scene.log;
|
||||
this.log = this.props.room.battle.scene.log;
|
||||
this.log.updateScroll();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,10 @@ export class MainMenuRoom extends PSRoom {
|
|||
PSLoginServer.query(
|
||||
'upkeep', { challstr }
|
||||
).then(res => {
|
||||
PS.user.initializing = false;
|
||||
if (!res) return;
|
||||
if (!res.loggedin) return;
|
||||
this.send(`/trn ${res.username},0,${res.assertion}`);
|
||||
PS.user.handleAssertion(res.username, res.assertion);
|
||||
});
|
||||
return;
|
||||
} case 'updateuser': {
|
||||
|
|
@ -235,14 +236,15 @@ export class MainMenuRoom extends PSRoom {
|
|||
handlePM(user1: string, user2: string, message: string) {
|
||||
const userid1 = toID(user1);
|
||||
const userid2 = toID(user2);
|
||||
let roomid = `pm-${[userid1, userid2].sort().join('-')}` as RoomID;
|
||||
if (userid1 === userid2 || !userid1) roomid = 'pm-' as RoomID;
|
||||
const pmTarget = PS.user.userid === userid1 ? user2 : user1;
|
||||
const pmTargetid = PS.user.userid === userid1 ? userid2 : userid1;
|
||||
let roomid = `dm-${pmTargetid}` as RoomID;
|
||||
if (pmTargetid === PS.user.userid) roomid = 'dm-' as RoomID;
|
||||
let room = PS.rooms[roomid];
|
||||
if (!room) {
|
||||
const pmTarget = PS.user.userid === userid1 ? user2 : user1;
|
||||
PS.addRoom({
|
||||
id: roomid,
|
||||
pmTarget,
|
||||
args: { pmTarget },
|
||||
}, true);
|
||||
room = PS.rooms[roomid]!;
|
||||
}
|
||||
|
|
@ -320,7 +322,7 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
|||
override focus() {
|
||||
this.base!.querySelector<HTMLButtonElement>('button.big')!.focus();
|
||||
}
|
||||
submit = (e: Event) => {
|
||||
submit = (ev: Event) => {
|
||||
PS.alert('todo: implement');
|
||||
};
|
||||
handleDragStart = (e: DragEvent) => {
|
||||
|
|
@ -406,14 +408,14 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
|||
|
||||
if (!PS.user.userid || PS.isOffline) {
|
||||
return <TeamForm class="menugroup" onSubmit={this.submit}>
|
||||
<button class="mainmenu1 big button disabled" name="search">
|
||||
<button class="mainmenu1 big button disabled" disabled name="search">
|
||||
<em>{PS.isOffline ? "Disconnected" : "Connecting..."}</em>
|
||||
</button>
|
||||
</TeamForm>;
|
||||
}
|
||||
|
||||
return <TeamForm class="menugroup" onSubmit={this.submit}>
|
||||
<button class="mainmenu1 big button" name="search">
|
||||
<button class="mainmenu1 big button" type="submit">
|
||||
<strong>Battle!</strong><br />
|
||||
<small>Find a random opponent</small>
|
||||
</button>
|
||||
|
|
@ -437,7 +439,7 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
|||
|
||||
<div class="menugroup">
|
||||
<p><button class={"mainmenu4" + onlineButton} name="joinRoom" value="battles">Watch a battle</button></p>
|
||||
<p><button class={"mainmenu5" + onlineButton} name="finduser">Find a user</button></p>
|
||||
<p><button class={"mainmenu5" + onlineButton} name="joinRoom" value="users">Find a user</button></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -537,7 +539,7 @@ class TeamDropdown extends preact.Component<{ format: string }> {
|
|||
|
||||
export class TeamForm extends preact.Component<{
|
||||
children: preact.ComponentChildren, class?: string, format?: string,
|
||||
onSubmit: null | ((e: Event, format: string, team?: Team) => void),
|
||||
onSubmit: ((e: Event, format: string, team?: Team) => void) | null,
|
||||
}> {
|
||||
override state = { format: '[Gen 7] Random Battle' };
|
||||
changeFormat = (e: Event) => {
|
||||
|
|
@ -548,7 +550,7 @@ export class TeamForm extends preact.Component<{
|
|||
const format = this.base!.querySelector<HTMLButtonElement>('button[name=format]')!.value;
|
||||
const teamKey = this.base!.querySelector<HTMLButtonElement>('button[name=team]')!.value;
|
||||
const team = teamKey ? PS.teams.byKey[teamKey] : undefined;
|
||||
if (this.props.onSubmit) this.props.onSubmit(e, format, team);
|
||||
this.props.onSubmit?.(e, format, team);
|
||||
};
|
||||
render() {
|
||||
return <form class={this.props.class} onSubmit={this.submit}>
|
||||
|
|
|
|||
|
|
@ -593,6 +593,7 @@ class TeamDropdownPanel extends PSRoomPanel {
|
|||
static readonly id = 'teamdropdown';
|
||||
static readonly routes = ['teamdropdown'];
|
||||
static readonly location = 'semimodal-popup';
|
||||
static readonly noURL = true;
|
||||
gen = '';
|
||||
format: string | null = null;
|
||||
getTeams() {
|
||||
|
|
@ -728,10 +729,10 @@ class TeamDropdownPanel extends PSRoomPanel {
|
|||
isEmpty = false;
|
||||
}
|
||||
|
||||
return <PSPanelWrapper room={room} width={width}>
|
||||
return <PSPanelWrapper room={room} width={width}><div class="pad">
|
||||
{teamList}
|
||||
{isEmpty && <p><em>No teams found</em></p>}
|
||||
</PSPanelWrapper>;
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -758,6 +759,7 @@ class FormatDropdownPanel extends PSRoomPanel {
|
|||
static readonly id = 'formatdropdown';
|
||||
static readonly routes = ['formatdropdown'];
|
||||
static readonly location = 'semimodal-popup';
|
||||
static readonly noURL = true;
|
||||
gen = '';
|
||||
format: string | null = null;
|
||||
click = (e: MouseEvent) => {
|
||||
|
|
@ -834,7 +836,7 @@ class FormatDropdownPanel extends PSRoomPanel {
|
|||
|
||||
const width = columns.length * 225 + 10;
|
||||
|
||||
return <PSPanelWrapper room={room} width={width}>
|
||||
return <PSPanelWrapper room={room} width={width}><div class="pad">
|
||||
{columns.map(column => <ul class="options" onClick={this.click}>
|
||||
{column.map(format => format.id ? (
|
||||
<li><button value={format.name} class="option">
|
||||
|
|
@ -847,7 +849,7 @@ class FormatDropdownPanel extends PSRoomPanel {
|
|||
))}
|
||||
</ul>)}
|
||||
<div style="float: left"></div>
|
||||
</PSPanelWrapper>;
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
*/
|
||||
|
||||
import preact from "../js/lib/preact";
|
||||
import { PS, PSRoom, type RoomOptions, type RoomID } from "./client-main";
|
||||
import { PS, PSRoom, type RoomOptions, type RoomID, type PSLoginState } from "./client-main";
|
||||
import { PSMain, PSPanelWrapper, PSRoomPanel } from "./panels";
|
||||
import type { Battle } from "./battle";
|
||||
import { Dex, toID, toRoomid, toUserid, type ID } from "./battle-dex";
|
||||
|
|
@ -152,7 +152,7 @@ export class PSHeader extends preact.Component<{ style: object }> {
|
|||
if (!PS.connected) {
|
||||
return <button class="button" disabled><em>Offline</em></button>;
|
||||
}
|
||||
if (!PS.user.userid) {
|
||||
if (PS.user.initializing) {
|
||||
return <button class="button" disabled><em>Connecting...</em></button>;
|
||||
}
|
||||
if (!PS.user.named) {
|
||||
|
|
@ -205,27 +205,33 @@ preact.render(<PSMain />, document.body, document.getElementById('ps-frame')!);
|
|||
|
||||
export class UserRoom extends PSRoom {
|
||||
override readonly classType = 'user';
|
||||
userid: ID;
|
||||
name: string;
|
||||
isSelf: boolean;
|
||||
userid!: ID;
|
||||
name!: string;
|
||||
isSelf!: boolean;
|
||||
constructor(options: RoomOptions) {
|
||||
super(options);
|
||||
this.userid = (this.id.split('-')[1] || '') as ID;
|
||||
const userid = (this.id.split('-')[1] || '') as ID;
|
||||
this.setName(options.args?.username as string || userid);
|
||||
}
|
||||
setName(name: string) {
|
||||
this.name = name;
|
||||
this.userid = toID(name);
|
||||
this.isSelf = (this.userid === PS.user.userid);
|
||||
this.name = options.username as string || this.userid;
|
||||
if (/[a-zA-Z0-9]/.test(this.name.charAt(0))) this.name = ' ' + this.name;
|
||||
this.update(null);
|
||||
PS.send(`|/cmd userdetails ${this.userid}`);
|
||||
}
|
||||
}
|
||||
|
||||
class UserPanel extends PSRoomPanel<UserRoom> {
|
||||
static readonly id = 'user';
|
||||
static readonly routes = ['user-*', 'viewuser-*'];
|
||||
static readonly routes = ['user-*', 'viewuser-*', 'users'];
|
||||
static readonly Model = UserRoom;
|
||||
static readonly location = 'popup';
|
||||
|
||||
override render() {
|
||||
renderUser() {
|
||||
const room = this.props.room;
|
||||
if (!room.userid) return null;
|
||||
const user = PS.mainmenu.userdetailsCache[room.userid] || { userid: room.userid, avatar: '[loading]' };
|
||||
const name = room.name.slice(1);
|
||||
const hideInteraction = room.id.startsWith('viewuser-');
|
||||
|
|
@ -308,7 +314,7 @@ class UserPanel extends PSRoomPanel<UserRoom> {
|
|||
buttonbar.push(isSelf ? (
|
||||
<p class="buttonbar">
|
||||
<button class="button" disabled>Challenge</button> {}
|
||||
<button class="button" data-href="/pm-">Chat Self</button>
|
||||
<button class="button" data-href="/dm-">Chat Self</button>
|
||||
</p>
|
||||
) : !PS.user.named ? (
|
||||
<p class="buttonbar">
|
||||
|
|
@ -318,7 +324,7 @@ class UserPanel extends PSRoomPanel<UserRoom> {
|
|||
) : (
|
||||
<p class="buttonbar">
|
||||
<button class="button" data-href={`/challenge-${user.userid}`}>Challenge</button> {}
|
||||
<button class="button" data-href={`/pm-${user.userid}`}>Chat</button> {}
|
||||
<button class="button" data-href={`/dm-${user.userid}`}>Chat</button> {}
|
||||
<button class="button disabled" name="userOptions">{'\u2026'}</button>
|
||||
</p>
|
||||
));
|
||||
|
|
@ -333,27 +339,63 @@ class UserPanel extends PSRoomPanel<UserRoom> {
|
|||
}
|
||||
}
|
||||
|
||||
return <PSPanelWrapper room={room}>
|
||||
<div class="userdetails">
|
||||
{user.avatar !== '[loading]' &&
|
||||
<img
|
||||
class={'trainersprite' + (room.isSelf ? ' yours' : '')}
|
||||
src={Dex.resolveAvatar(`${user.avatar || 'unknown'}`)}
|
||||
/>}
|
||||
<strong><a
|
||||
href={`//${Config.routes.users}/${user.userid}`} target="_blank"
|
||||
style={{ color: away ? '#888888' : BattleLog.usernameColor(user.userid) }}
|
||||
>
|
||||
{name}
|
||||
</a></strong><br />
|
||||
{status && <div class="userstatus">{status}</div>}
|
||||
{groupName && <div class="usergroup roomgroup">{groupName}</div>}
|
||||
{globalGroupName && <div class="usergroup globalgroup">{globalGroupName}</div>}
|
||||
{user.customgroup && <div class="usergroup globalgroup">{user.customgroup}</div>}
|
||||
{!hideInteraction && roomsList}
|
||||
</div>
|
||||
{buttonbar}
|
||||
</PSPanelWrapper>;
|
||||
return [<div class="userdetails">
|
||||
{user.avatar !== '[loading]' &&
|
||||
<img
|
||||
class={'trainersprite' + (room.isSelf ? ' yours' : '')}
|
||||
src={Dex.resolveAvatar(`${user.avatar || 'unknown'}`)}
|
||||
/>}
|
||||
<strong><a
|
||||
href={`//${Config.routes.users}/${user.userid}`} target="_blank"
|
||||
style={{ color: away ? '#888888' : BattleLog.usernameColor(user.userid) }}
|
||||
>
|
||||
{name}
|
||||
</a></strong><br />
|
||||
{status && <div class="userstatus">{status}</div>}
|
||||
{groupName && <div class="usergroup roomgroup">{groupName}</div>}
|
||||
{globalGroupName && <div class="usergroup globalgroup">{globalGroupName}</div>}
|
||||
{user.customgroup && <div class="usergroup globalgroup">{user.customgroup}</div>}
|
||||
{!hideInteraction && roomsList}
|
||||
</div>, buttonbar];
|
||||
}
|
||||
|
||||
lookup = (ev: Event) => {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
const room = this.props.room;
|
||||
const username = this.base!.querySelector<HTMLInputElement>('input[name=username]')?.value;
|
||||
room.setName(username || '');
|
||||
};
|
||||
maybeReset = (ev: Event) => {
|
||||
const room = this.props.room;
|
||||
const username = this.base!.querySelector<HTMLInputElement>('input[name=username]')?.value;
|
||||
if (toID(username) !== room.userid) {
|
||||
room.setName('');
|
||||
}
|
||||
};
|
||||
override focus() {
|
||||
this.base?.querySelector<HTMLElement>('.autofocus')?.focus();
|
||||
}
|
||||
|
||||
override render() {
|
||||
const room = this.props.room;
|
||||
const showLookup = room.id === 'users';
|
||||
|
||||
return <PSPanelWrapper room={room}><div class="pad">
|
||||
{showLookup && <form onSubmit={this.lookup} style={{ minWidth: '278px' }}>
|
||||
<label class="label">
|
||||
Username:
|
||||
<input type="search" name="username" class="textbox autofocus" onInput={this.maybeReset} onChange={this.maybeReset} />
|
||||
</label>
|
||||
{!room.userid && <p class="buttonbar">
|
||||
<button type="submit" class="button"><strong>Look up</strong></button> {}
|
||||
<button name="closeRoom" class="button">Close</button>
|
||||
</p>}
|
||||
{!!room.userid && <hr />}
|
||||
</form>}
|
||||
|
||||
{this.renderUser()}
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -380,7 +422,7 @@ class VolumePanel extends PSRoomPanel {
|
|||
}
|
||||
override render() {
|
||||
const room = this.props.room;
|
||||
return <PSPanelWrapper room={room}>
|
||||
return <PSPanelWrapper room={room}><div class="pad">
|
||||
<h3>Volume</h3>
|
||||
<p class="volume">
|
||||
<label class="optlabel">
|
||||
|
|
@ -421,7 +463,7 @@ class VolumePanel extends PSRoomPanel {
|
|||
<input type="checkbox" name="mute" checked={PS.prefs.mute} onChange={this.setMute} /> Mute all
|
||||
</label>
|
||||
</p>
|
||||
</PSPanelWrapper>;
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -437,7 +479,7 @@ class OptionsPanel extends PSRoomPanel {
|
|||
};
|
||||
override render() {
|
||||
const room = this.props.room;
|
||||
return <PSPanelWrapper room={room}>
|
||||
return <PSPanelWrapper room={room}><div class="pad">
|
||||
<h3>Graphics</h3>
|
||||
<p>
|
||||
<label class="optlabel">Theme: <select onChange={this.setTheme}>
|
||||
|
|
@ -446,7 +488,7 @@ class OptionsPanel extends PSRoomPanel {
|
|||
<option value="system" selected={PS.prefs.theme === 'system'}>Match system theme</option>
|
||||
</select></label>
|
||||
</p>
|
||||
</PSPanelWrapper>;
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -491,16 +533,14 @@ class LoginPanel extends PSRoomPanel {
|
|||
this.close();
|
||||
return;
|
||||
}
|
||||
this.props.room.loginState = args;
|
||||
this.props.room.args = args;
|
||||
setTimeout(() => this.focus(), 1);
|
||||
}
|
||||
this.forceUpdate();
|
||||
}));
|
||||
// I think it's the click when opening the panel that causes focus to be lost
|
||||
setTimeout(() => this.focus(), 1);
|
||||
}
|
||||
getUsername() {
|
||||
const loginName = PS.user.loggingIn || this.props.room.loginState?.name;
|
||||
const loginName = PS.user.loggingIn || this.props.room.args?.name as string;
|
||||
if (loginName) return loginName;
|
||||
|
||||
const input = this.base?.querySelector<HTMLInputElement>('input[name=username]');
|
||||
|
|
@ -529,7 +569,7 @@ class LoginPanel extends PSRoomPanel {
|
|||
reset = (ev: Event) => {
|
||||
ev.preventDefault();
|
||||
ev.stopImmediatePropagation();
|
||||
this.props.room.loginState = null;
|
||||
this.props.room.args = null;
|
||||
this.forceUpdate();
|
||||
};
|
||||
handleShowPassword = (ev: Event) => {
|
||||
|
|
@ -539,8 +579,8 @@ class LoginPanel extends PSRoomPanel {
|
|||
};
|
||||
override render() {
|
||||
const room = this.props.room;
|
||||
const loginState = room.loginState;
|
||||
return <PSPanelWrapper room={room} width={280}>
|
||||
const loginState = room.args as PSLoginState;
|
||||
return <PSPanelWrapper room={room} width={280}><div class="pad">
|
||||
<h3>Log in</h3>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
{loginState?.error && <p class="error">{loginState.error}</p>}
|
||||
|
|
@ -601,8 +641,29 @@ class LoginPanel extends PSRoomPanel {
|
|||
</p>
|
||||
</div>}
|
||||
</form>
|
||||
</PSPanelWrapper>;
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
PS.addRoomType(UserPanel, VolumePanel, OptionsPanel, LoginPanel);
|
||||
class PopupPanel extends PSRoomPanel {
|
||||
static readonly id = 'popup';
|
||||
static readonly routes = ['popup-*'];
|
||||
static readonly location = 'semimodal-popup';
|
||||
static readonly noURL = true;
|
||||
|
||||
override focus() {
|
||||
this.base?.querySelector<HTMLButtonElement>('.autofocus')?.focus();
|
||||
}
|
||||
override render() {
|
||||
const room = this.props.room;
|
||||
const okButtonLabel = room.args?.okButtonLabel as string || 'OK';
|
||||
return <PSPanelWrapper room={room} width={480}><div class="pad">
|
||||
{room.args?.message && <p style="white-space:pre-wrap;word-wrap:break-word">
|
||||
{room.args.message}
|
||||
</p>}
|
||||
<p class="buttonbar"><button class="button autofocus" name="closeRoom"><strong>{okButtonLabel}</strong></button></p>
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
PS.addRoomType(UserPanel, VolumePanel, OptionsPanel, LoginPanel, PopupPanel);
|
||||
|
|
|
|||
|
|
@ -58,13 +58,16 @@ export class PSRouter {
|
|||
return url as RoomID;
|
||||
}
|
||||
updatePanelState(): { roomid: RoomID, changed: boolean } {
|
||||
const room = PS.room;
|
||||
let room = PS.room;
|
||||
// there's definitely a better way to do this but I'm lazy
|
||||
if (room.noURL) room = PS.rooms[PS.popups[PS.popups.length - 2]] || PS.panel;
|
||||
if (room.noURL) room = PS.panel;
|
||||
let roomid = room.id;
|
||||
const panelState = (PS.leftPanelWidth && PS.room === PS.panel ?
|
||||
const panelState = (PS.leftPanelWidth && room === PS.panel ?
|
||||
PS.leftPanel.id + '..' + PS.rightPanel!.id :
|
||||
PS.room.id);
|
||||
room.id);
|
||||
// don't generate history when focusing things on the home page
|
||||
if (roomid === 'news' && PS.room.location === 'mini-window') roomid = '' as RoomID;
|
||||
if (roomid === 'news' && room.location === 'mini-window') roomid = '' as RoomID;
|
||||
if (roomid === 'rooms') roomid = '' as RoomID;
|
||||
if (roomid === this.roomid && panelState === this.panelState) {
|
||||
return { roomid, changed: false };
|
||||
|
|
@ -158,8 +161,13 @@ export class PSRoomPanel<T extends PSRoom = PSRoom> extends preact.Component<{ r
|
|||
}
|
||||
}
|
||||
override componentDidUpdate() {
|
||||
if (this.base && ['popup', 'semimodal-popup'].includes(this.props.room.location)) {
|
||||
this.props.room.setDimensions(this.base.offsetWidth, this.base.offsetHeight);
|
||||
const room = this.props.room;
|
||||
if (this.base && ['popup', 'semimodal-popup'].includes(room.location)) {
|
||||
if (room.width && room.hiddenInit) {
|
||||
room.hiddenInit = false;
|
||||
this.focus();
|
||||
}
|
||||
room.setDimensions(this.base.offsetWidth, this.base.offsetHeight);
|
||||
}
|
||||
}
|
||||
override componentWillUnmount() {
|
||||
|
|
@ -250,7 +258,7 @@ export class PSMain extends preact.Component {
|
|||
id: roomid,
|
||||
parentElem: elem,
|
||||
rightPopup: elem.className === 'userbutton username',
|
||||
username: name,
|
||||
args: { username: name },
|
||||
});
|
||||
PS.update();
|
||||
e.preventDefault();
|
||||
|
|
@ -351,7 +359,7 @@ export class PSMain extends preact.Component {
|
|||
} else if (e.keyCode === 191 && !isTextInput && PS.room === PS.mainmenu) { // forward slash
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
PS.join('pm---' as RoomID);
|
||||
PS.join('dm---' as RoomID);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -457,9 +465,10 @@ export class PSMain extends preact.Component {
|
|||
}
|
||||
static getPopupStyle(room: PSRoom, width?: number | 'auto'): any {
|
||||
if (room.location === 'modal-popup' || !room.parentElem) {
|
||||
return { width: width || 480 };
|
||||
return { maxWidth: width || 480 };
|
||||
}
|
||||
if (!room.width || !room.height) {
|
||||
room.hiddenInit = true;
|
||||
return {
|
||||
position: 'absolute',
|
||||
visibility: 'hidden',
|
||||
|
|
|
|||
|
|
@ -405,7 +405,6 @@ span.header-username:hover {
|
|||
background: #E1E8E8;
|
||||
color: black;
|
||||
border: 1px solid #999999;
|
||||
padding: 2px 10px;
|
||||
|
||||
border-radius: 6px;
|
||||
box-shadow: inset 1px 1px 0 #ffffff, inset -1px -1px 0 #DDDDDD, 2px 2px 3px rgba(0,0,0,.2);
|
||||
|
|
@ -665,6 +664,7 @@ p.or:after {
|
|||
margin: 0 5px;
|
||||
background: #fcd2b3;
|
||||
border: 1px solid #f57b21;
|
||||
border-radius: 5px;
|
||||
color: #682f05;
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user