mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -05:00
Preact minor updates batch 20
Some checks are pending
Node.js CI / build (22.x) (push) Waiting to run
Some checks are pending
Node.js CI / build (22.x) (push) Waiting to run
Battles - Fix render issues on mobile layout - Fix join/leave batching - Support disabled buttons Teambuilder - Redesign upload UI to be clearer - Support scrolling tab bar for Boxes in focus editor - Don't overwrite local team with remote team - Improve Defensive Coverage ability support - Support importing from pokepaste (fixes #2422)
This commit is contained in:
parent
924400bba7
commit
a35a62cfce
|
|
@ -141,7 +141,7 @@ export class BattleLog {
|
|||
let divClass = 'chat';
|
||||
let divHTML = '';
|
||||
let noNotify: boolean | undefined;
|
||||
if (!['join', 'j', 'leave', 'l'].includes(args[0])) this.joinLeave = null;
|
||||
if (!['join', 'j', 'leave', 'l', 'turn'].includes(args[0])) this.joinLeave = null;
|
||||
if (!['name', 'n'].includes(args[0])) this.lastRename = null;
|
||||
switch (args[0]) {
|
||||
case 'chat': case 'c': case 'c:':
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { PSSearchResults } from "./battle-searchresults";
|
|||
import { BattleNatures, BattleStatNames, type StatName } from "./battle-dex-data";
|
||||
import { BattleStatGuesser, BattleStatOptimizer } from "./battle-tooltips";
|
||||
import { PSModel } from "./client-core";
|
||||
import { Net } from "./client-connection";
|
||||
|
||||
type SelectionType = 'pokemon' | 'ability' | 'item' | 'move' | 'stats' | 'details';
|
||||
|
||||
|
|
@ -49,6 +50,10 @@ class TeamEditorState extends PSModel {
|
|||
this.setFormat(team.format);
|
||||
window.search = this.search;
|
||||
}
|
||||
setReadonly(readonly: boolean) {
|
||||
if (!readonly && this.readonly) this.sets = PSTeambuilder.unpackTeam(this.team.packedTeam);
|
||||
this.readonly = readonly;
|
||||
}
|
||||
setFormat(format: string) {
|
||||
const team = this.team;
|
||||
const formatid = toID(format);
|
||||
|
|
@ -499,6 +504,9 @@ class TeamEditorState extends PSModel {
|
|||
if (attackType === 'Ground' && abilityid === 'eartheater') return 0;
|
||||
if (attackType === 'Fire' && abilityid === 'wellbakedbody') return 0;
|
||||
|
||||
if (attackType === 'Fire' && abilityid === 'primordialsea') return 0;
|
||||
if (attackType === 'Water' && abilityid === 'desolateland') return 0;
|
||||
|
||||
if (abilityid === 'wonderguard') {
|
||||
for (const type of types) {
|
||||
if (this.getTypeWeakness(type, attackType) <= 1) return 0;
|
||||
|
|
@ -506,6 +514,14 @@ class TeamEditorState extends PSModel {
|
|||
}
|
||||
|
||||
let factor = 1;
|
||||
if ((attackType === 'Fire' || attackType === 'Ice') && abilityid === 'thickfat') factor *= 0.5;
|
||||
if (attackType === 'Fire' && abilityid === 'waterbubble') factor *= 0.5;
|
||||
if (attackType === 'Fire' && abilityid === 'heatproof') factor *= 0.5;
|
||||
if (attackType === 'Ghost' && abilityid === 'purifyingsalt') factor *= 0.5;
|
||||
if (attackType === 'Fire' && abilityid === 'fluffy') factor *= 2;
|
||||
if ((attackType === 'Electric' || attackType === 'Rock' || attackType === 'Ice') && abilityid === 'deltastream') {
|
||||
factor *= 0.5;
|
||||
}
|
||||
for (const type of types) {
|
||||
factor *= this.getTypeWeakness(type, attackType);
|
||||
}
|
||||
|
|
@ -650,7 +666,7 @@ export class TeamEditor extends preact.Component<{
|
|||
}
|
||||
override render() {
|
||||
this.editor ||= new TeamEditorState(this.props.team);
|
||||
this.editor.readonly = !!this.props.readonly;
|
||||
this.editor.setReadonly(!!this.props.readonly);
|
||||
this.editor.narrow = this.props.narrow ?? document.body.offsetWidth < 500;
|
||||
if (this.props.team.format !== this.editor.format) {
|
||||
this.editor.setFormat(this.props.team.format);
|
||||
|
|
@ -714,7 +730,10 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?:
|
|||
this.heightTester.value = fullLine && !newValue.endsWith('\n') ? newValue + '\n' : newValue;
|
||||
return this.heightTester.scrollHeight;
|
||||
}
|
||||
input = () => this.updateText();
|
||||
input = () => {
|
||||
this.updateText();
|
||||
this.save();
|
||||
};
|
||||
keyUp = () => this.updateText(true);
|
||||
contextMenu = (ev: MouseEvent) => {
|
||||
if (!ev.shiftKey) {
|
||||
|
|
@ -786,13 +805,15 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?:
|
|||
if (ev.keyCode === 13 && ev.shiftKey) return;
|
||||
if (ev.altKey || ev.metaKey) return;
|
||||
if (!this.innerFocus) {
|
||||
if (
|
||||
if (this.maybeReplaceLine()) {
|
||||
// do nothing else
|
||||
} else if (
|
||||
this.textbox.selectionStart === this.textbox.value.length &&
|
||||
(this.textbox.value.endsWith('\n\n') || !this.textbox.value)
|
||||
) {
|
||||
this.addPokemon();
|
||||
} else {
|
||||
this.openInnerFocus();
|
||||
} else if (!this.openInnerFocus()) {
|
||||
break;
|
||||
}
|
||||
ev.stopImmediatePropagation();
|
||||
ev.preventDefault();
|
||||
|
|
@ -820,6 +841,24 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?:
|
|||
}
|
||||
}
|
||||
};
|
||||
maybeReplaceLine = () => {
|
||||
if (this.textbox.selectionStart !== this.textbox.selectionEnd) return;
|
||||
const current = this.textbox.selectionEnd;
|
||||
const lineStart = this.textbox.value.lastIndexOf('\n', current) + 1;
|
||||
const value = this.textbox.value.slice(lineStart, current);
|
||||
|
||||
const pokepaste = /^https?:\/\/pokepast.es\/([a-z0-9]+)(?:\/.*)?$/.exec(value)?.[1];
|
||||
if (pokepaste) {
|
||||
Net(`https://pokepast.es/${pokepaste}/json`).get().then(json => {
|
||||
const paste = JSON.parse(json);
|
||||
// make sure it's still there:
|
||||
const valueIndex = this.textbox.value.indexOf(value);
|
||||
this.replace(paste.paste.replace(/\r\n/g, '\n'), valueIndex, valueIndex + value.length);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
getInnerFocusValue() {
|
||||
if (!this.innerFocus) return '';
|
||||
return this.textbox.value.slice(this.innerFocus.range[0], this.innerFocus.range[1]);
|
||||
|
|
@ -840,6 +879,7 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?:
|
|||
this.clearInnerFocus();
|
||||
if (this.setDirty) {
|
||||
this.updateText();
|
||||
this.save();
|
||||
} else {
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
|
@ -986,7 +1026,6 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?:
|
|||
}
|
||||
|
||||
textbox.style.height = `${bottomY + 100}px`;
|
||||
this.save();
|
||||
}
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
|
@ -1180,6 +1219,7 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?:
|
|||
// for future updates
|
||||
if (!this.setInfo[index]) {
|
||||
this.updateText();
|
||||
this.save();
|
||||
} else {
|
||||
if (this.setInfo[index + 1]) {
|
||||
this.setInfo[index + 1].index = start + newText.length;
|
||||
|
|
@ -1352,7 +1392,7 @@ class TeamTextbox extends preact.Component<{ editor: TeamEditorState, onChange?:
|
|||
<textarea
|
||||
class="textbox teamtextbox" style={`padding-left:${editor.narrow ? '50px' : '100px'}`}
|
||||
onInput={this.input} onContextMenu={this.contextMenu} onKeyUp={this.keyUp} onKeyDown={this.keyDown}
|
||||
readOnly={editor.readonly}
|
||||
readOnly={editor.readonly} onChange={this.maybeReplaceLine}
|
||||
/>
|
||||
<textarea
|
||||
class="textbox teamtextbox heighttester" tabIndex={-1} aria-hidden
|
||||
|
|
@ -1876,7 +1916,7 @@ class TeamWizard extends preact.Component<{
|
|||
<i class="fa fa-plus"></i>
|
||||
</button></li>}
|
||||
</ul>
|
||||
<div class="pad">{this.renderButton(set, setIndex)}</div>
|
||||
<div class="pad" style="padding-top:0">{this.renderButton(set, setIndex)}</div>
|
||||
{type === 'stats' ? (
|
||||
<StatForm editor={editor} set={set!} onChange={this.handleSetChange} />
|
||||
) : type === 'details' ? (
|
||||
|
|
|
|||
|
|
@ -284,6 +284,8 @@ export interface Team {
|
|||
/** password, if private. null = public, undefined = unknown, not loaded yet */
|
||||
private?: string | null,
|
||||
};
|
||||
/** team at the point it was last uploaded. outside of `uploaded` so it can track loading state */
|
||||
uploadedPackedTeam?: string;
|
||||
}
|
||||
interface UploadedTeam {
|
||||
name: string;
|
||||
|
|
@ -435,14 +437,9 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
|||
if (!team) {
|
||||
continue;
|
||||
}
|
||||
const compare = this.compareTeams(team, localTeam);
|
||||
if (compare !== true) {
|
||||
if (!localTeam.name.endsWith(' (local version)')) localTeam.name += ' (local version)';
|
||||
continue;
|
||||
}
|
||||
localTeam.uploaded = {
|
||||
teamid: team.teamid,
|
||||
notLoaded: true,
|
||||
notLoaded: false,
|
||||
private: team.private,
|
||||
};
|
||||
delete teams[localTeam.teamid];
|
||||
|
|
@ -465,7 +462,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
|||
localTeam.teamid = team.teamid;
|
||||
localTeam.uploaded = {
|
||||
teamid: team.teamid,
|
||||
notLoaded: true,
|
||||
notLoaded: false,
|
||||
private: team.private,
|
||||
};
|
||||
break;
|
||||
|
|
@ -495,10 +492,10 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
|||
loadTeam(team: Team | undefined | null, ifNeeded: true): void | Promise<void>;
|
||||
loadTeam(team: Team | undefined | null): Promise<void>;
|
||||
loadTeam(team: Team | undefined | null, ifNeeded?: boolean): void | Promise<void> {
|
||||
if (!team) return ifNeeded ? undefined : Promise.resolve();
|
||||
if (!team.uploaded?.notLoaded) return ifNeeded ? undefined : Promise.resolve();
|
||||
if (team.uploaded.notLoaded !== true) return team.uploaded.notLoaded;
|
||||
if (!team?.uploaded || team.uploadedPackedTeam) return ifNeeded ? undefined : Promise.resolve();
|
||||
if (team.uploaded.notLoaded && team.uploaded.notLoaded !== true) return team.uploaded.notLoaded;
|
||||
|
||||
const notLoaded = team.uploaded.notLoaded;
|
||||
return (team.uploaded.notLoaded = PSLoginServer.query('getteam', {
|
||||
teamid: team.uploaded.teamid,
|
||||
}).then(data => {
|
||||
|
|
@ -508,8 +505,11 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
|||
return;
|
||||
}
|
||||
team.uploaded.notLoaded = false;
|
||||
team.packedTeam = data.team;
|
||||
PS.teams.save();
|
||||
team.uploadedPackedTeam = data.team;
|
||||
if (notLoaded) {
|
||||
team.packedTeam = data.team;
|
||||
PS.teams.save();
|
||||
}
|
||||
}));
|
||||
}
|
||||
compareTeams(serverTeam: UploadedTeam, localTeam: Team) {
|
||||
|
|
|
|||
|
|
@ -152,55 +152,6 @@ class BattleDiv extends preact.Component<{ room: BattleRoom }> {
|
|||
}
|
||||
}
|
||||
|
||||
function MoveButton(props: {
|
||||
cmd: string, type: Dex.TypeName, tooltip: string, moveData: { pp?: number, maxpp?: number, disabled?: boolean },
|
||||
children: string,
|
||||
}) {
|
||||
const pp = props.moveData.maxpp ? `${props.moveData.pp!}/${props.moveData.maxpp}` : '–';
|
||||
return <button
|
||||
data-cmd={props.cmd} data-tooltip={props.tooltip}
|
||||
class={`movebutton type-${props.type} has-tooltip${props.moveData.disabled ? ' disabled' : ''}`}
|
||||
>
|
||||
{props.children}<br />
|
||||
<small class="type">{props.type}</small> <small class="pp">{pp}</small>
|
||||
</button>;
|
||||
}
|
||||
function PokemonButton(props: {
|
||||
pokemon: Pokemon | ServerPokemon | null, cmd: string, noHPBar?: boolean, disabled?: boolean | 'fade', tooltip: string,
|
||||
}) {
|
||||
const pokemon = props.pokemon;
|
||||
if (!pokemon) {
|
||||
return <button
|
||||
data-cmd={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
||||
style={{ opacity: props.disabled === 'fade' ? 0.5 : 1 }} data-tooltip={props.tooltip}
|
||||
>
|
||||
(empty slot)
|
||||
</button>;
|
||||
}
|
||||
|
||||
let hpColorClass;
|
||||
switch (BattleScene.getHPColor(pokemon)) {
|
||||
case 'y': hpColorClass = 'hpbar hpbar-yellow'; break;
|
||||
case 'r': hpColorClass = 'hpbar hpbar-red'; break;
|
||||
default: hpColorClass = 'hpbar'; break;
|
||||
}
|
||||
|
||||
return <button
|
||||
data-cmd={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
||||
style={{ opacity: props.disabled === 'fade' ? 0.5 : 1 }} data-tooltip={props.tooltip}
|
||||
>
|
||||
<PSIcon pokemon={pokemon} />
|
||||
{pokemon.name}
|
||||
{
|
||||
!props.noHPBar && !pokemon.fainted &&
|
||||
<span class={hpColorClass}>
|
||||
<span style={{ width: Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1 }}></span>
|
||||
</span>
|
||||
}
|
||||
{!props.noHPBar && pokemon.status && <span class={`status ${pokemon.status}`}></span>}
|
||||
</button>;
|
||||
}
|
||||
|
||||
class TimerButton extends preact.Component<{ room: BattleRoom }> {
|
||||
timerInterval: number | null = null;
|
||||
override componentWillUnmount() {
|
||||
|
|
@ -442,6 +393,57 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
</p>
|
||||
</div>;
|
||||
}
|
||||
renderMoveButton(props: {
|
||||
name: string,
|
||||
cmd: string, type: Dex.TypeName, tooltip: string, moveData: { pp?: number, maxpp?: number, disabled?: boolean },
|
||||
} | null) {
|
||||
if (!props) {
|
||||
return <button class="movebutton" disabled> </button>;
|
||||
}
|
||||
const pp = props.moveData.maxpp ? `${props.moveData.pp!}/${props.moveData.maxpp}` : '\u2014';
|
||||
return <button
|
||||
data-cmd={props.cmd} data-tooltip={props.tooltip}
|
||||
class={`movebutton type-${props.type} has-tooltip${props.moveData.disabled ? ' disabled' : ''}`}
|
||||
>
|
||||
{props.name}<br />
|
||||
<small class="type">{props.type}</small> <small class="pp">{pp}</small>
|
||||
</button>;
|
||||
}
|
||||
renderPokemonButton(props: {
|
||||
pokemon: Pokemon | ServerPokemon | null, cmd: string, noHPBar?: boolean, disabled?: boolean | 'fade', tooltip: string,
|
||||
}) {
|
||||
const pokemon = props.pokemon;
|
||||
if (!pokemon) {
|
||||
return <button
|
||||
data-cmd={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
||||
style={{ opacity: props.disabled === 'fade' ? 0.5 : 1 }} data-tooltip={props.tooltip}
|
||||
>
|
||||
(empty slot)
|
||||
</button>;
|
||||
}
|
||||
|
||||
let hpColorClass;
|
||||
switch (BattleScene.getHPColor(pokemon)) {
|
||||
case 'y': hpColorClass = 'hpbar hpbar-yellow'; break;
|
||||
case 'r': hpColorClass = 'hpbar hpbar-red'; break;
|
||||
default: hpColorClass = 'hpbar'; break;
|
||||
}
|
||||
|
||||
return <button
|
||||
data-cmd={props.cmd} class={`${props.disabled ? 'disabled ' : ''}has-tooltip`}
|
||||
style={{ opacity: props.disabled === 'fade' ? 0.5 : 1 }} data-tooltip={props.tooltip}
|
||||
>
|
||||
{PSIcon({ pokemon })}
|
||||
{pokemon.name}
|
||||
{
|
||||
!props.noHPBar && !pokemon.fainted &&
|
||||
<span class={hpColorClass}>
|
||||
<span style={{ width: Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1 }}></span>
|
||||
</span>
|
||||
}
|
||||
{!props.noHPBar && pokemon.status && <span class={`status ${pokemon.status}`}></span>}
|
||||
</button>;
|
||||
}
|
||||
renderMoveMenu(choices: BattleChoiceBuilder) {
|
||||
const moveRequest = choices.currentMoveRequest()!;
|
||||
|
||||
|
|
@ -521,9 +523,13 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
}
|
||||
const gmaxTooltip = maxMoveData.id.startsWith('gmax') ? `|${maxMoveData.id}` : ``;
|
||||
const tooltip = `maxmove|${moveData.name}|${pokemonIndex}${gmaxTooltip}`;
|
||||
return <MoveButton cmd={`/move ${i + 1} max`} type={moveType} tooltip={tooltip} moveData={moveData}>
|
||||
{maxMoveData.name}
|
||||
</MoveButton>;
|
||||
return this.renderMoveButton({
|
||||
name: maxMoveData.name,
|
||||
cmd: `/move ${i + 1} max`,
|
||||
type: moveType,
|
||||
tooltip,
|
||||
moveData,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -534,15 +540,19 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
return active.moves.map((moveData, i) => {
|
||||
const zMoveData = active.zMoves![i];
|
||||
if (!zMoveData) {
|
||||
return <button class="movebutton" disabled> </button>;
|
||||
return this.renderMoveButton(null);
|
||||
}
|
||||
const specialMove = dex.moves.get(zMoveData.name);
|
||||
const move = specialMove.exists ? specialMove : dex.moves.get(moveData.name);
|
||||
const moveType = tooltips.getMoveType(move, valueTracker)[0];
|
||||
const tooltip = `zmove|${moveData.name}|${pokemonIndex}`;
|
||||
return <MoveButton cmd={`/move ${i + 1} zmove`} type={moveType} tooltip={tooltip} moveData={{ pp: 1, maxpp: 1 }}>
|
||||
{zMoveData.name}
|
||||
</MoveButton>;
|
||||
return this.renderMoveButton({
|
||||
name: zMoveData.name,
|
||||
cmd: `/move ${i + 1} zmove`,
|
||||
type: moveType,
|
||||
tooltip,
|
||||
moveData: { pp: 1, maxpp: 1 },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -551,9 +561,13 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
const move = dex.moves.get(moveData.name);
|
||||
const moveType = tooltips.getMoveType(move, valueTracker)[0];
|
||||
const tooltip = `move|${moveData.name}|${pokemonIndex}`;
|
||||
return <MoveButton cmd={`/move ${i + 1}${special}`} type={moveType} tooltip={tooltip} moveData={moveData}>
|
||||
{move.name}
|
||||
</MoveButton>;
|
||||
return this.renderMoveButton({
|
||||
name: move.name,
|
||||
cmd: `/move ${i + 1}${special}`,
|
||||
type: moveType,
|
||||
tooltip,
|
||||
moveData,
|
||||
});
|
||||
});
|
||||
}
|
||||
renderMoveTargetControls(request: BattleMoveRequest, choices: BattleChoiceBuilder) {
|
||||
|
|
@ -577,10 +591,12 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
}
|
||||
|
||||
if (pokemon?.fainted) pokemon = null;
|
||||
return <PokemonButton
|
||||
pokemon={pokemon}
|
||||
cmd={disabled ? `` : `/${moveChoice} +${i + 1}`} disabled={disabled && 'fade'} tooltip={`activepokemon|1|${i}`}
|
||||
/>;
|
||||
return this.renderPokemonButton({
|
||||
pokemon,
|
||||
cmd: disabled ? `` : `/${moveChoice} +${i + 1}`,
|
||||
disabled: disabled && 'fade',
|
||||
tooltip: `activepokemon|1|${i}`,
|
||||
});
|
||||
}).reverse(),
|
||||
<div style="clear: left"></div>,
|
||||
battle.nearSide.active.map((pokemon, i) => {
|
||||
|
|
@ -593,10 +609,12 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
if (moveTarget !== 'adjacentAllyOrSelf' && userSlot === i) disabled = true;
|
||||
|
||||
if (pokemon?.fainted) pokemon = null;
|
||||
return <PokemonButton
|
||||
pokemon={pokemon}
|
||||
cmd={disabled ? `` : `/${moveChoice} -${i + 1}`} disabled={disabled && 'fade'} tooltip={`activepokemon|0|${i}`}
|
||||
/>;
|
||||
return this.renderPokemonButton({
|
||||
pokemon,
|
||||
cmd: disabled ? `` : `/${moveChoice} -${i + 1}`,
|
||||
disabled: disabled && 'fade',
|
||||
tooltip: `activepokemon|0|${i}`,
|
||||
});
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
|
@ -616,18 +634,24 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
</em>}
|
||||
{request.side.pokemon.map((serverPokemon, i) => {
|
||||
const cantSwitch = trapped || i < numActive || choices.alreadySwitchingIn.includes(i + 1) || serverPokemon.fainted;
|
||||
return <PokemonButton
|
||||
pokemon={serverPokemon} cmd={`/switch ${i + 1}`} disabled={cantSwitch} tooltip={`switchpokemon|${i}`}
|
||||
/>;
|
||||
return this.renderPokemonButton({
|
||||
pokemon: serverPokemon,
|
||||
cmd: `/switch ${i + 1}`,
|
||||
disabled: cantSwitch,
|
||||
tooltip: `switchpokemon|${i}`,
|
||||
});
|
||||
})}
|
||||
</div>;
|
||||
}
|
||||
renderTeamControls(request: | BattleTeamRequest, choices: BattleChoiceBuilder) {
|
||||
renderTeamPreviewChooser(request: | BattleTeamRequest, choices: BattleChoiceBuilder) {
|
||||
return request.side.pokemon.map((serverPokemon, i) => {
|
||||
const cantSwitch = choices.alreadySwitchingIn.includes(i + 1);
|
||||
return <PokemonButton
|
||||
pokemon={serverPokemon} cmd={`/switch ${i + 1}`} noHPBar disabled={cantSwitch && 'fade'} tooltip={`switchpokemon|${i}`}
|
||||
/>;
|
||||
return this.renderPokemonButton({
|
||||
pokemon: serverPokemon,
|
||||
cmd: `/switch ${i + 1}`,
|
||||
disabled: cantSwitch && 'fade',
|
||||
tooltip: `switchpokemon|${i}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
renderTeamList() {
|
||||
|
|
@ -637,9 +661,12 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
<h3 class="switchselect">Team</h3>
|
||||
<div class="switchmenu">
|
||||
{team.map((serverPokemon, i) => {
|
||||
return <PokemonButton
|
||||
pokemon={serverPokemon} cmd="" noHPBar disabled tooltip={`switchpokemon|${i}`}
|
||||
/>;
|
||||
return this.renderPokemonButton({
|
||||
pokemon: serverPokemon,
|
||||
cmd: "",
|
||||
disabled: true,
|
||||
tooltip: `switchpokemon|${i}`,
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
</div>;
|
||||
|
|
@ -647,9 +674,12 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
renderChosenTeam(request: BattleTeamRequest, choices: BattleChoiceBuilder) {
|
||||
return choices.alreadySwitchingIn.map(slot => {
|
||||
const serverPokemon = request.side.pokemon[slot - 1];
|
||||
return <PokemonButton
|
||||
pokemon={serverPokemon} cmd={`/switch ${slot}`} disabled tooltip={`switchpokemon|${slot - 1}`}
|
||||
/>;
|
||||
return this.renderPokemonButton({
|
||||
pokemon: serverPokemon,
|
||||
cmd: `/switch ${slot}`,
|
||||
disabled: true,
|
||||
tooltip: `switchpokemon|${slot - 1}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
renderOldChoices(request: BattleRequest, choices: BattleChoiceBuilder) {
|
||||
|
|
@ -821,7 +851,7 @@ class BattlePanel extends PSRoomPanel<BattleRoom> {
|
|||
Choose {choices.alreadySwitchingIn.length <= 0 ? `lead` : `slot ${choices.alreadySwitchingIn.length + 1}`}
|
||||
</h3>
|
||||
<div class="switchmenu">
|
||||
{this.renderTeamControls(request, choices)}
|
||||
{this.renderTeamPreviewChooser(request, choices)}
|
||||
<div style="clear:left"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1221,7 +1221,7 @@ export class ChatLog extends preact.Component<{
|
|||
if (controlsElem && controlsElem.className !== 'controls') controlsElem = undefined;
|
||||
if (!jsx) {
|
||||
if (!controlsElem) return;
|
||||
preact.render(null, elem, controlsElem);
|
||||
elem.removeChild(controlsElem);
|
||||
this.updateScroll();
|
||||
return;
|
||||
}
|
||||
|
|
@ -1230,7 +1230,9 @@ export class ChatLog extends preact.Component<{
|
|||
controlsElem.className = 'controls';
|
||||
elem.appendChild(controlsElem);
|
||||
}
|
||||
preact.render(<div class="controls">{jsx}</div>, elem, controlsElem);
|
||||
// for some reason, the replaceNode feature isn't working?
|
||||
if (controlsElem.children[0]) controlsElem.removeChild(controlsElem.children[0]);
|
||||
preact.render(<div>{jsx}</div>, controlsElem);
|
||||
this.updateScroll();
|
||||
}
|
||||
updateScroll() {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ class TeamRoom extends PSRoom {
|
|||
/** Doesn't _literally_ always exist, but does in basically all code
|
||||
* and constantly checking for its existence is legitimately annoying... */
|
||||
team!: Team;
|
||||
uploaded = false;
|
||||
override clientCommands = this.parseClientCommands({
|
||||
'validate'(target) {
|
||||
if (this.team.format.length <= 4) {
|
||||
|
|
@ -32,7 +31,6 @@ class TeamRoom extends PSRoom {
|
|||
this.team = team!;
|
||||
this.title = `[Team] ${this.team?.name || 'Error'}`;
|
||||
if (team) this.setFormat(team.format);
|
||||
this.uploaded = !!team?.uploaded;
|
||||
this.load();
|
||||
}
|
||||
setFormat(format: string) {
|
||||
|
|
@ -60,7 +58,7 @@ class TeamRoom extends PSRoom {
|
|||
buf.push(exported);
|
||||
PS.teams.uploading = team;
|
||||
PS.send(`|/teams ${cmd} ${buf.join(', ')}`);
|
||||
this.uploaded = true;
|
||||
team.uploadedPackedTeam = exported;
|
||||
this.update(null);
|
||||
}
|
||||
save() {
|
||||
|
|
@ -114,11 +112,10 @@ class TeamPanel extends PSRoomPanel<TeamRoom> {
|
|||
|
||||
uploadTeam = (ev: Event) => {
|
||||
const room = this.props.room;
|
||||
room.upload(PS.prefs.uploadprivacy);
|
||||
room.upload(room.team.uploaded ? !!room.team.uploaded.private : PS.prefs.uploadprivacy);
|
||||
};
|
||||
|
||||
changePrivacyPref = (ev: Event) => {
|
||||
this.props.room.uploaded = false;
|
||||
PS.prefs.uploadprivacy = !(ev.currentTarget as HTMLInputElement).checked;
|
||||
PS.prefs.save();
|
||||
this.forceUpdate();
|
||||
|
|
@ -136,7 +133,6 @@ class TeamPanel extends PSRoomPanel<TeamRoom> {
|
|||
};
|
||||
save = () => {
|
||||
this.props.room.save();
|
||||
this.props.room.uploaded = false;
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
|
|
@ -156,18 +152,20 @@ class TeamPanel extends PSRoomPanel<TeamRoom> {
|
|||
|
||||
const info = TeamPanel.formatResources[team.format];
|
||||
const formatName = BattleLog.formatName(team.format);
|
||||
const unsaved = team.uploaded ? team.uploadedPackedTeam !== team.packedTeam : false;
|
||||
return <PSPanelWrapper room={room} scrollable><div class="pad">
|
||||
<a class="button" href="teambuilder" data-target="replace">
|
||||
<i class="fa fa-chevron-left" aria-hidden></i> Teams
|
||||
</a> {}
|
||||
{team.uploaded?.private ? (
|
||||
<button class="button" data-href={`teamstorage-${team.key}`}>
|
||||
<i class="fa fa-cloud"></i> Account
|
||||
</button>
|
||||
) : team.uploaded ? (
|
||||
<button class="button" data-href={`teamstorage-${team.key}`}>
|
||||
<i class="fa fa-globe"></i> Account (public)
|
||||
</button>
|
||||
{team.uploaded ? (
|
||||
<>
|
||||
<button class={`button${unsaved ? ' button-first' : ''}`} data-href={`teamstorage-${team.key}`}>
|
||||
<i class="fa fa-globe"></i> Account {team.uploaded.private ? '' : "(public)"}
|
||||
</button>
|
||||
{unsaved && <button class="button button-last" onClick={this.uploadTeam}>
|
||||
<strong>Upload changes</strong>
|
||||
</button>}
|
||||
</>
|
||||
) : team.teamid ? (
|
||||
<button class="button" data-href={`teamstorage-${team.key}`}>
|
||||
<i class="fa fa-plug"></i> Disconnected (wrong account?)
|
||||
|
|
@ -191,31 +189,36 @@ class TeamPanel extends PSRoomPanel<TeamRoom> {
|
|||
onInput={this.handleRename} onChange={this.handleRename} onKeyUp={this.handleRename}
|
||||
/>
|
||||
</label>
|
||||
<TeamEditor team={team} onChange={this.save} readonly={!!team.teamid && !team.uploaded}>
|
||||
<TeamEditor team={team} onChange={this.save} readonly={!!team.teamid && !team.uploadedPackedTeam}>
|
||||
{!!(team.packedTeam && team.format.length > 4) && <p>
|
||||
<button data-cmd="/validate" class="button"><i class="fa fa-check"></i> Validate</button>
|
||||
</p>}
|
||||
{team.uploaded?.private === null && <p>
|
||||
<small>Share URL:</small> {}
|
||||
<input type="text" class="textbox" value={`https://psim.us/t/${team.uploaded.teamid}`} readOnly size={24} />
|
||||
</p>}
|
||||
{!!(team.packedTeam || team.uploaded) && <p>
|
||||
<label class="checkbox inline">
|
||||
{!!(team.packedTeam || team.uploaded) && <p class="infobox" style="padding: 5px 8px">
|
||||
{team.uploadedPackedTeam && !team.uploaded ? <>
|
||||
Uploading...
|
||||
</> : team.uploaded ? <>
|
||||
<small>Share URL:</small> {}
|
||||
<input
|
||||
name="teamprivacy" checked={!PS.prefs.uploadprivacy}
|
||||
type="checkbox" onChange={this.changePrivacyPref}
|
||||
/> Public
|
||||
</label>
|
||||
{room.uploaded ? (
|
||||
<button class="button exportbutton" disabled>
|
||||
<i class="fa fa-check"></i> Saved to your account
|
||||
</button>
|
||||
) : (
|
||||
type="text" class="textbox" readOnly size={45}
|
||||
value={`https://psim.us/t/${team.uploaded.teamid}${team.uploaded.private ? '-' + team.uploaded.private : ''}`}
|
||||
/> {}
|
||||
{unsaved && <div style="padding-top:5px">
|
||||
<button class="button" onClick={this.uploadTeam}>
|
||||
<i class="fa fa-upload"></i> Upload changes
|
||||
</button>
|
||||
</div>}
|
||||
</> : !team.teamid ? <>
|
||||
<label class="checkbox inline">
|
||||
<input
|
||||
name="teamprivacy" checked={!PS.prefs.uploadprivacy}
|
||||
type="checkbox" onChange={this.changePrivacyPref}
|
||||
/> Public
|
||||
</label>
|
||||
<button class="button exportbutton" onClick={this.uploadTeam}>
|
||||
<i class="fa fa-upload"></i> Save to my account {}
|
||||
({PS.prefs.uploadprivacy ? 'use on other devices' : 'share and make searchable'})
|
||||
<i class="fa fa-upload"></i> Upload for shareable URL {}
|
||||
{PS.prefs.uploadprivacy ? '' : '(and make searchable)'}
|
||||
</button>
|
||||
)}
|
||||
</> : null}
|
||||
</p>}
|
||||
</TeamEditor>
|
||||
{!!(info && (info.resources.length || info.url)) && (
|
||||
|
|
@ -252,6 +255,7 @@ class TeamStoragePanel extends PSRoomPanel {
|
|||
PS.mainmenu.send(`/teams delete ${team.uploaded.teamid}`);
|
||||
team.uploaded = undefined;
|
||||
team.teamid = undefined;
|
||||
team.uploadedPackedTeam = undefined;
|
||||
PS.teams.save();
|
||||
(room.getParent() as TeamRoom).update(null);
|
||||
} else if (storage === 'public' && team.uploaded?.private) {
|
||||
|
|
|
|||
|
|
@ -470,9 +470,9 @@ class TeambuilderPanel extends PSRoomPanel<TeambuilderRoom> {
|
|||
{teams.map(team => team ? (
|
||||
<li key={team.key} onDragEnter={this.dragEnterTeam} data-teamkey={team.key}>
|
||||
<TeamBox team={team} /> {}
|
||||
<button data-cmd={`/deleteteam ${team.key}`} class="option">
|
||||
{!team.uploaded && <button data-cmd={`/deleteteam ${team.key}`} class="option">
|
||||
<i class="fa fa-trash" aria-hidden></i> Delete
|
||||
</button> {}
|
||||
</button>} {}
|
||||
{team.uploaded?.private ? (
|
||||
<i class="fa fa-cloud gray"></i>
|
||||
) : team.uploaded ? (
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ li::marker {
|
|||
content: "";
|
||||
display: block;
|
||||
height: 6px;
|
||||
margin: -1px 0 -1px 0;
|
||||
margin: -1px 0 0 0;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.2);
|
||||
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.2);
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.2);
|
||||
|
|
@ -1993,6 +1993,7 @@ pre.textbox.textbox-empty[placeholder]:before {
|
|||
|
||||
.switchmenu button.disabled,
|
||||
.switchmenu button:disabled,
|
||||
.movebutton.disabled,
|
||||
.movebutton:disabled {
|
||||
cursor: default;
|
||||
background: #F3F3F3 !important;
|
||||
|
|
@ -2080,6 +2081,7 @@ pre.textbox.textbox-empty[placeholder]:before {
|
|||
text-align: center;
|
||||
/* individual pokemon in a team have margin-right -4px so this compensates */
|
||||
margin: 0 -1px 0 -5px;
|
||||
white-space: normal;
|
||||
}
|
||||
.team small span {
|
||||
margin-right: -4px;
|
||||
|
|
|
|||
|
|
@ -536,7 +536,7 @@ you can't delete it by pressing Backspace */
|
|||
border: 0;
|
||||
border-spacing: 0;
|
||||
table-layout: fixed;
|
||||
margin: 4px 0;
|
||||
margin: 1px 0;
|
||||
width: 100%;
|
||||
}
|
||||
.set-button td {
|
||||
|
|
@ -570,10 +570,11 @@ you can't delete it by pressing Backspace */
|
|||
|
||||
.set-button .set-nickname {
|
||||
position: absolute;
|
||||
height: 35px;
|
||||
height: 32px;
|
||||
width: 120px;
|
||||
top: 5px;
|
||||
left: -5px;
|
||||
padding: 0 3px;
|
||||
}
|
||||
.tiny-layout .set-button .set-nickname {
|
||||
width: 90px;
|
||||
|
|
@ -661,8 +662,9 @@ you can't delete it by pressing Backspace */
|
|||
}
|
||||
.team-focus-editor .tabbar {
|
||||
/* note to self: make this scrollable later */
|
||||
/* overflow: auto; */
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
min-height: 59px;
|
||||
}
|
||||
.tabbar .button.picontab {
|
||||
width: 80px;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user