mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-04-26 18:18:46 -05:00
Preact: Add highlight, receivepopup etc (#2406)
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
This commit is contained in:
parent
45f8880807
commit
33f78c413f
|
|
@ -53,7 +53,7 @@ export class BattleLog {
|
||||||
* * 1 = player 2: "Red sent out Pikachu!" "Eevee used Tackle!"
|
* * 1 = player 2: "Red sent out Pikachu!" "Eevee used Tackle!"
|
||||||
*/
|
*/
|
||||||
perspective: -1 | 0 | 1 = -1;
|
perspective: -1 | 0 | 1 = -1;
|
||||||
getHighlight: ((message: string, name: string) => boolean) | null = null;
|
getHighlight: ((line: Args) => boolean) | null = null;
|
||||||
constructor(elem: HTMLDivElement, scene?: BattleScene | null, innerElem?: HTMLDivElement) {
|
constructor(elem: HTMLDivElement, scene?: BattleScene | null, innerElem?: HTMLDivElement) {
|
||||||
this.elem = elem;
|
this.elem = elem;
|
||||||
|
|
||||||
|
|
@ -177,7 +177,7 @@ export class BattleLog {
|
||||||
}
|
}
|
||||||
timestampHtml = `<small class="gray">[${components.map(x => x < 10 ? `0${x}` : x).join(':')}] </small>`;
|
timestampHtml = `<small class="gray">[${components.map(x => x < 10 ? `0${x}` : x).join(':')}] </small>`;
|
||||||
}
|
}
|
||||||
const isHighlighted = window.app?.rooms?.[battle!.roomid].getHighlight(message) || this.getHighlight?.(message, name);
|
const isHighlighted = window.app?.rooms?.[battle!.roomid].getHighlight(message) || this.getHighlight?.(args);
|
||||||
[divClass, divHTML, noNotify] = this.parseChatMessage(message, name, timestampHtml, isHighlighted);
|
[divClass, divHTML, noNotify] = this.parseChatMessage(message, name, timestampHtml, isHighlighted);
|
||||||
if (!noNotify && isHighlighted) {
|
if (!noNotify && isHighlighted) {
|
||||||
const notifyTitle = "Mentioned by " + name + " in " + (battle?.roomid || '');
|
const notifyTitle = "Mentioned by " + name + " in " + (battle?.roomid || '');
|
||||||
|
|
@ -266,6 +266,7 @@ export class BattleLog {
|
||||||
|
|
||||||
case 'unlink': {
|
case 'unlink': {
|
||||||
// |unlink| is deprecated in favor of |hidelines|
|
// |unlink| is deprecated in favor of |hidelines|
|
||||||
|
if (window.PS.prefs.nounlink) return;
|
||||||
const user = toID(args[2]) || toID(args[1]);
|
const user = toID(args[2]) || toID(args[1]);
|
||||||
this.unlinkChatFrom(user);
|
this.unlinkChatFrom(user);
|
||||||
if (args[2]) {
|
if (args[2]) {
|
||||||
|
|
@ -276,6 +277,7 @@ export class BattleLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'hidelines': {
|
case 'hidelines': {
|
||||||
|
if (window.PS.prefs.nounlink) return;
|
||||||
const user = toID(args[2]);
|
const user = toID(args[2]);
|
||||||
this.unlinkChatFrom(user);
|
this.unlinkChatFrom(user);
|
||||||
if (args[1] !== 'unlink') {
|
if (args[1] !== 'unlink') {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
import { PSConnection, PSLoginServer } from './client-connection';
|
import { PSConnection, PSLoginServer } from './client-connection';
|
||||||
import { PSModel, PSStreamModel } from './client-core';
|
import { PSModel, PSStreamModel } from './client-core';
|
||||||
import type { PSRoomPanel, PSRouter } from './panels';
|
import type { PSRoomPanel, PSRouter } from './panels';
|
||||||
import type { ChatRoom } from './panel-chat';
|
import { ChatRoom } from './panel-chat';
|
||||||
import type { MainMenuRoom } from './panel-mainmenu';
|
import type { MainMenuRoom } from './panel-mainmenu';
|
||||||
import { Dex, toID, type ID } from './battle-dex';
|
import { Dex, toID, type ID } from './battle-dex';
|
||||||
import { BattleTextParser, type Args } from './battle-text-parser';
|
import { BattleTextParser, type Args } from './battle-text-parser';
|
||||||
|
|
@ -78,6 +78,7 @@ class PSPrefs extends PSStreamModel<string | null> {
|
||||||
hidelinks: false,
|
hidelinks: false,
|
||||||
hideinterstice: true,
|
hideinterstice: true,
|
||||||
};
|
};
|
||||||
|
nounlink: boolean | null = null;
|
||||||
|
|
||||||
/* Battle preferences */
|
/* Battle preferences */
|
||||||
ignorenicks: boolean | null = null;
|
ignorenicks: boolean | null = null;
|
||||||
|
|
@ -118,6 +119,9 @@ class PSPrefs extends PSStreamModel<string | null> {
|
||||||
|
|
||||||
afd: boolean | 'sprites' = false;
|
afd: boolean | 'sprites' = false;
|
||||||
|
|
||||||
|
highlights: Record<string, string[]> | null = null;
|
||||||
|
logtimes: Record<string, { [roomid: RoomID]: number }> | null = null;
|
||||||
|
|
||||||
// PREFS END HERE
|
// PREFS END HERE
|
||||||
|
|
||||||
storageEngine: 'localStorage' | 'iframeLocalStorage' | '' = '';
|
storageEngine: 'localStorage' | 'iframeLocalStorage' | '' = '';
|
||||||
|
|
@ -830,6 +834,14 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
PS.update();
|
PS.update();
|
||||||
}
|
}
|
||||||
autoDismissNotifications() {
|
autoDismissNotifications() {
|
||||||
|
let room = PS.rooms[this.id] as ChatRoom;
|
||||||
|
if (room.lastMessageTime) {
|
||||||
|
// Mark chat messages as read to avoid double-notifying on reload
|
||||||
|
let lastMessageDates = PS.prefs.logtimes || {};
|
||||||
|
if (!lastMessageDates[PS.server.id]) lastMessageDates[PS.server.id] = {};
|
||||||
|
lastMessageDates[PS.server.id][room.id] = room.lastMessageTime || 0;
|
||||||
|
PS.prefs.set('logtimes', lastMessageDates);
|
||||||
|
}
|
||||||
this.notifications = this.notifications.filter(notification => notification.noAutoDismiss);
|
this.notifications = this.notifications.filter(notification => notification.noAutoDismiss);
|
||||||
this.isSubtleNotifying = false;
|
this.isSubtleNotifying = false;
|
||||||
}
|
}
|
||||||
|
|
@ -926,6 +938,9 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
this.send(target);
|
this.send(target);
|
||||||
PS.leave(this.id);
|
PS.leave(this.id);
|
||||||
},
|
},
|
||||||
|
'receivepopup'(target) {
|
||||||
|
PS.alert(target);
|
||||||
|
},
|
||||||
'inopener,inparent'(target) {
|
'inopener,inparent'(target) {
|
||||||
// do this command in the popup opener
|
// do this command in the popup opener
|
||||||
let room = this.getParent();
|
let room = this.getParent();
|
||||||
|
|
@ -1187,10 +1202,103 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
||||||
}
|
}
|
||||||
this.add("||All PM windows cleared and closed.");
|
this.add("||All PM windows cleared and closed.");
|
||||||
},
|
},
|
||||||
|
'unpackhidden'() {
|
||||||
|
PS.prefs.set('nounlink', true);
|
||||||
|
this.add('||Locked/banned users\' chat messages: ON');
|
||||||
|
},
|
||||||
|
'packhidden'() {
|
||||||
|
PS.prefs.set('nounlink', false);
|
||||||
|
this.add('||Locked/banned users\' chat messages: HIDDEN');
|
||||||
|
},
|
||||||
|
'hl,highlight'(target) {
|
||||||
|
let highlights = PS.prefs.highlights || {};
|
||||||
|
if (target.includes(' ')) {
|
||||||
|
let targets = target.split(' ');
|
||||||
|
let subCmd = targets[0];
|
||||||
|
targets = targets.slice(1).join(' ').match(/([^,]+?({\d*,\d*})?)+/g) as string[];
|
||||||
|
// trim the targets to be safe
|
||||||
|
for (let i = 0, len = targets.length; i < len; i++) {
|
||||||
|
targets[i] = targets[i].replace(/\n/g, '').trim();
|
||||||
|
}
|
||||||
|
switch (subCmd) {
|
||||||
|
case 'add': case 'roomadd': {
|
||||||
|
let key = subCmd === 'roomadd' ? (PS.server.id + '#' + this.id) : 'global';
|
||||||
|
let highlightList = highlights[key] || [];
|
||||||
|
for (let i = 0, len = targets.length; i < len; i++) {
|
||||||
|
if (!targets[i]) continue;
|
||||||
|
if (/[\\^$*+?()|{}[\]]/.test(targets[i])) {
|
||||||
|
// Catch any errors thrown by newly added regular expressions so they don't break the entire highlight list
|
||||||
|
try {
|
||||||
|
new RegExp(targets[i]);
|
||||||
|
} catch (e: any) {
|
||||||
|
return this.add(`|error|${(e.message.substr(0, 28) === 'Invalid regular expression: ' ? e.message : 'Invalid regular expression: /' + targets[i] + '/: ' + e.message)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (highlightList.includes(targets[i])) {
|
||||||
|
return this.add(`|error|${targets[i]} is already on your highlights list.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highlights[key] = highlightList.concat(targets);
|
||||||
|
this.add(`||Now highlighting on ${(key === 'global' ? "(everywhere): " : "(in " + key + "): ")} ${highlights[key].join(', ')}`);
|
||||||
|
// We update the regex
|
||||||
|
ChatRoom.updateHighlightRegExp(highlights);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'delete': case 'roomdelete': {
|
||||||
|
let key = subCmd === 'roomdelete' ? (PS.server.id + '#' + this.id) : 'global';
|
||||||
|
let highlightList = highlights[key] || [];
|
||||||
|
let newHls: string[] = [];
|
||||||
|
for (let i = 0, len = highlightList.length; i < len; i++) {
|
||||||
|
if (!targets.includes(highlightList[i])) {
|
||||||
|
newHls.push(highlightList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
highlights[key] = newHls;
|
||||||
|
this.add(`||Now highlighting on ${(key === 'global' ? "(everywhere): " : "(in " + key + "): ")} ${highlights[key].join(', ')}`);
|
||||||
|
// We update the regex
|
||||||
|
ChatRoom.updateHighlightRegExp(highlights);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Wrong command
|
||||||
|
this.add('|error|Invalid /highlight command.');
|
||||||
|
this.handleSend('/help highlight'); // show help
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PS.prefs.set('highlights', highlights);
|
||||||
|
} else {
|
||||||
|
if (['clear', 'roomclear', 'clearall'].includes(target)) {
|
||||||
|
let key = (target === 'roomclear' ? (PS.server.id + '#' + this.id) : (target === 'clearall' ? '' : 'global'));
|
||||||
|
if (key) {
|
||||||
|
highlights[key] = [];
|
||||||
|
this.add(`||All highlights (${(key === 'global' ? "everywhere" : "in " + key)}) cleared.`);
|
||||||
|
ChatRoom.updateHighlightRegExp(highlights);
|
||||||
|
} else {
|
||||||
|
PS.prefs.set('highlights', null);
|
||||||
|
this.add("||All highlights (in all rooms and globally) cleared.");
|
||||||
|
ChatRoom.updateHighlightRegExp({});
|
||||||
|
}
|
||||||
|
} else if (['show', 'list', 'roomshow', 'roomlist'].includes(target)) {
|
||||||
|
// Shows a list of the current highlighting words
|
||||||
|
let key = target.startsWith('room') ? (PS.server.id + '#' + this.id) : 'global';
|
||||||
|
if (highlights[key] && highlights[key].length > 0) {
|
||||||
|
this.add(`||Current highlight list ${(key === 'global' ? "(everywhere): " : "(in " + key + "): ")}${highlights[key].join(", ")}`);
|
||||||
|
} else {
|
||||||
|
this.add(`||Your highlight list${(key === 'global' ? '' : ' in ' + key)} is empty.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Wrong command
|
||||||
|
this.add('|error|Invalid /highlight command.');
|
||||||
|
this.handleSend('/help highlight'); // show help
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
'senddirect'(target) {
|
'senddirect'(target) {
|
||||||
this.sendDirect(target);
|
this.sendDirect(target);
|
||||||
},
|
},
|
||||||
'help'(target) {
|
'h,help'(target) {
|
||||||
switch (toID(target)) {
|
switch (toID(target)) {
|
||||||
case 'chal':
|
case 'chal':
|
||||||
case 'chall':
|
case 'chall':
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,13 @@ export class ChatRoom extends PSRoom {
|
||||||
log: BattleLog | null = null;
|
log: BattleLog | null = null;
|
||||||
tour: ChatTournament | null = null;
|
tour: ChatTournament | null = null;
|
||||||
lastMessage: Args | null = null;
|
lastMessage: Args | null = null;
|
||||||
|
lastMessageTime: number | null = null;
|
||||||
|
|
||||||
joinLeave: { join: string[], leave: string[], messageId: string } | null = null;
|
joinLeave: { join: string[], leave: string[], messageId: string } | null = null;
|
||||||
/** in order from least to most recent */
|
/** in order from least to most recent */
|
||||||
userActivity: string[] = [];
|
userActivity: string[] = [];
|
||||||
timeOffset = 0;
|
timeOffset = 0;
|
||||||
|
static highlightRegExp: Record<string, RegExp | null> | null = null;
|
||||||
|
|
||||||
constructor(options: RoomOptions) {
|
constructor(options: RoomOptions) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
@ -179,21 +181,20 @@ export class ChatRoom extends PSRoom {
|
||||||
this.title = `[DM] ${nameWithGroup.trim()}`;
|
this.title = `[DM] ${nameWithGroup.trim()}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleHighlight = (message: string, name: string) => {
|
static getHighlight(message: string, roomid: string) {
|
||||||
if (!PS.prefs.noselfhighlight && PS.user.nameRegExp?.test(message)) {
|
let highlights = PS.prefs.highlights || {};
|
||||||
this.notify({
|
if (Array.isArray(highlights)) {
|
||||||
title: `Mentioned by ${name} in ${this.id}`,
|
highlights = { global: highlights };
|
||||||
body: `"${message}"`,
|
// Migrate from the old highlight system
|
||||||
id: 'highlight',
|
PS.prefs.set('highlights', highlights);
|
||||||
});
|
}
|
||||||
return true;
|
if (!PS.prefs.noselfhighlight && PS.user.nameRegExp) {
|
||||||
|
if (PS.user.nameRegExp?.test(message)) return true;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
// TODO!
|
|
||||||
if (!this.highlightRegExp) {
|
if (!this.highlightRegExp) {
|
||||||
try {
|
try {
|
||||||
//this.updateHighlightRegExp(highlights);
|
this.updateHighlightRegExp(highlights);
|
||||||
} catch (e) {
|
} catch {
|
||||||
// If the expression above is not a regexp, we'll get here.
|
// If the expression above is not a regexp, we'll get here.
|
||||||
// Don't throw an exception because that would prevent the chat
|
// Don't throw an exception because that would prevent the chat
|
||||||
// message from showing up, or, when the lobby is initialising,
|
// message from showing up, or, when the lobby is initialising,
|
||||||
|
|
@ -201,14 +202,60 @@ export class ChatRoom extends PSRoom {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var id = PS.server.id + '#' + this.id;
|
const id = PS.server.id + '#' + roomid;
|
||||||
var globalHighlightsRegExp = this.highlightRegExp['global'];
|
const globalHighlightsRegExp = this.highlightRegExp?.['global'];
|
||||||
var roomHighlightsRegExp = this.highlightRegExp[id];
|
const roomHighlightsRegExp = this.highlightRegExp?.[id];
|
||||||
|
return (((globalHighlightsRegExp?.test(message)) || (roomHighlightsRegExp?.test(message))));
|
||||||
return (((globalHighlightsRegExp &&
|
}
|
||||||
globalHighlightsRegExp.test(message)) ||
|
static updateHighlightRegExp(highlights: Record<string, string[]>) {
|
||||||
(roomHighlightsRegExp && roomHighlightsRegExp.test(message))));
|
// Enforce boundary for match sides, if a letter on match side is
|
||||||
*/
|
// a word character. For example, regular expression "a" matches
|
||||||
|
// "a", but not "abc", while regular expression "!" matches
|
||||||
|
// "!" and "!abc".
|
||||||
|
this.highlightRegExp = {};
|
||||||
|
for (let i in highlights) {
|
||||||
|
if (!highlights[i].length) {
|
||||||
|
this.highlightRegExp[i] = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
this.highlightRegExp[i] = new RegExp('(?:\\b|(?!\\w))(?:' + highlights[i].join('|') + ')(?:\\b|(?!\\w))', 'i');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleHighlight = (args: Args) => {
|
||||||
|
let name;
|
||||||
|
let message;
|
||||||
|
let msgTime = 0;
|
||||||
|
if (args[0] === 'c:') {
|
||||||
|
msgTime = parseInt(args[1]);
|
||||||
|
name = args[2];
|
||||||
|
message = args[3];
|
||||||
|
} else {
|
||||||
|
name = args[1];
|
||||||
|
message = args[2];
|
||||||
|
}
|
||||||
|
let lastMessageDates = Dex.prefs('logtimes') || (PS.prefs.set('logtimes', {}), Dex.prefs('logtimes'));
|
||||||
|
if (!lastMessageDates[PS.server.id]) lastMessageDates[PS.server.id] = {};
|
||||||
|
let 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
|
||||||
|
let serverMsgTime = msgTime - (this.timeOffset || 0);
|
||||||
|
let mayNotify = serverMsgTime > lastMessageDate && name !== PS.user.userid;
|
||||||
|
if (PS.isVisible(this)) {
|
||||||
|
this.lastMessageTime = null;
|
||||||
|
lastMessageDates[PS.server.id][this.id] = serverMsgTime;
|
||||||
|
PS.prefs.set('logtimes', lastMessageDates);
|
||||||
|
} else {
|
||||||
|
// To be saved on focus
|
||||||
|
let lastMessageTime = this.lastMessageTime || 0;
|
||||||
|
if (lastMessageTime < serverMsgTime) this.lastMessageTime = serverMsgTime;
|
||||||
|
}
|
||||||
|
if (ChatRoom.getHighlight(message, this.id)) {
|
||||||
|
if (mayNotify) this.notify({
|
||||||
|
title: `Mentioned by ${name} in ${this.id}`,
|
||||||
|
body: `"${message}"`,
|
||||||
|
id: 'highlight',
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
override clientCommands = this.parseClientCommands({
|
override clientCommands = this.parseClientCommands({
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user