mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-03-21 17:50:29 -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!"
|
||||
*/
|
||||
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) {
|
||||
this.elem = elem;
|
||||
|
||||
|
|
@ -177,7 +177,7 @@ export class BattleLog {
|
|||
}
|
||||
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);
|
||||
if (!noNotify && isHighlighted) {
|
||||
const notifyTitle = "Mentioned by " + name + " in " + (battle?.roomid || '');
|
||||
|
|
@ -266,6 +266,7 @@ export class BattleLog {
|
|||
|
||||
case 'unlink': {
|
||||
// |unlink| is deprecated in favor of |hidelines|
|
||||
if (window.PS.prefs.nounlink) return;
|
||||
const user = toID(args[2]) || toID(args[1]);
|
||||
this.unlinkChatFrom(user);
|
||||
if (args[2]) {
|
||||
|
|
@ -276,6 +277,7 @@ export class BattleLog {
|
|||
}
|
||||
|
||||
case 'hidelines': {
|
||||
if (window.PS.prefs.nounlink) return;
|
||||
const user = toID(args[2]);
|
||||
this.unlinkChatFrom(user);
|
||||
if (args[1] !== 'unlink') {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
import { PSConnection, PSLoginServer } from './client-connection';
|
||||
import { PSModel, PSStreamModel } from './client-core';
|
||||
import type { PSRoomPanel, PSRouter } from './panels';
|
||||
import type { ChatRoom } from './panel-chat';
|
||||
import { ChatRoom } from './panel-chat';
|
||||
import type { MainMenuRoom } from './panel-mainmenu';
|
||||
import { Dex, toID, type ID } from './battle-dex';
|
||||
import { BattleTextParser, type Args } from './battle-text-parser';
|
||||
|
|
@ -78,6 +78,7 @@ class PSPrefs extends PSStreamModel<string | null> {
|
|||
hidelinks: false,
|
||||
hideinterstice: true,
|
||||
};
|
||||
nounlink: boolean | null = null;
|
||||
|
||||
/* Battle preferences */
|
||||
ignorenicks: boolean | null = null;
|
||||
|
|
@ -118,6 +119,9 @@ class PSPrefs extends PSStreamModel<string | null> {
|
|||
|
||||
afd: boolean | 'sprites' = false;
|
||||
|
||||
highlights: Record<string, string[]> | null = null;
|
||||
logtimes: Record<string, { [roomid: RoomID]: number }> | null = null;
|
||||
|
||||
// PREFS END HERE
|
||||
|
||||
storageEngine: 'localStorage' | 'iframeLocalStorage' | '' = '';
|
||||
|
|
@ -830,6 +834,14 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
|||
PS.update();
|
||||
}
|
||||
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.isSubtleNotifying = false;
|
||||
}
|
||||
|
|
@ -926,6 +938,9 @@ export class PSRoom extends PSStreamModel<Args | null> implements RoomOptions {
|
|||
this.send(target);
|
||||
PS.leave(this.id);
|
||||
},
|
||||
'receivepopup'(target) {
|
||||
PS.alert(target);
|
||||
},
|
||||
'inopener,inparent'(target) {
|
||||
// do this command in the popup opener
|
||||
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.");
|
||||
},
|
||||
'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) {
|
||||
this.sendDirect(target);
|
||||
},
|
||||
'help'(target) {
|
||||
'h,help'(target) {
|
||||
switch (toID(target)) {
|
||||
case 'chal':
|
||||
case 'chall':
|
||||
|
|
|
|||
|
|
@ -47,11 +47,13 @@ export class ChatRoom extends PSRoom {
|
|||
log: BattleLog | null = null;
|
||||
tour: ChatTournament | null = null;
|
||||
lastMessage: Args | null = null;
|
||||
lastMessageTime: number | null = null;
|
||||
|
||||
joinLeave: { join: string[], leave: string[], messageId: string } | null = null;
|
||||
/** in order from least to most recent */
|
||||
userActivity: string[] = [];
|
||||
timeOffset = 0;
|
||||
static highlightRegExp: Record<string, RegExp | null> | null = null;
|
||||
|
||||
constructor(options: RoomOptions) {
|
||||
super(options);
|
||||
|
|
@ -179,21 +181,20 @@ export class ChatRoom extends PSRoom {
|
|||
this.title = `[DM] ${nameWithGroup.trim()}`;
|
||||
}
|
||||
}
|
||||
handleHighlight = (message: string, name: string) => {
|
||||
if (!PS.prefs.noselfhighlight && PS.user.nameRegExp?.test(message)) {
|
||||
this.notify({
|
||||
title: `Mentioned by ${name} in ${this.id}`,
|
||||
body: `"${message}"`,
|
||||
id: 'highlight',
|
||||
});
|
||||
return true;
|
||||
static getHighlight(message: string, roomid: string) {
|
||||
let highlights = PS.prefs.highlights || {};
|
||||
if (Array.isArray(highlights)) {
|
||||
highlights = { global: highlights };
|
||||
// Migrate from the old highlight system
|
||||
PS.prefs.set('highlights', highlights);
|
||||
}
|
||||
if (!PS.prefs.noselfhighlight && PS.user.nameRegExp) {
|
||||
if (PS.user.nameRegExp?.test(message)) return true;
|
||||
}
|
||||
/*
|
||||
// TODO!
|
||||
if (!this.highlightRegExp) {
|
||||
try {
|
||||
//this.updateHighlightRegExp(highlights);
|
||||
} catch (e) {
|
||||
this.updateHighlightRegExp(highlights);
|
||||
} catch {
|
||||
// If the expression above is not a regexp, we'll get here.
|
||||
// Don't throw an exception because that would prevent the chat
|
||||
// message from showing up, or, when the lobby is initialising,
|
||||
|
|
@ -201,14 +202,60 @@ export class ChatRoom extends PSRoom {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
var id = PS.server.id + '#' + this.id;
|
||||
var globalHighlightsRegExp = this.highlightRegExp['global'];
|
||||
var roomHighlightsRegExp = this.highlightRegExp[id];
|
||||
|
||||
return (((globalHighlightsRegExp &&
|
||||
globalHighlightsRegExp.test(message)) ||
|
||||
(roomHighlightsRegExp && roomHighlightsRegExp.test(message))));
|
||||
*/
|
||||
const id = PS.server.id + '#' + roomid;
|
||||
const globalHighlightsRegExp = this.highlightRegExp?.['global'];
|
||||
const roomHighlightsRegExp = this.highlightRegExp?.[id];
|
||||
return (((globalHighlightsRegExp?.test(message)) || (roomHighlightsRegExp?.test(message))));
|
||||
}
|
||||
static updateHighlightRegExp(highlights: Record<string, string[]>) {
|
||||
// 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;
|
||||
};
|
||||
override clientCommands = this.parseClientCommands({
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user