diff --git a/src/client-main.ts b/src/client-main.ts index cb2cc4328..d47f96db6 100644 --- a/src/client-main.ts +++ b/src/client-main.ts @@ -559,6 +559,7 @@ const PS = new class extends PSModel { case 'html': case 'raw': case 'challstr': + case 'popup': case '': return [cmd, str.slice(index + 1)]; case 'c': @@ -878,11 +879,18 @@ const PS = new class extends PSModel { } return buf; } + getPMRoom(userid: ID) { + const myUserid = PS.user.userid; + const roomid = `pm-${[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; + } addRoom(options: RoomOptions, noFocus?: boolean) { // support hardcoded PM room-IDs if (options.id.startsWith('challenge-')) { options.id = `pm-${options.id.slice(10)}` as RoomID; - options.challenging = true; + options.challengeMenuOpen = true; } if (options.id.startsWith('pm-') && options.id.indexOf('-', 3) < 0) { const userid1 = PS.user.userid; @@ -902,7 +910,7 @@ const PS = new class extends PSModel { } } if (!noFocus) { - if (options.challenging) { + if (options.challengeMenuOpen) { (this.rooms[options.id] as ChatRoom).openChallenge(); } this.focusRoom(options.id); diff --git a/src/panel-chat.tsx b/src/panel-chat.tsx index 9aa985b73..91c2834a9 100644 --- a/src/panel-chat.tsx +++ b/src/panel-chat.tsx @@ -9,12 +9,17 @@ class ChatRoom extends PSRoom { readonly classType: string = 'chat'; users: {[userid: string]: string} = {}; userCount = 0; + + // PM-only properties pmTarget: string | null = null; - challenging = false; + challengeMenuOpen = false; + challengingFormat: string | null = null; + challengedFormat: string | null = null; + constructor(options: RoomOptions) { super(options); if (options.pmTarget) this.pmTarget = options.pmTarget as string; - if (options.challenging) this.challenging = true; + if (options.challengeMenuOpen) this.challengeMenuOpen = true; this.updateTarget(true); this.connect(); } @@ -64,6 +69,9 @@ class ChatRoom extends PSRoom { } this.openChallenge(); return true; + } case 'cchall': case 'cancelchallenge': { + this.cancelChallenge(); + return true; }} return false; } @@ -72,7 +80,21 @@ class ChatRoom extends PSRoom { this.receive(`|error|Can only be used in a PM.`); return; } - this.challenging = true; + this.challengeMenuOpen = true; + this.update(''); + } + cancelChallenge() { + if (!this.pmTarget) { + this.receive(`|error|Can only be used in a PM.`); + return; + } + if (this.challengingFormat) { + this.send('/cancelchallenge', true); + this.challengingFormat = null; + this.challengeMenuOpen = true; + } else { + this.challengeMenuOpen = false; + } this.update(''); } send(line: string, direct?: boolean) { @@ -310,30 +332,57 @@ class ChatPanel extends PSRoomPanel { challenge = (e: Event, format: string, team?: Team) => { const room = this.props.room; const packedTeam = team ? team.packedTeam : ''; - room.challenging = false; if (!room.pmTarget) throw new Error("Not a PM room"); PS.send(`|/utm ${packedTeam}`); PS.send(`|/challenge ${room.pmTarget}, ${format}`); + room.challengeMenuOpen = false; + room.challengingFormat = format; + room.update(''); + }; + acceptChallenge = (e: Event, format: string, team?: Team) => { + const room = this.props.room; + const packedTeam = team ? team.packedTeam : ''; + if (!room.pmTarget) throw new Error("Not a PM room"); + PS.send(`|/utm ${packedTeam}`); + this.props.room.send(`/accept`); + room.challengedFormat = null; + room.update(''); }; cancelChallenge = (e: Event) => { + e.preventDefault(); + e.stopImmediatePropagation(); + this.props.room.cancelChallenge(); + }; + rejectChallenge = (e: Event) => { const room = this.props.room; - room.challenging = false; - room.update(''); + room.challengedFormat = null; + room.send(`/reject`); }; render() { const room = this.props.room; - const challenge = room.challenging ?
+ const challengeTo = room.challengingFormat ?
+ + + +
: room.challengeMenuOpen ?
{}
: null; + const challengeFrom = room.challengedFormat ?
+ + {} + + +
: null; + return
- {challenge} + {challengeTo || challengeFrom && [challengeTo, challengeFrom]} diff --git a/src/panel-mainmenu.tsx b/src/panel-mainmenu.tsx index b25452476..4485702c7 100644 --- a/src/panel-mainmenu.tsx +++ b/src/panel-mainmenu.tsx @@ -38,6 +38,9 @@ class MainMenuRoom extends PSRoom { case 'updateuser': PS.user.setName(tokens[1], tokens[2] === '1', tokens[3]); return; + case 'updatechallenges': + this.receiveChallenges(tokens[1]); + return; case 'queryresponse': this.handleQueryResponse(tokens[1] as ID, JSON.parse(tokens[2])); return; @@ -47,10 +50,37 @@ class MainMenuRoom extends PSRoom { case 'formats': this.parseFormats(tokens); return; + case 'popup': + alert(tokens[1]); + return; } const lobby = PS.rooms['lobby']; if (lobby) lobby.receive(line); } + receiveChallenges(dataBuf: string) { + let json; + try { + json = JSON.parse(dataBuf); + } catch {} + for (const userid in json.challengesFrom) { + PS.getPMRoom(toID(userid)); + } + if (json.challengeTo) { + PS.getPMRoom(toID(json.challengeTo.to)); + } + for (const roomid in PS.rooms) { + const room = PS.rooms[roomid] as ChatRoom; + if (!room.pmTarget) continue; + const targetUserid = toID(room.pmTarget); + if (!room.challengedFormat && !(targetUserid in json.challengesFrom) && + !room.challengingFormat && (json.challengeTo || {}).to !== targetUserid) { + continue; + } + room.challengedFormat = json.challengesFrom[targetUserid] || null; + room.challengingFormat = json.challengeTo.to === targetUserid ? json.challengeTo.format : null; + room.update(''); + } + } parseFormats(formatsList: string[]) { let isSection = false; let section = ''; @@ -378,7 +408,7 @@ class TeamDropdown extends preact.Component<{format: string}> { class TeamForm extends preact.Component<{ children: preact.ComponentChildren, class?: string, format?: string, - onSubmit: (e: Event, format: string, team?: Team) => void, + onSubmit: null | ((e: Event, format: string, team?: Team) => void), }> { state = {format: '[Gen 7] Random Battle'}; changeFormat = (e: Event) => { @@ -389,7 +419,7 @@ class TeamForm extends preact.Component<{ const format = (this.base!.querySelector('button[name=format]') as HTMLButtonElement).value; const teamKey = (this.base!.querySelector('button[name=team]') as HTMLButtonElement).value; const team = teamKey ? PS.teams.byKey[teamKey] : undefined; - this.props.onSubmit(e, format, team); + if (this.props.onSubmit) this.props.onSubmit(e, format, team); }; render() { return