2023-06-08 09:07:28 -04:00
|
|
|
import { html, Component } from "../lib/index.js";
|
|
|
|
import { BufferType, getBufferURL, getServerName } from "../state.js";
|
2023-06-14 05:52:54 -04:00
|
|
|
import * as irc from "../lib/irc.js";
|
2023-06-08 09:07:28 -04:00
|
|
|
|
|
|
|
class SwitcherItem extends Component {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.handleClick = this.handleClick.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
handleClick(event) {
|
|
|
|
event.preventDefault();
|
|
|
|
this.props.onClick();
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
let class_ = this.props.selected ? "selected" : "";
|
|
|
|
|
|
|
|
return html`
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href=${getBufferURL(this.props.buffer)}
|
|
|
|
class=${class_}
|
|
|
|
onClick=${this.handleClick}
|
|
|
|
>
|
|
|
|
<span class="server">
|
|
|
|
${getServerName(this.props.server, this.props.bouncerNetwork)}
|
|
|
|
</span>
|
|
|
|
${this.props.buffer.name}
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-14 05:52:54 -04:00
|
|
|
function matchString(s, query) {
|
|
|
|
return s.toLowerCase().includes(query) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function matchBuffer(buf, server, query) {
|
|
|
|
let score = 2 * matchString(buf.name, query);
|
|
|
|
switch (buf.type) {
|
|
|
|
case BufferType.CHANNEL:
|
|
|
|
score += matchString(buf.topic || "", query);
|
|
|
|
break;
|
|
|
|
case BufferType.NICK:
|
|
|
|
let user = server.users.get(buf.name);
|
|
|
|
if (user && user.realname && irc.isMeaningfulRealname(user.realname, buf.name)) {
|
|
|
|
score += matchString(user.realname, query);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return score;
|
|
|
|
}
|
|
|
|
|
2023-06-08 09:07:28 -04:00
|
|
|
export default class SwitcherForm extends Component {
|
|
|
|
state = {
|
|
|
|
query: "",
|
|
|
|
selected: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.handleInput = this.handleInput.bind(this);
|
|
|
|
this.handleSubmit = this.handleSubmit.bind(this);
|
|
|
|
this.handleKeyUp = this.handleKeyUp.bind(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
getSuggestions() {
|
|
|
|
let query = this.state.query.toLowerCase();
|
|
|
|
|
|
|
|
let l = [];
|
2023-06-14 05:52:54 -04:00
|
|
|
let scores = new Map();
|
2023-06-08 09:07:28 -04:00
|
|
|
for (let buf of this.props.buffers.values()) {
|
|
|
|
if (buf.type === BufferType.SERVER) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-06-14 05:52:54 -04:00
|
|
|
let score = 0;
|
|
|
|
if (query !== "") {
|
|
|
|
let server = this.props.servers.get(buf.server);
|
|
|
|
score = matchBuffer(buf, server, query);
|
|
|
|
if (!score) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-06-08 09:07:28 -04:00
|
|
|
}
|
2023-06-14 05:52:54 -04:00
|
|
|
scores.set(buf.id, score);
|
2023-06-08 09:07:28 -04:00
|
|
|
l.push(buf);
|
|
|
|
}
|
2023-06-14 05:52:54 -04:00
|
|
|
|
|
|
|
l.sort((a, b) => {
|
|
|
|
return scores.get(b.id) - scores.get(a.id);
|
|
|
|
});
|
|
|
|
|
|
|
|
return l.slice(0, 20);
|
2023-06-08 09:07:28 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
handleInput(event) {
|
|
|
|
let target = event.target;
|
|
|
|
this.setState({ [target.name]: target.value });
|
|
|
|
}
|
|
|
|
|
|
|
|
handleSubmit(event) {
|
|
|
|
event.preventDefault();
|
|
|
|
this.props.onSubmit(this.getSuggestions()[this.state.selected]);
|
|
|
|
}
|
|
|
|
|
|
|
|
handleKeyUp(event) {
|
|
|
|
switch (event.key) {
|
|
|
|
case "ArrowUp":
|
|
|
|
event.stopPropagation();
|
|
|
|
this.move(-1);
|
|
|
|
break;
|
|
|
|
case "ArrowDown":
|
|
|
|
event.stopPropagation();
|
|
|
|
this.move(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
move(delta) {
|
|
|
|
let numSuggestions = this.getSuggestions().length;
|
|
|
|
this.setState((state) => {
|
|
|
|
return {
|
|
|
|
selected: (state.selected + delta + numSuggestions) % numSuggestions,
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
let items = this.getSuggestions().map((buf, i) => {
|
|
|
|
let server = this.props.servers.get(buf.server);
|
|
|
|
|
|
|
|
let bouncerNetwork = null;
|
|
|
|
if (server.bouncerNetID) {
|
|
|
|
bouncerNetwork = this.props.bouncerNetworks.get(server.bouncerNetID);
|
|
|
|
}
|
|
|
|
|
|
|
|
return html`
|
|
|
|
<${SwitcherItem}
|
|
|
|
buffer=${buf}
|
|
|
|
server=${server}
|
|
|
|
bouncerNetwork=${bouncerNetwork}
|
|
|
|
selected=${this.state.selected === i}
|
|
|
|
onClick=${() => this.props.onSubmit(buf)}
|
|
|
|
/>
|
|
|
|
`;
|
|
|
|
});
|
|
|
|
|
|
|
|
return html`
|
|
|
|
<form
|
|
|
|
onInput=${this.handleInput}
|
|
|
|
onSubmit=${this.handleSubmit}
|
|
|
|
onKeyUp=${this.handleKeyUp}
|
|
|
|
>
|
|
|
|
<input
|
|
|
|
type="search"
|
|
|
|
name="query"
|
|
|
|
value=${this.state.query}
|
|
|
|
placeholder="Filter"
|
|
|
|
autocomplete="off"
|
|
|
|
autofocus
|
|
|
|
/>
|
|
|
|
<ul class="switcher-list">
|
|
|
|
${items}
|
|
|
|
</ul>
|
|
|
|
</form>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
}
|