mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-10-26 04:07:56 -05:00
Preact minor updates batch 30
- Fix starting tournaments with custom name - Fix error when joining private rooms - Improve colors for command syntax highlighting - Improve performance by not updating background rooms - Rename a bunch of room visibility functions for clarity - Default language to English - Add timestamps to disconnect/reconnect messages - Fix server teams not getting fully downloaded
This commit is contained in:
@@ -122,6 +122,13 @@ export class BattleLog {
|
||||
});
|
||||
this.addNode(el);
|
||||
}
|
||||
static renderTimestamp(timestamp: number | null, showTimestamps?: 'minutes' | 'seconds' | null) {
|
||||
if (!showTimestamps) return '';
|
||||
const date = timestamp && !isNaN(timestamp) ? new Date(timestamp * 1000) : new Date();
|
||||
const components = [date.getHours(), date.getMinutes()];
|
||||
if (showTimestamps === 'seconds') components.push(date.getSeconds());
|
||||
return `<small class="gray">[${components.map(x => x < 10 ? `0${x}` : x).join(':')}] </small>`;
|
||||
}
|
||||
add(args: Args, kwArgs?: KWArgs, preempt?: boolean, showTimestamps?: 'minutes' | 'seconds') {
|
||||
if (kwArgs?.silent) return;
|
||||
const battle = this.scene?.battle;
|
||||
@@ -169,15 +176,7 @@ export class BattleLog {
|
||||
}
|
||||
const ignoreList = window.app?.ignore || window.PS?.prefs?.ignore;
|
||||
if (ignoreList?.[toUserid(name)] && ' +^\u2605\u2606'.includes(rank)) return;
|
||||
let timestampHtml = '';
|
||||
if (showTimestamps) {
|
||||
const date = timestamp && !isNaN(timestamp) ? new Date(timestamp * 1000) : new Date();
|
||||
const components = [date.getHours(), date.getMinutes()];
|
||||
if (showTimestamps === 'seconds') {
|
||||
components.push(date.getSeconds());
|
||||
}
|
||||
timestampHtml = `<small class="gray">[${components.map(x => x < 10 ? `0${x}` : x).join(':')}] </small>`;
|
||||
}
|
||||
const timestampHtml = BattleLog.renderTimestamp(timestamp, showTimestamps);
|
||||
const isHighlighted = window.app?.rooms?.[battle!.roomid].getHighlight(message) || this.getHighlight?.(args);
|
||||
[divClass, divHTML, noNotify] = this.parseChatMessage(message, name, timestampHtml, isHighlighted);
|
||||
if (!noNotify && isHighlighted) {
|
||||
|
||||
@@ -531,7 +531,7 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||
}
|
||||
localTeam.uploaded = {
|
||||
teamid: team.teamid,
|
||||
notLoaded: false,
|
||||
notLoaded: this.unloadedPackedTeam(team.team) === localTeam.packedTeam,
|
||||
private: team.private,
|
||||
};
|
||||
delete teams[localTeam.teamid];
|
||||
@@ -554,22 +554,22 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||
localTeam.teamid = team.teamid;
|
||||
localTeam.uploaded = {
|
||||
teamid: team.teamid,
|
||||
notLoaded: false,
|
||||
notLoaded: this.unloadedPackedTeam(team.team) === localTeam.packedTeam,
|
||||
private: team.private,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
const mons = team.team.split(',').map((m: string) => ({ species: m, moves: [] }));
|
||||
const newTeam: Team = {
|
||||
name: team.name,
|
||||
format: team.format,
|
||||
folder: '',
|
||||
packedTeam: Teams.pack(mons),
|
||||
packedTeam: this.unloadedPackedTeam(team.team),
|
||||
iconCache: null,
|
||||
isBox: false,
|
||||
key: this.getKey(team.name),
|
||||
teamid: team.teamid,
|
||||
uploaded: {
|
||||
teamid: team.teamid,
|
||||
notLoaded: true,
|
||||
@@ -581,6 +581,10 @@ class PSTeams extends PSStreamModel<'team' | 'format'> {
|
||||
}
|
||||
});
|
||||
}
|
||||
unloadedPackedTeam(uploadedTeam: string) {
|
||||
const mons = uploadedTeam.split(',').map((m: string) => ({ species: m, moves: [] }));
|
||||
return Teams.pack(mons);
|
||||
}
|
||||
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> {
|
||||
@@ -1059,7 +1063,7 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||
}
|
||||
notify(options: { title: string, body?: string, noAutoDismiss?: boolean, id?: string }) {
|
||||
let desktopNotification: Notification | null = null;
|
||||
const roomIsFocused = document.hasFocus?.() && PS.isVisible(this);
|
||||
const roomIsFocused = document.hasFocus?.() && PS.isVisiblePanel(this);
|
||||
if (roomIsFocused && !options.noAutoDismiss) return;
|
||||
if (!roomIsFocused) {
|
||||
PS.playNotificationSound();
|
||||
@@ -1092,7 +1096,7 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||
PS.update();
|
||||
}
|
||||
subtleNotify() {
|
||||
if (PS.isVisible(this)) return;
|
||||
if (PS.isVisiblePanel(this)) return;
|
||||
const room = PS.rooms[this.id] as ChatRoom;
|
||||
const lastSeenTimestamp = PS.prefs.logtimes?.[PS.server.id]?.[this.id] || 0;
|
||||
const lastMessageTime = +(room.lastMessage?.[1] || 0);
|
||||
@@ -1245,7 +1249,7 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||
const roomid = /[^a-z0-9-]/.test(target) ? toID(target) as any as RoomID : target as RoomID;
|
||||
const targetRoom = roomid ? PS.rooms[roomid] : this;
|
||||
if (!targetRoom) return this.errorReply(`Room '${roomid}' not found.`);
|
||||
if (PS.isNormalRoom(targetRoom)) {
|
||||
if (PS.isPanel(targetRoom)) {
|
||||
this.errorReply(`'${roomid}' is already maximized.`);
|
||||
} else if (!PS.isPopup(targetRoom)) {
|
||||
PS.moveRoom(targetRoom, 'left', false, 0);
|
||||
@@ -2180,7 +2184,7 @@ export const PS = new class extends PSModel {
|
||||
if (!PS.user.initializing) {
|
||||
room.receiveLine(['error', args[2]]);
|
||||
}
|
||||
} else if (args[1] === 'nonexistent') {
|
||||
} else if (args[1] === 'nonexistent' || args[1] === 'joinfailed') {
|
||||
// sometimes we assume a room is a chatroom when it's not
|
||||
// when that happens, just ignore this error
|
||||
if (room.type === 'chat' || room.type === 'battle') room.receiveLine(args);
|
||||
@@ -2222,7 +2226,17 @@ export const PS = new class extends PSModel {
|
||||
}
|
||||
this.connection.send(`${roomid || ''}|${msg}`);
|
||||
}
|
||||
isVisible(room: PSRoom) {
|
||||
isVisible(room: PSRoom): boolean {
|
||||
if (PS.isPanel(room)) {
|
||||
return !this.leftPanelWidth ? room === this.panel : room === this.leftPanel || room === this.rightPanel;
|
||||
}
|
||||
if (room.location === 'mini-window') {
|
||||
return !this.leftPanelWidth ? this.mainmenu === this.panel : this.mainmenu === this.leftPanel;
|
||||
}
|
||||
// some kind of popup
|
||||
return true;
|
||||
}
|
||||
isVisiblePanel(room: PSRoom) {
|
||||
if (!this.leftPanelWidth) {
|
||||
// one panel visible
|
||||
return room === this.panel || room === this.room;
|
||||
@@ -2357,10 +2371,10 @@ export const PS = new class extends PSModel {
|
||||
return true;
|
||||
}
|
||||
this.closePopupsAbove(room, true);
|
||||
if (!this.isVisible(room)) {
|
||||
if (!this.isVisiblePanel(room)) {
|
||||
room.focusNextUpdate = true;
|
||||
}
|
||||
if (PS.isNormalRoom(room)) {
|
||||
if (PS.isPanel(room)) {
|
||||
if (room.location === 'right') {
|
||||
this.rightPanel = room;
|
||||
} else {
|
||||
@@ -2583,16 +2597,6 @@ export const PS = new class extends PSModel {
|
||||
PS.focusRoom(PS.leftPanel.id);
|
||||
}
|
||||
}
|
||||
roomVisible(room: PSRoom): boolean {
|
||||
if (PS.isNormalRoom(room)) {
|
||||
return !this.leftPanelWidth ? room === this.panel : room === this.leftPanel || room === this.rightPanel;
|
||||
}
|
||||
if (room.location === 'mini-window') {
|
||||
return !this.leftPanelWidth ? this.mainmenu === this.panel : this.mainmenu === this.leftPanel;
|
||||
}
|
||||
// some kind of popup
|
||||
return true;
|
||||
}
|
||||
renameRoom(room: PSRoom, id: RoomID) {
|
||||
// should never happen
|
||||
if (this.rooms[id]) this.removeRoom(this.rooms[id]);
|
||||
@@ -2617,7 +2621,8 @@ export const PS = new class extends PSModel {
|
||||
if (!room) return false;
|
||||
return room.location === 'popup' || room.location === 'modal-popup';
|
||||
}
|
||||
isNormalRoom(room: PSRoom | undefined | null) {
|
||||
/** this isn't just !isPopup. you forgot about mini windows again. */
|
||||
isPanel(room: PSRoom | undefined | null) {
|
||||
if (!room) return false;
|
||||
return room.location === 'left' || room.location === 'right';
|
||||
}
|
||||
@@ -2641,7 +2646,7 @@ export const PS = new class extends PSModel {
|
||||
return;
|
||||
}
|
||||
|
||||
background ??= !this.roomVisible(room);
|
||||
background ??= !this.isVisible(room);
|
||||
|
||||
if (room.location === 'mini-window') {
|
||||
const miniRoomIndex = this.miniRoomList.indexOf(room.id);
|
||||
|
||||
@@ -53,6 +53,7 @@ interface TournamentTableBracketData {
|
||||
scores: number[];
|
||||
}
|
||||
type TournamentInfo = {
|
||||
/** nickname will override this */
|
||||
format?: string,
|
||||
teambuilderFormat?: string,
|
||||
generator?: string,
|
||||
@@ -148,7 +149,8 @@ export class ChatTournament extends PSModel {
|
||||
} else if (args[0] === 'tournament') {
|
||||
switch (cmd) {
|
||||
case 'create': {
|
||||
this.info.format = args[2];
|
||||
this.info.format = args[5] || args[2];
|
||||
this.info.teambuilderFormat = args[2];
|
||||
this.info.generator = args[3];
|
||||
const formatName = BattleLog.formatName(args[2]);
|
||||
const type = args[3];
|
||||
|
||||
@@ -107,12 +107,18 @@ export class ChatRoom extends PSRoom {
|
||||
return;
|
||||
|
||||
case 'noinit':
|
||||
if (this.battle) {
|
||||
if (this.battle && args[1] === 'joinfailed') {
|
||||
this.receiveLine(['bigerror', args[2]]);
|
||||
this.receiveLine(['html',
|
||||
`<div class="broadcast-red pad"><p class="buttonbar"><button class="button" data-cmd="/close"><strong>Close</strong></button></p></div>`,
|
||||
]);
|
||||
} else if (this.battle) {
|
||||
// check the Replays database
|
||||
(this as any as BattleRoom).loadReplay();
|
||||
} else {
|
||||
const message = args[2] ? BattleLog.escapeHTML(args[2]) : `Chatroom "${BattleLog.escapeHTML(this.title)}" not found`;
|
||||
this.receiveLine(['html',
|
||||
`<div class="broadcast-red pad"><h3>Chatroom "${BattleLog.escapeHTML(this.title)}" not found</h3><p class="buttonbar"><button class="button" data-cmd="/close"><strong>Close</strong></button></p></div>`,
|
||||
`<div class="broadcast-red pad"><h3>${message}</h3><p class="buttonbar"><button class="button" data-cmd="/close"><strong>Close</strong></button></p></div>`,
|
||||
]);
|
||||
}
|
||||
return;
|
||||
@@ -199,12 +205,13 @@ export class ChatRoom extends PSRoom {
|
||||
if (time < cutOffTime) cutOffStart = i;
|
||||
}
|
||||
if (lines[i].startsWith('|raw|<div class="infobox"> You joined ')) {
|
||||
reconnectMessage = `|raw|<div class="infobox">You reconnected to ${lines[i].slice(38)}`;
|
||||
const timestamp = BattleLog.renderTimestamp(Date.now() / 1000, PS.prefs.timestamps?.chatrooms);
|
||||
reconnectMessage = `|raw|<div class="infobox">${timestamp}You reconnected to ${lines[i].slice(38)}`;
|
||||
cutOffEnd = i;
|
||||
if (!lines[i - 1]) cutOffEnd = i - 1;
|
||||
}
|
||||
}
|
||||
console.log("Reconnection log splice:");
|
||||
console.log(`Reconnection log splice: (cutoff: ${cutOffTime})`);
|
||||
console.log([
|
||||
...lines.slice(0, cutOffStart),
|
||||
'====================',
|
||||
@@ -215,7 +222,8 @@ export class ChatRoom extends PSRoom {
|
||||
lines = lines.slice(cutOffStart, cutOffEnd);
|
||||
|
||||
if (lines.length) {
|
||||
this.receiveLine([`raw`, `<div class="infobox">You disconnected.</div>`]);
|
||||
const timestamp = BattleLog.renderTimestamp(cutOffTime, PS.prefs.timestamps?.chatrooms);
|
||||
this.receiveLine([`raw`, `<div class="infobox">${timestamp}You disconnected.</div>`]);
|
||||
for (const line of lines) this.receiveLine(BattleTextParser.parseLine(line));
|
||||
this.receiveLine(BattleTextParser.parseLine(reconnectMessage));
|
||||
}
|
||||
@@ -307,7 +315,7 @@ export class ChatRoom extends PSRoom {
|
||||
const lastMessageDate = lastMessageDates[PS.server.id][this.id] || 0;
|
||||
// because the time offset to the server can vary slightly, subtract it to not have it affect comparisons between dates
|
||||
const time = serverTime - (this.timeOffset || 0);
|
||||
if (PS.isVisible(this)) {
|
||||
if (PS.isVisiblePanel(this)) {
|
||||
this.lastMessageTime = null;
|
||||
lastMessageDates[PS.server.id][this.id] = time;
|
||||
PS.prefs.set('logtimes', lastMessageDates);
|
||||
|
||||
@@ -458,6 +458,7 @@ export class MainMenuRoom extends PSRoom {
|
||||
case 'teamupload':
|
||||
if (PS.teams.uploading) {
|
||||
const team = PS.teams.uploading;
|
||||
team.teamid = response.teamid;
|
||||
team.uploaded = {
|
||||
teamid: response.teamid,
|
||||
notLoaded: false,
|
||||
|
||||
@@ -666,7 +666,7 @@ class OptionsPanel extends PSRoomPanel {
|
||||
|
||||
{this.state.showStatusInput ? (
|
||||
<p>
|
||||
<input name="statustext" />
|
||||
<input class="textbox" name="statustext" />
|
||||
<button class="button" onClick={this.editStatus}><i class="fa fa-pencil" aria-hidden></i></button>
|
||||
</p>
|
||||
) : (
|
||||
@@ -758,9 +758,9 @@ class OptionsPanel extends PSRoomPanel {
|
||||
<p>
|
||||
<label class="optlabel">
|
||||
Language: {}
|
||||
<select name="language" onChange={this.handleOnChange} class="button" value={serverSettings.language}>
|
||||
<option value="german">Deutsch</option>
|
||||
<select name="language" onChange={this.handleOnChange} class="button" value={serverSettings.language || 'english'}>
|
||||
<option value="english">English</option>
|
||||
<option value="german">Deutsch</option>
|
||||
<option value="spanish">Español</option>
|
||||
<option value="french">Français</option>
|
||||
<option value="italian">Italiano</option>
|
||||
|
||||
@@ -191,7 +191,7 @@ class RoomsPanel extends PSRoomPanel {
|
||||
return [...hidden, ["Search results", results]];
|
||||
}
|
||||
override render() {
|
||||
if (this.hidden && PS.isVisible(this.props.room)) this.hidden = false;
|
||||
if (this.hidden && PS.isVisiblePanel(this.props.room)) this.hidden = false;
|
||||
if (this.hidden) {
|
||||
return <PSPanelWrapper room={this.props.room}>{null}</PSPanelWrapper>;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ export class PSHeader extends preact.Component {
|
||||
const room = PS.rooms[id];
|
||||
if (!room) return null;
|
||||
const closable = (id === '' || id === 'rooms' ? '' : ' closable');
|
||||
const cur = PS.isVisible(room) ? ' cur' : '';
|
||||
const cur = PS.isVisiblePanel(room) ? ' cur' : '';
|
||||
let notifying = room.isSubtleNotifying ? ' subtle-notifying' : '';
|
||||
let hoverTitle = '';
|
||||
let notifications = room.notifications;
|
||||
|
||||
@@ -188,6 +188,7 @@ PS.router = new PSRouter();
|
||||
|
||||
export class PSRoomPanel<T extends PSRoom = PSRoom> extends preact.Component<{ room: T }> {
|
||||
subscriptions: PSSubscription[] = [];
|
||||
wasVisible = true; // remember, shouldComponentUpdate isn't called on first render
|
||||
subscribeTo<M>(
|
||||
model: PSModel<M> | PSStreamModel<M>, callback: (value: M) => void = () => { this.forceUpdate(); }
|
||||
): PSSubscription {
|
||||
@@ -205,6 +206,12 @@ export class PSRoomPanel<T extends PSRoom = PSRoom> extends preact.Component<{ r
|
||||
}));
|
||||
this.componentDidUpdate();
|
||||
}
|
||||
override shouldComponentUpdate() {
|
||||
const wasVisible = this.wasVisible;
|
||||
const visible = PS.isVisible(this.props.room);
|
||||
this.wasVisible = visible;
|
||||
return visible || wasVisible;
|
||||
}
|
||||
justUpdatedDimensions = false;
|
||||
updateDimensions() {
|
||||
const justUpdated = this.justUpdatedDimensions;
|
||||
@@ -940,7 +947,7 @@ export class PSView extends preact.Component {
|
||||
let rooms = [] as preact.VNode[];
|
||||
for (const roomid in PS.rooms) {
|
||||
const room = PS.rooms[roomid]!;
|
||||
if (PS.isNormalRoom(room)) {
|
||||
if (PS.isPanel(room)) {
|
||||
rooms.push(this.renderRoom(room));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +420,12 @@ pre.textbox {
|
||||
.textbox tt {
|
||||
color: #3388CC;
|
||||
}
|
||||
.textbox tt.message-cmd {
|
||||
color: #1a5f97;
|
||||
}
|
||||
.dark .textbox tt.message-cmd {
|
||||
color: #86b7df;
|
||||
}
|
||||
.textbox:hover {
|
||||
border-color: #474747;
|
||||
box-shadow: inset 0px 1px 2px #D2D2D2, 1px 1px 0 rgba(255,255,255,.6);
|
||||
|
||||
@@ -5,7 +5,7 @@ License: GPLv2
|
||||
|
||||
*/
|
||||
|
||||
@import url(./battle-log.css?v11);
|
||||
@import url(./battle-log.css?v12);
|
||||
|
||||
.battle {
|
||||
position: absolute;
|
||||
|
||||
Reference in New Issue
Block a user