Teams: Support copying a team to your builder

This commit is contained in:
Mia 2025-07-23 22:59:56 -05:00
parent 00d4b0aab7
commit 2bed05fcb9
4 changed files with 47 additions and 28 deletions

View File

@ -987,6 +987,7 @@ function toId() {
team.privacy = data.privacy;
team.team = data.team;
team.loaded = true;
Storage.saveTeams();
callback(team);
var entry = app.loadingTeamQueue.shift();
if (entry) {

View File

@ -1,7 +1,7 @@
/** @jsx preact.h */
/** @jsxFrag preact.Fragment */
import preact from '../../play.pokemonshowdown.com/js/lib/preact';
import { Net, type ServerTeam, MiniTeam } from './utils';
import { Net, type ServerTeam, MiniTeam, query } from './utils';
import type { PageProps } from './teams';
import { Config } from '../../play.pokemonshowdown.com/src/client-main';
@ -84,16 +84,9 @@ export class TeamSearcher extends preact.Component<PageProps> {
for (const k in this.state.search) {
if (!this.state.search[k]) delete this.state.search[k];
}
void Net('/api/searchteams').get({
void query('searchteams', {
query: { ...this.state.search, format, count: this.state.curCount },
}).then(resultText => {
if (resultText.startsWith(']')) resultText = resultText.slice(1);
let result;
try {
result = JSON.parse(resultText);
} catch {
result = { actionerror: "Malformed response received. Try again later." };
}
}).then(result => {
this.setState({ ...result, loading: false });
});
}

View File

@ -1,7 +1,7 @@
/** @jsx preact.h */
/** @jsxFrag preact.Fragment */
import preact from '../../play.pokemonshowdown.com/js/lib/preact';
import { Net, PSIcon, getShowdownUsername, unpackTeam } from './utils';
import { PSIcon, getShowdownUsername, unpackTeam, query } from './utils';
import { BattleLog } from '../../play.pokemonshowdown.com/src/battle-log';
import type { PageProps } from './teams';
import { Dex } from '../../play.pokemonshowdown.com/src/battle-dex';
@ -213,6 +213,8 @@ export class TeamViewer extends preact.Component<PageProps> {
changesMade: false,
teamEdits: null as { format?: string, private?: number } | null,
editError: null as null | string,
copied: null as null | string,
copyError: null as null | string,
};
constructor(props: PageProps) {
super(props);
@ -239,6 +241,8 @@ export class TeamViewer extends preact.Component<PageProps> {
const is2Col = this.state.displayMode === '2col';
const isDark = document.querySelector('html')?.classList[0] === 'dark';
const link = this.id + (this.state.team.private ? `-${this.state.team.private}` : '');
const loggedin = toID(getShowdownUsername());
const manageClass = this.state.manageOpen ? `button notifying` : `button`;
return <div class="section" style={{ wordWrap: 'break-word' }}>
<div name="header" className="noselect">
@ -250,10 +254,9 @@ export class TeamViewer extends preact.Component<PageProps> {
<label>Shortlink: </label><a href={`https://psim.us/t/${link}`}>https://psim.us/t/{link}</a><br />
<hr />
<div name="manage" style={{ display: 'flex', gap: '5px' }}>
{toID(getShowdownUsername()) === this.state.team.ownerid && <button
class={this.state.manageOpen ? `button notifying` : `button`}
onClick={() => this.changeManage()}
>Manage</button>}
{loggedin === this.state.team.ownerid ?
<button class={manageClass} onClick={() => this.changeManage()}>Manage</button> :
loggedin && <button class="button" onClick={() => this.copyToBuilder()}>Copy to builder</button>}
<button
class="button"
disabled={!this.state.team || this.state.copyButtonMsg}
@ -274,6 +277,12 @@ export class TeamViewer extends preact.Component<PageProps> {
<div class="message-error">{this.state.editError}</div>
<hr />
</>}
{this.state.copied && <>
<br />Copied to your teambuilder! <a href={`/view/${this.state.copied}`}>View</a><hr />
</>}
{this.state.copyError && <>
<br /><div className="message-error">Error copying team: {this.state.copyError}</div><hr />
</>}
{this.state.manageOpen && <>
<label>Team visibility: </label>
<select
@ -351,14 +360,7 @@ export class TeamViewer extends preact.Component<PageProps> {
}
loadTeamData() {
void Net('/api/getteam').get({ query: { teamid: this.id, password: this.pw, full: 1 } }).then(resultText => {
if (resultText.startsWith(']')) resultText = resultText.slice(1);
let result;
try {
result = JSON.parse(resultText);
} catch {
result = { actionerror: "Malformed response received. Try again later." };
}
void query('getteam', { query: { teamid: this.id, password: this.pw, full: 1 } }).then(result => {
if (result.actionerror) {
this.setState({ error: result.actionerror });
} else {
@ -379,6 +381,16 @@ export class TeamViewer extends preact.Component<PageProps> {
}, 1000);
}
copyToBuilder() {
void query('copyteam', { query: { teamid: this.id, pw: this.pw } }).then(result => {
if (result.actionerror) {
this.setState({ copyError: result.actionerror, copied: null });
} else {
this.setState({ copied: result.teamid, copyError: null });
}
});
}
editTeamValue(val: 'format' | 'private', { currentTarget }: any) {
(this.state.teamEdits ||= {})[val] = currentTarget.value;
let changes;
@ -400,7 +412,7 @@ export class TeamViewer extends preact.Component<PageProps> {
commitEdit() {
if (!this.state.changesMade || !this.state.team) return;
void Net('/api/editteam').get({
void query('editteam', {
query: { teamid: this.id, ...this.state.teamEdits },
}).then(resultText => {
if (resultText.startsWith(']')) resultText = resultText.slice(1);

View File

@ -111,11 +111,11 @@ Net.encodeQuery = function (data: string | PostData) {
}
return urlencodedData;
};
Net.decodeQuery = function (query: string): { [key: string]: string } {
Net.decodeQuery = function (queryData: string): { [key: string]: string } {
let out: { [key: string]: string } = {};
const questionIndex = query.indexOf('?');
if (questionIndex >= 0) query = query.slice(questionIndex + 1);
for (const queryPart of query.split('&')) {
const questionIndex = queryData.indexOf('?');
if (questionIndex >= 0) queryData = queryData.slice(questionIndex + 1);
for (const queryPart of queryData.split('&')) {
const [key, value] = queryPart.split('=');
out[decodeURIComponent(key)] = decodeURIComponent(value || '');
}
@ -389,3 +389,16 @@ export function getShowdownUsername() {
}
return null;
}
export function query(act: string, opts?: NetRequestOptions) {
return Net('/api/' + act).get(opts).then(resultText => {
if (resultText.startsWith(']')) resultText = resultText.slice(1);
let result;
try {
result = JSON.parse(resultText);
} catch {
result = { actionerror: "Malformed response received. Try again later." };
}
return result;
});
}