Support room search in rooms panel

This commit is contained in:
Guangcong Luo
2019-03-22 13:53:56 +09:00
parent 087c030c2b
commit c70a54f4e6
2 changed files with 108 additions and 6 deletions

View File

@@ -5,7 +5,7 @@
* @license AGPLv3
*/
type RoomInfo = {title: string, desc: string, userCount: number, subRooms?: string[]};
type RoomInfo = {title: string, desc?: string, userCount?: number, subRooms?: string[]};
class MainMenuRoom extends PSRoom {
readonly classType: string = 'mainmenu';

View File

@@ -15,6 +15,8 @@ class RoomsRoom extends PSRoom {
class RoomsPanel extends PSRoomPanel {
hidden = false;
search = '';
lastKeyCode = 0;
componentDidMount() {
super.componentDidMount();
this.subscriptions.push(PS.user.subscribe(() => {
@@ -28,12 +30,107 @@ class RoomsPanel extends PSRoomPanel {
this.forceUpdate();
PS.update();
};
changeSearch = (e: Event) => {
const target = (e.currentTarget as HTMLInputElement);
if (target.selectionStart !== target.selectionEnd) return;
this.search = target.value;
this.forceUpdate();
};
keyDownSearch = (e: KeyboardEvent) => {
this.lastKeyCode = e.keyCode;
if (e.keyCode === 13) {
const target = (e.currentTarget as HTMLInputElement);
let value = target.value;
const arrowIndex = value.indexOf(' \u21d2 ');
if (arrowIndex >= 0) value = value.slice(arrowIndex + 3);
if (!/^[a-z0-9-]$/.test(value)) value = toId(value);
e.preventDefault();
e.stopImmediatePropagation();
target.value = '';
PS.join(value as RoomID);
}
};
runSearch() {
const searchid = toId(this.search);
let exactMatch = false;
const rooms = PS.mainmenu.roomsCache;
let roomList = [...(rooms.official || []), ...(rooms.pspl || []), ...(rooms.chat || [])];
for (const room of roomList) {
if (!room.subRooms) continue;
for (const title of room.subRooms) {
roomList.push({
title,
desc: `Subroom of ${room.title}`,
});
}
}
let start = roomList.filter(room => {
const titleid = toId(room.title);
if (titleid === searchid) exactMatch = true;
return titleid.startsWith(searchid) ||
toId(room.title.replace(/^The /, '')).startsWith(searchid);
});
roomList = roomList.filter(room => !start.includes(room));
let abbr = roomList.filter(room =>
toId(room.title.toLowerCase().replace(/\b([a-z0-9])[a-z0-9]*\b/g, '$1')).startsWith(searchid) ||
room.title.replace(/[^A-Z0-9]+/g, '').toLowerCase().startsWith(searchid)
);
const hidden = !exactMatch ? [{title: this.search, desc: "(Private room?)"}] : [];
const autoFill = this.lastKeyCode !== 127 && this.lastKeyCode >= 32;
if (autoFill) {
const firstTitle = (start[0] || abbr[0] || hidden[0]).title;
let firstTitleOffset = 0;
while (
searchid !== toId(firstTitle.slice(0, firstTitleOffset)) &&
firstTitleOffset < firstTitle.length // should never happen, but sanity against infinite loop
) {
firstTitleOffset++;
}
let autoFillValue = firstTitle.slice(firstTitleOffset);
if (!autoFillValue && toId(firstTitle) !== searchid) {
autoFillValue = ' \u21d2 ' + firstTitle;
}
const oldSearch = this.search;
const searchElem = this.base!.querySelector('input[type=search]') as HTMLInputElement;
searchElem.value = oldSearch + autoFillValue;
searchElem.setSelectionRange(oldSearch.length, oldSearch.length + autoFillValue.length);
}
return {start, abbr, hidden};
}
focus() {
(this.base!.querySelector('input[type=search]') as HTMLInputElement).focus();
}
render() {
if (this.hidden && PS.isVisible(this.props.room)) this.hidden = false;
if (this.hidden) {
return <PSPanelWrapper room={this.props.room} scrollable>{null}</PSPanelWrapper>;
}
const rooms = PS.mainmenu.roomsCache;
let roomList;
if (this.search) {
const search = this.runSearch();
roomList = [
this.renderRoomList("Search results", search.start),
this.renderRoomList("Search results (acronym)", search.abbr),
this.renderRoomList("Possible hidden room", search.hidden),
];
} else {
roomList = [
this.renderRoomList("Official chat rooms", rooms.official),
this.renderRoomList("PSPL winner", rooms.pspl),
this.renderRoomList("Chat rooms", rooms.chat),
];
}
return <PSPanelWrapper room={this.props.room} scrollable><div class="pad">
<button class="button" style="float:right;font-size:10pt;margin-top:3px" onClick={this.hide}>
<i class="fa fa-caret-right"></i> Hide
@@ -56,10 +153,15 @@ class RoomsPanel extends PSRoomPanel {
title="Meloetta is PS's mascot! The Pirouette forme is Fighting-type, and represents our battles."
></span>
</div>
<div>
<input
type="search" name="roomsearch" class="textbox" style="width: 100%; max-width: 480px"
placeholder="Join or search for rooms"
onInput={this.changeSearch} onKeyDown={this.keyDownSearch}
/>
</div>
{rooms.userCount === undefined && <h2>Connecting...</h2>}
{this.renderRoomList("Official chat rooms", rooms.official)}
{this.renderRoomList("PSPL winner", rooms.pspl)}
{this.renderRoomList("Chat rooms", rooms.chat)}
{roomList}
</div></PSPanelWrapper>;
}
renderRoomList(title: string, rooms?: RoomInfo[]) {
@@ -68,9 +170,9 @@ class RoomsPanel extends PSRoomPanel {
<h2>{title}</h2>
{rooms.map(roomInfo => <div>
<a href={`/${toId(roomInfo.title)}`} class="ilink">
<small style="float:right">({roomInfo.userCount} users)</small>
{roomInfo.userCount !== undefined && <small style="float:right">({roomInfo.userCount} users)</small>}
<strong><i class="fa fa-comment-o"></i> {roomInfo.title}<br /></strong>
<small>{roomInfo.desc}</small>
<small>{roomInfo.desc || ''}</small>
{roomInfo.subRooms && <small><br />
<i class="fa fa-level-up fa-rotate-90"></i> Subrooms: <strong>
{roomInfo.subRooms.map((roomName, i) => [