mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
Preact: Add battle options and timer (#2395)
Some checks failed
Node.js CI / build (22.x) (push) Has been cancelled
Some checks failed
Node.js CI / build (22.x) (push) Has been cancelled
Co-authored-by: Guangcong Luo <guangcongluo@gmail.com>
This commit is contained in:
parent
68d04c131a
commit
b26709a0a0
|
|
@ -159,7 +159,12 @@ export class BattleLog {
|
|||
let rank = name.charAt(0);
|
||||
if (battle?.ignoreSpects && ' +'.includes(rank)) return;
|
||||
if (battle?.ignoreOpponent) {
|
||||
if ('\u2605\u2606'.includes(rank) && toUserid(name) !== app.user.get('userid')) return;
|
||||
if (
|
||||
'\u2605\u2606'.includes(rank) &&
|
||||
toUserid(name) !== (window.app?.user?.get('userid') || window.PS?.user?.userid)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const ignoreList = window.app?.ignore || window.PS?.prefs?.ignore;
|
||||
if (ignoreList?.[toUserid(name)] && ' +^\u2605\u2606'.includes(rank)) return;
|
||||
|
|
|
|||
|
|
@ -3507,7 +3507,7 @@ export class Battle {
|
|||
return;
|
||||
} else if (args[1].endsWith(' seconds left.')) {
|
||||
let hasIndex = args[1].indexOf(' has ');
|
||||
let userid = window.app?.user?.get('userid');
|
||||
let userid = window.app?.user?.get('userid') || window.PS?.user.userid;
|
||||
if (toID(args[1].slice(0, hasIndex)) === userid) {
|
||||
this.kickingInactive = parseInt(args[1].slice(hasIndex + 5), 10) || true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,13 @@ class PSPrefs extends PSStreamModel<string | null> {
|
|||
hideinterstice: true,
|
||||
};
|
||||
|
||||
/* Battle preferences */
|
||||
ignorenicks: boolean | null = null;
|
||||
ignorespects: boolean | null = null;
|
||||
ignoreopp: boolean | null = null;
|
||||
autotimer: boolean | null = null;
|
||||
rightpanelbattles: boolean | null = null;
|
||||
|
||||
/**
|
||||
* Show "User joined" and "User left" messages. serverid:roomid
|
||||
* table. Uses 1 and 0 instead of true/false for JSON packing
|
||||
|
|
@ -895,6 +902,13 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
|||
this.send(target);
|
||||
PS.leave(this.id);
|
||||
},
|
||||
'inopener,inparent'(target) {
|
||||
// do this command in the popup opener
|
||||
let room = this.getParent();
|
||||
if (room && PS.isPopup(room)) room = room.getParent();
|
||||
// will crash if the parent doesn't exist, which is fine
|
||||
room!.send(target);
|
||||
},
|
||||
'maximize'(target) {
|
||||
const roomid = /[^a-z0-9-]/.test(target) ? toID(target) as any as RoomID : target as RoomID;
|
||||
const targetRoom = roomid ? PS.rooms[roomid] : this;
|
||||
|
|
@ -1561,6 +1575,10 @@ export const PS = new class extends PSModel {
|
|||
}
|
||||
getRoom(elem: HTMLElement | EventTarget | null | undefined, skipClickable?: boolean): PSRoom | null {
|
||||
let curElem: HTMLElement | null = elem as HTMLElement;
|
||||
// might be the close button on the roomtab
|
||||
if ((curElem as HTMLButtonElement)?.name === 'closeRoom' && (curElem as HTMLButtonElement).value) {
|
||||
return PS.rooms[(curElem as HTMLButtonElement).value] || null;
|
||||
}
|
||||
while (curElem) {
|
||||
if (curElem.id.startsWith('room-')) {
|
||||
return PS.rooms[curElem.id.slice(5)] || null;
|
||||
|
|
@ -1941,7 +1959,7 @@ export const PS = new class extends PSModel {
|
|||
options.args = { initialSlash: true };
|
||||
}
|
||||
}
|
||||
|
||||
if (options.id.startsWith('battle-') && PS.prefs.rightpanelbattles) options.location = 'right';
|
||||
options.parentRoomid ??= this.getRoom(options.parentElem)?.id;
|
||||
let preexistingRoom = this.rooms[options.id];
|
||||
if (preexistingRoom && this.isPopup(preexistingRoom)) {
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ export class BattleRoom extends ChatRoom {
|
|||
side: BattleRequestSideInfo | null = null;
|
||||
request: BattleRequest | null = null;
|
||||
choices: BattleChoiceBuilder | null = null;
|
||||
autoTimerActivated: boolean | null = null;
|
||||
}
|
||||
|
||||
class BattleDiv extends preact.Component<{ room: BattleRoom }> {
|
||||
|
|
@ -196,6 +197,64 @@ function PokemonButton(props: {
|
|||
</button>;
|
||||
}
|
||||
|
||||
class TimerButton extends preact.Component<{ room: BattleRoom }> {
|
||||
timerInterval: number | null = null;
|
||||
override componentWillUnmount() {
|
||||
if (this.timerInterval) {
|
||||
clearInterval(this.timerInterval);
|
||||
this.timerInterval = null;
|
||||
}
|
||||
}
|
||||
secondsToTime(seconds: number | true) {
|
||||
if (seconds === true) return '-:--';
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
seconds -= minutes * 60;
|
||||
return `${minutes}:${(seconds < 10 ? '0' : '')}${seconds}`;
|
||||
}
|
||||
render() {
|
||||
let time = 'Timer';
|
||||
const room = this.props.room;
|
||||
if (!this.timerInterval && room.battle.kickingInactive) {
|
||||
this.timerInterval = setInterval(() => {
|
||||
if (typeof room.battle.kickingInactive === 'number' && room.battle.kickingInactive > 1) {
|
||||
room.battle.kickingInactive--;
|
||||
if (room.battle.graceTimeLeft) room.battle.graceTimeLeft--;
|
||||
else if (room.battle.totalTimeLeft) room.battle.totalTimeLeft--;
|
||||
}
|
||||
this.forceUpdate();
|
||||
}, 1000);
|
||||
} else if (this.timerInterval && !room.battle.kickingInactive) {
|
||||
clearInterval(this.timerInterval);
|
||||
this.timerInterval = null;
|
||||
}
|
||||
|
||||
let timerTicking = (room.battle.kickingInactive &&
|
||||
room.request && room.request.requestType !== "wait" && (room.choices && !room.choices.isDone())) ?
|
||||
' timerbutton-on' : '';
|
||||
|
||||
if (room.battle.kickingInactive) {
|
||||
const secondsLeft = room.battle.kickingInactive;
|
||||
time = this.secondsToTime(secondsLeft);
|
||||
if (secondsLeft !== true) {
|
||||
if (secondsLeft <= 10 && timerTicking) {
|
||||
timerTicking = ' timerbutton-critical';
|
||||
}
|
||||
|
||||
if (room.battle.totalTimeLeft) {
|
||||
const totalTime = this.secondsToTime(room.battle.totalTimeLeft);
|
||||
time += ` | ${totalTime} total`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return <button
|
||||
style={{ position: "absolute", right: '10px' }} data-href="battletimer" class={`button${timerTicking}`} role="timer"
|
||||
>
|
||||
<i class="fa fa-hourglass-start" aria-hidden></i> {time}
|
||||
</button>;
|
||||
}
|
||||
};
|
||||
|
||||
class BattlePanel extends PSRoomPanel<BattleRoom> {
|
||||
static readonly id = 'battle';
|
||||
static readonly routes = ['battle-*'];
|
||||
|
|
@ -316,6 +375,11 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (PS.prefs.autotimer && !room.battle.kickingInactive && !room.autoTimerActivated) {
|
||||
this.send('/timer on');
|
||||
room.autoTimerActivated = true;
|
||||
}
|
||||
|
||||
BattleChoiceBuilder.fixRequest(request, room.battle);
|
||||
|
||||
if (request.side) {
|
||||
|
|
@ -569,9 +633,6 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
if (choices.isDone()) {
|
||||
return <div class="controls">
|
||||
<div class="whatdo">
|
||||
<button name="openTimer" class="button disabled timerbutton">
|
||||
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
|
||||
</button>
|
||||
{this.renderOldChoices(request, choices)}
|
||||
</div>
|
||||
<div class="pad">
|
||||
|
|
@ -599,9 +660,6 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
const moveName = choices.getChosenMove(choices.current, choices.index()).name;
|
||||
return <div class="controls">
|
||||
<div class="whatdo">
|
||||
<button name="openTimer" class="button disabled timerbutton">
|
||||
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
|
||||
</button>
|
||||
{this.renderOldChoices(request, choices)}
|
||||
{pokemon.name} should use <strong>{moveName}</strong> at where? {}
|
||||
</div>
|
||||
|
|
@ -617,9 +675,6 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
|
||||
return <div class="controls">
|
||||
<div class="whatdo">
|
||||
<button name="openTimer" class="button disabled timerbutton">
|
||||
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
|
||||
</button>
|
||||
{this.renderOldChoices(request, choices)}
|
||||
What will <strong>{pokemon.name}</strong> do?
|
||||
</div>
|
||||
|
|
@ -674,9 +729,6 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
const pokemon = request.side.pokemon[choices.index()];
|
||||
return <div class="controls">
|
||||
<div class="whatdo">
|
||||
<button name="openTimer" class="button disabled timerbutton">
|
||||
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
|
||||
</button>
|
||||
{this.renderOldChoices(request, choices)}
|
||||
What will <strong>{pokemon.name}</strong> do?
|
||||
</div>
|
||||
|
|
@ -690,9 +742,6 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
} case 'team': {
|
||||
return <div class="controls">
|
||||
<div class="whatdo">
|
||||
<button name="openTimer" class="button disabled timerbutton">
|
||||
<i class="fa fa-hourglass-start" aria-hidden></i> Timer
|
||||
</button>
|
||||
{choices.alreadySwitchingIn.length > 0 ? (
|
||||
[<button data-cmd="/cancel" class="button"><i class="fa fa-chevron-left" aria-hidden></i> Back</button>,
|
||||
" What about the rest of your team? "]
|
||||
|
|
@ -765,7 +814,6 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
</p>
|
||||
)}
|
||||
</div>;
|
||||
|
||||
}
|
||||
|
||||
handleDownloadReplay = (e: MouseEvent) => {
|
||||
|
|
@ -787,9 +835,14 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
override render() {
|
||||
const room = this.props.room;
|
||||
this.updateLayout();
|
||||
const id = `room-${room.id}`;
|
||||
const hardcoreStyle = room.battle?.hardcoreMode ? <style
|
||||
dangerouslySetInnerHTML={{ __html: `#${id} .battle .turn, #${id} .battle-history { display: none !important; }` }}
|
||||
></style> : null;
|
||||
|
||||
if (room.width < 700) {
|
||||
return <PSPanelWrapper room={room} focusClick scrollable="hidden">
|
||||
{hardcoreStyle}
|
||||
<BattleDiv room={room} />
|
||||
<ChatLog
|
||||
class="battle-log hasuserlist" room={room} top={this.battleHeight} noSubscription
|
||||
|
|
@ -800,11 +853,20 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
</ChatLog>
|
||||
<ChatTextEntry room={room} onMessage={this.send} onKey={this.onKey} left={0} />
|
||||
<ChatUserList room={room} top={this.battleHeight} minimized />
|
||||
<button
|
||||
data-href="battleoptions" class="button"
|
||||
style={{ position: 'absolute', right: '75px', top: this.battleHeight }}
|
||||
>
|
||||
Battle Options
|
||||
</button>
|
||||
{(room.battle && !room.battle.ended && room.request && room.battle.mySide.id === PS.user.userid) &&
|
||||
<TimerButton room={room} />}
|
||||
<div class="battle-controls-container"></div>
|
||||
</PSPanelWrapper>;
|
||||
}
|
||||
|
||||
return <PSPanelWrapper room={room} focusClick scrollable="hidden">
|
||||
{hardcoreStyle}
|
||||
<BattleDiv room={room} />
|
||||
<ChatLog
|
||||
class="battle-log hasuserlist" room={room} left={640} noSubscription
|
||||
|
|
@ -813,8 +875,16 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
</ChatLog>
|
||||
<ChatTextEntry room={room} onMessage={this.send} onKey={this.onKey} left={640} />
|
||||
<ChatUserList room={room} left={640} minimized />
|
||||
<button
|
||||
data-href="battleoptions" class="button"
|
||||
style={{ position: 'absolute', right: '15px' }}
|
||||
>
|
||||
Battle Options
|
||||
</button>
|
||||
<div class="battle-controls-container">
|
||||
<div class="battle-controls" role="complementary" aria-label="Battle Controls" style="top: 370px;">
|
||||
{(room.battle && !room.battle.ended && room.request && room.battle.mySide.id === PS.user.userid) &&
|
||||
<TimerButton room={room} />}
|
||||
{this.renderControls()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -503,7 +503,7 @@ class MainMenuPanel extends PSRoomPanel<MainMenuRoom> {
|
|||
<h3
|
||||
class={`mini-window-header${notifying}`} draggable onDragStart={this.handleDragStart} onClick={this.handleClickMinimize}
|
||||
>
|
||||
<button class="closebutton" data-cmd="/close" aria-label="Close" tabIndex={-1}>
|
||||
<button class="closebutton" name="closeRoom" value={roomid} aria-label="Close" tabIndex={-1}>
|
||||
<i class="fa fa-times-circle" aria-hidden></i>
|
||||
</button>
|
||||
<button class="maximizebutton" data-cmd="/maximize" tabIndex={-1} aria-label="Maximize">
|
||||
|
|
|
|||
|
|
@ -946,40 +946,22 @@ class BattleForfeitPanel extends PSRoomPanel {
|
|||
static readonly location = 'semimodal-popup';
|
||||
static readonly noURL = true;
|
||||
|
||||
handleForfeit = (ev: Event) => {
|
||||
const elem = this.props.room.parentElem;
|
||||
const roomid = (elem as HTMLInputElement)?.value as RoomID || PS.getRoom(elem)?.id || '' as RoomID;
|
||||
const room = PS.rooms[roomid] as BattleRoom;
|
||||
|
||||
const closeAfter = this.base!.querySelector<HTMLInputElement>('input[name=closeroom]')?.checked;
|
||||
room.send("/forfeit");
|
||||
if (closeAfter) PS.leave(room.id);
|
||||
ev.preventDefault();
|
||||
this.close();
|
||||
};
|
||||
|
||||
override render() {
|
||||
const room = this.props.room;
|
||||
const battleRoom = room.getParent() as BattleRoom;
|
||||
|
||||
return <PSPanelWrapper room={room} width={480}><div class="pad">
|
||||
<form onSubmit={this.handleForfeit}>
|
||||
<p>Forfeiting makes you lose the battle. Are you sure?</p>
|
||||
<p>
|
||||
<label class="checkbox"><input
|
||||
type="checkbox" name="closeroom" checked={true}
|
||||
/> Close after forfeiting</label>
|
||||
</p>
|
||||
<p>
|
||||
<button type="submit" class="button"><strong>Forfeit</strong></button> {}
|
||||
{!battleRoom.battle.rated && <button type="button" data-href="replaceplayer" class="button">
|
||||
Replace player
|
||||
</button>} {}
|
||||
<button type="button" data-cmd="/close" class="button">
|
||||
Cancel
|
||||
</button>
|
||||
</p>
|
||||
</form>
|
||||
<p>Forfeiting makes you lose the battle. Are you sure?</p>
|
||||
<p>
|
||||
<button data-cmd="/closeand /inopener /closeand /forfeit" class="button"><strong>Forfeit and close</strong></button> {}
|
||||
<button data-cmd="/closeand /inopener /forfeit" class="button">Just forfeit</button> {}
|
||||
{!battleRoom.battle.rated && <button type="button" data-href="replaceplayer" class="button">
|
||||
Replace player
|
||||
</button>} {}
|
||||
<button type="button" data-cmd="/close" class="button">
|
||||
Cancel
|
||||
</button>
|
||||
</p>
|
||||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1400,6 +1382,181 @@ class LeaveRoomPanel extends PSRoomPanel {
|
|||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
class BattleOptionsPanel extends PSRoomPanel {
|
||||
static readonly id = 'battleoptions';
|
||||
static readonly routes = ['battleoptions'];
|
||||
static readonly location = 'semimodal-popup';
|
||||
static readonly noURL = true;
|
||||
|
||||
handleHardcoreMode = (ev: Event) => {
|
||||
const mode = (ev.currentTarget as HTMLInputElement).checked;
|
||||
const room = this.props.room.getParent() as BattleRoom;
|
||||
if (!room?.battle) return this.close();
|
||||
room.battle.setHardcoreMode(mode);
|
||||
if (mode) {
|
||||
room.add(`||Hardcore mode ON: Information not available in-game is now hidden.`);
|
||||
} else {
|
||||
room.add(`||Hardcore mode OFF: Information not available in-game is now shown.`);
|
||||
}
|
||||
room.update(null);
|
||||
};
|
||||
handleIgnoreSpectators = (ev: Event | boolean) => {
|
||||
const value = typeof ev === "object" ?
|
||||
(ev.currentTarget as HTMLInputElement).checked :
|
||||
ev;
|
||||
const room = this.props.room.getParent() as BattleRoom;
|
||||
if (!room?.battle) return this.close();
|
||||
room.battle.ignoreSpects = value;
|
||||
room.add(`||Spectators ${room.battle.ignoreSpects ? '' : 'no longer '}ignored.`);
|
||||
const chats = document.querySelectorAll<HTMLElement>('.battle-log .chat');
|
||||
const displaySetting = room.battle.ignoreSpects ? 'none' : '';
|
||||
for (const chat of chats) {
|
||||
const small = chat.querySelector('small');
|
||||
if (!small) continue;
|
||||
const text = small.innerText;
|
||||
const isPlayerChat = text.includes('\u2606') || text.includes('\u2605');
|
||||
if (!isPlayerChat) {
|
||||
chat.style.display = displaySetting;
|
||||
}
|
||||
}
|
||||
room.battle.scene.log.updateScroll();
|
||||
};
|
||||
handleIgnoreOpponent = (ev: Event | boolean) => {
|
||||
const value = typeof ev === "object" ?
|
||||
(ev.currentTarget as HTMLInputElement).checked :
|
||||
ev;
|
||||
const room = this.props.room.getParent() as BattleRoom;
|
||||
if (!room?.battle) return this.close();
|
||||
room.battle.ignoreOpponent = value;
|
||||
room.battle.resetToCurrentTurn();
|
||||
};
|
||||
handleIgnoreNicks = (ev: Event | boolean) => {
|
||||
const value = typeof ev === "object" ?
|
||||
(ev.currentTarget as HTMLInputElement).checked :
|
||||
ev;
|
||||
const room = this.props.room.getParent() as BattleRoom;
|
||||
if (!room?.battle) return this.close();
|
||||
room.battle.ignoreNicks = value;
|
||||
room.battle.resetToCurrentTurn();
|
||||
};
|
||||
handleAllSettings = (ev: Event) => {
|
||||
const setting = (ev.currentTarget as HTMLInputElement).name;
|
||||
const value = (ev.currentTarget as HTMLInputElement).checked;
|
||||
const room = this.props.room.getParent() as BattleRoom;
|
||||
if (!room?.battle) return this.close();
|
||||
switch (setting) {
|
||||
case 'autotimer': {
|
||||
PS.prefs.set('autotimer', value);
|
||||
if (value) {
|
||||
room.send('/timer on');
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ignoreopp': {
|
||||
PS.prefs.set('ignoreopp', value);
|
||||
this.handleIgnoreOpponent(value);
|
||||
break;
|
||||
}
|
||||
case 'ignorespects': {
|
||||
PS.prefs.set('ignorespects', value);
|
||||
this.handleIgnoreSpectators(value);
|
||||
break;
|
||||
}
|
||||
case 'ignorenicks': {
|
||||
PS.prefs.set('ignorenicks', value);
|
||||
this.handleIgnoreNicks(value);
|
||||
break;
|
||||
}
|
||||
case 'rightpanel': {
|
||||
PS.prefs.set('rightpanelbattles', value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
override render() {
|
||||
const room = this.props.room;
|
||||
const battleRoom = this.props.room.getParent() as BattleRoom;
|
||||
|
||||
return <PSPanelWrapper room={room} width={380}><div class="pad">
|
||||
<p><strong>In this battle</strong></p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
checked={battleRoom?.battle?.hardcoreMode}
|
||||
type="checkbox" onChange={this.handleHardcoreMode}
|
||||
/> Hardcore mode (hide info not shown in-game)
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
checked={battleRoom?.battle?.ignoreSpects}
|
||||
type="checkbox" onChange={this.handleIgnoreSpectators}
|
||||
/> Ignore spectators
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
checked={battleRoom?.battle?.ignoreOpponent}
|
||||
type="checkbox" onChange={this.handleIgnoreOpponent}
|
||||
/> Ignore opponent
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
checked={battleRoom?.battle?.ignoreNicks}
|
||||
type="checkbox" onChange={this.handleIgnoreNicks}
|
||||
/> Ignore nicknames
|
||||
</label>
|
||||
</p>
|
||||
<p><strong>All battles</strong></p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
name="ignorenicks" checked={PS.prefs.ignorenicks || false}
|
||||
type="checkbox" onChange={this.handleAllSettings}
|
||||
/> Ignore nicknames
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
name="ignorespects" checked={PS.prefs.ignorespects || false}
|
||||
type="checkbox" onChange={this.handleAllSettings}
|
||||
/> Ignore spectators
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
name="ignoreopp" checked={PS.prefs.ignoreopp || false}
|
||||
type="checkbox" onChange={this.handleAllSettings}
|
||||
/> Ignore opponent
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
name="autotimer" checked={PS.prefs.autotimer || false}
|
||||
type="checkbox" onChange={this.handleAllSettings}
|
||||
/> Automatically start timer
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label class="checkbox">
|
||||
<input
|
||||
name="rightpanel" checked={PS.prefs.rightpanelbattles || false}
|
||||
type="checkbox" onChange={this.handleAllSettings}
|
||||
/> Open new battles on the right side
|
||||
</label>
|
||||
</p>
|
||||
<p><button data-cmd="/close" class="button">Done</button></p>
|
||||
</div>
|
||||
</PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
class PopupRoom extends PSRoom {
|
||||
returnValue: unknown = this.args?.cancelValue;
|
||||
|
|
@ -1487,6 +1644,24 @@ class RoomTabListPanel extends PSRoomPanel {
|
|||
</div></PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
class BattleTimerPanel extends PSRoomPanel {
|
||||
static readonly id = 'battletimer';
|
||||
static readonly routes = ['battletimer'];
|
||||
static readonly location = 'semimodal-popup';
|
||||
static readonly noURL = true;
|
||||
|
||||
override render() {
|
||||
const room = this.props.room.getParent() as BattleRoom;
|
||||
return <PSPanelWrapper room={this.props.room}><div class="pad">
|
||||
{room.battle.kickingInactive ? (
|
||||
<button class="button" data-cmd="/closeand /inopener /timer stop">Stop Timer</button>
|
||||
) : (
|
||||
<button class="button" data-cmd="/closeand /inopener /timer start">Start Timer</button>
|
||||
)}
|
||||
</div>
|
||||
</PSPanelWrapper>;
|
||||
}
|
||||
}
|
||||
|
||||
PS.addRoomType(
|
||||
UserPanel,
|
||||
|
|
@ -1504,5 +1679,7 @@ PS.addRoomType(
|
|||
LeaveRoomPanel,
|
||||
ChatFormattingPanel,
|
||||
PopupPanel,
|
||||
RoomTabListPanel
|
||||
RoomTabListPanel,
|
||||
BattleOptionsPanel,
|
||||
BattleTimerPanel
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user