Compare commits
24 Commits
v1.0.0-bet
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 77ccba04ba | |||
| e68534a846 | |||
| 5b96dd0d8b | |||
| 4c245ad49e | |||
|
|
bdfde7f1e0 | ||
|
|
223b9b1531 | ||
|
|
8aae4a2b07 | ||
|
|
910ce284a2 | ||
|
|
1e630aed0d | ||
|
|
b39b46fa12 | ||
|
|
65a0a34fa1 | ||
|
|
642e90f51c | ||
|
|
64c2325db8 | ||
|
|
700919b5c4 | ||
|
|
e91c246a95 | ||
|
|
5b7459f24d | ||
|
|
af3a255824 | ||
|
|
7e785ed101 | ||
|
|
02cc554df6 | ||
|
|
6aef3e906b | ||
|
|
9879dbc722 | ||
|
|
afbd7c0bb3 | ||
|
|
e09541ad2f | ||
|
|
6905b9d768 |
@@ -11,7 +11,7 @@ artifacts:
|
||||
tasks:
|
||||
- setup: |
|
||||
cd gamja
|
||||
npm install --include=dev
|
||||
npm clean-install --include=dev
|
||||
- build: |
|
||||
cd gamja
|
||||
npm run build
|
||||
|
||||
39
.gitea/workflows/build.yml
Normal file
39
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Bun Package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout código
|
||||
run: |
|
||||
git clone https://fedesrv.ddns.net/git/${{ github.repository }}.git .
|
||||
git checkout ${{ github.sha }}
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install deps
|
||||
run: bun install
|
||||
|
||||
- name: Build gamja
|
||||
run: bun build ./index.html --outdir ./dist --minify
|
||||
|
||||
- name: Generar fecha
|
||||
run: echo "BUILD_DATE=$(date +'%Y-%m-%d_%H-%M')" >> $GITHUB_ENV
|
||||
|
||||
- name: Upload dist
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: gamja-dist-${{ env.BUILD_DATE }}
|
||||
path: dist
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -220,6 +220,7 @@ export default class App extends Component {
|
||||
this.handleAddNetworkClick = this.handleAddNetworkClick.bind(this);
|
||||
this.handleNetworkSubmit = this.handleNetworkSubmit.bind(this);
|
||||
this.handleNetworkRemove = this.handleNetworkRemove.bind(this);
|
||||
this.showError = this.showError.bind(this);
|
||||
this.handleDismissError = this.handleDismissError.bind(this);
|
||||
this.handleAuthSubmit = this.handleAuthSubmit.bind(this);
|
||||
this.handleRegisterSubmit = this.handleRegisterSubmit.bind(this);
|
||||
@@ -667,12 +668,6 @@ export default class App extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
addChatMessage(serverID, bufName, msg) {
|
||||
this.prepareChatMessage(serverID, msg);
|
||||
let bufID = { server: serverID, name: bufName };
|
||||
this.setState((state) => State.addMessage(state, msg, bufID));
|
||||
}
|
||||
|
||||
handleChatMessage(serverID, bufName, msg) {
|
||||
let client = this.clients.get(serverID);
|
||||
|
||||
@@ -1800,7 +1795,12 @@ export default class App extends Component {
|
||||
}
|
||||
|
||||
for (let msg of result.messages) {
|
||||
this.addChatMessage(buf.server, buf.name, msg);
|
||||
this.prepareChatMessage(buf.server, msg);
|
||||
let destBuffers = this.routeMessage(buf.server, msg);
|
||||
for (let bufName of destBuffers) {
|
||||
let bufID = { server: buf.server, name: bufName };
|
||||
this.setState((state) => State.addMessage(state, msg, bufID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2072,7 +2072,7 @@ export default class App extends Component {
|
||||
}
|
||||
|
||||
bufferHeader = html`
|
||||
<section id="buffer-header">
|
||||
<section id="buffer-header" role="banner">
|
||||
<${BufferHeader}
|
||||
buffer=${activeBuffer}
|
||||
server=${activeServer}
|
||||
@@ -2094,8 +2094,10 @@ export default class App extends Component {
|
||||
if (activeBuffer && activeBuffer.type === BufferType.CHANNEL) {
|
||||
memberList = html`
|
||||
<section
|
||||
id="member-list"
|
||||
class=${this.state.openPanels.memberList ? "expand" : ""}
|
||||
id="member-list"
|
||||
class=${this.state.openPanels.memberList ? "expand" : ""}
|
||||
role="complementary"
|
||||
aria-label="Members list"
|
||||
>
|
||||
<button
|
||||
class="expander"
|
||||
@@ -2222,7 +2224,7 @@ export default class App extends Component {
|
||||
let error = null;
|
||||
if (this.state.error) {
|
||||
error = html`
|
||||
<div id="error-msg">
|
||||
<div id="error-msg" role="alert">
|
||||
${this.state.error}
|
||||
${" "}
|
||||
<button onClick=${this.handleDismissError}>×</button>
|
||||
@@ -2246,8 +2248,8 @@ export default class App extends Component {
|
||||
|
||||
let app = html`
|
||||
<section
|
||||
id="buffer-list"
|
||||
class=${this.state.openPanels.bufferList ? "expand" : ""}
|
||||
id="buffer-list"
|
||||
class=${this.state.openPanels.bufferList ? "expand" : ""}
|
||||
>
|
||||
<${BufferList}
|
||||
buffers=${this.state.buffers}
|
||||
@@ -2272,7 +2274,7 @@ export default class App extends Component {
|
||||
scrollKey=${this.state.activeBuffer}
|
||||
onScrollTop=${this.handleBufferScrollTop}
|
||||
>
|
||||
<section id="buffer" ref=${this.buffer} tabindex="-1">
|
||||
<section id="buffer" ref=${this.buffer} tabindex="-1" role="log">
|
||||
<${Buffer}
|
||||
buffer=${activeBuffer}
|
||||
server=${activeServer}
|
||||
@@ -2291,6 +2293,7 @@ export default class App extends Component {
|
||||
client=${activeClient}
|
||||
readOnly=${composerReadOnly}
|
||||
onSubmit=${this.handleComposerSubmit}
|
||||
onError=${this.showError}
|
||||
autocomplete=${this.autocomplete}
|
||||
commandOnly=${commandOnly}
|
||||
maxLen=${privmsgMaxLen}
|
||||
|
||||
@@ -47,7 +47,7 @@ function BufferItem(props) {
|
||||
}
|
||||
|
||||
return html`
|
||||
<li class="${classes.join(" ")}">
|
||||
<li class="${classes.join(" ")}" role="tab" aria-selected="${props.active}">
|
||||
<a
|
||||
href=${getBufferURL(props.buffer)}
|
||||
title=${title}
|
||||
@@ -80,5 +80,9 @@ export default function BufferList(props) {
|
||||
`;
|
||||
});
|
||||
|
||||
return html`<ul>${items}</ul>`;
|
||||
return html`
|
||||
<ul role="tablist" aria-label="Buffer list">
|
||||
${items}
|
||||
</ul>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ function _Timestamp({ date, url, showSeconds }) {
|
||||
if (showSeconds) {
|
||||
timestamp += ":--";
|
||||
}
|
||||
return html`<span class="timestamp">${timestamp}</span>`;
|
||||
return html`<span class="timestamp" aria-hidden="true">${timestamp}</span>`;
|
||||
}
|
||||
|
||||
let hh = date.getHours().toString().padStart(2, "0");
|
||||
@@ -154,7 +154,13 @@ class LogLine extends Component {
|
||||
content = html`${linkify(stripANSI(text), onChannelClick)}`;
|
||||
lineClass += " talk";
|
||||
}
|
||||
content = html`<span class="nick-caret">${prefix}</span>${createNick(msg.prefix.name)}<span class="nick-caret">${suffix}</span> ${content}`;
|
||||
content = html`
|
||||
<span class="nick-caret" aria-hidden="true">${prefix}</span>
|
||||
${createNick(msg.prefix.name)}
|
||||
<span class="nick-caret" aria-hidden="true">${suffix}</span>
|
||||
${" "}
|
||||
${content}
|
||||
`;
|
||||
}
|
||||
|
||||
let allowedPrefixes = server.statusMsg;
|
||||
@@ -380,7 +386,7 @@ class LogLine extends Component {
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="logline ${lineClass}" data-key=${msg.key}>
|
||||
<div class="logline ${lineClass}" data-key=${msg.key} role="listitem">
|
||||
<${Timestamp} date=${new Date(msg.tags.time)} url=${getMessageURL(buf, msg)}/>
|
||||
${" "}
|
||||
${content}
|
||||
@@ -505,7 +511,7 @@ class FoldGroup extends Component {
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="logline" data-key=${msgs[0].key}>
|
||||
<div class="logline" data-key=${msgs[0].key} role="listitem">
|
||||
${timestamp}
|
||||
${" "}
|
||||
${content}
|
||||
@@ -558,7 +564,7 @@ class NotificationNagger extends Component {
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="logline">
|
||||
<div class="logline nag" role="listitem">
|
||||
<${Timestamp}/>
|
||||
${" "}
|
||||
<a href="#" onClick=${this.handleClick}>Turn on desktop notifications</a> to get notified about new messages
|
||||
@@ -599,7 +605,7 @@ class ProtocolHandlerNagger extends Component {
|
||||
}
|
||||
let name = this.props.bouncerName || "this bouncer";
|
||||
return html`
|
||||
<div class="logline">
|
||||
<div class="logline nag" role="listitem">
|
||||
<${Timestamp}/>
|
||||
${" "}
|
||||
<a href="#" onClick=${this.handleClick}>Register our protocol handler</a> to open IRC links with ${name}
|
||||
@@ -637,7 +643,7 @@ function AccountNagger({ server, onAuthClick, onRegisterClick }) {
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="logline">
|
||||
<div class="logline nag" role="listitem">
|
||||
<${Timestamp}/> ${msg}
|
||||
</div>
|
||||
`;
|
||||
@@ -656,7 +662,7 @@ class DateSeparator extends Component {
|
||||
let date = this.props.date;
|
||||
let text = date.toLocaleDateString([], { year: "numeric", month: "2-digit", day: "2-digit" });
|
||||
return html`
|
||||
<div class="separator date-separator">
|
||||
<div class="separator date-separator" role="separator">
|
||||
${text}
|
||||
</div>
|
||||
`;
|
||||
@@ -664,7 +670,7 @@ class DateSeparator extends Component {
|
||||
}
|
||||
|
||||
function UnreadSeparator(props) {
|
||||
return html`<div class="separator unread-separator">New messages</div>`;
|
||||
return html`<div class="separator unread-separator" role="separator">New messages</div>`;
|
||||
}
|
||||
|
||||
function sameDate(d1, d2) {
|
||||
@@ -841,7 +847,7 @@ export default class Buffer extends Component {
|
||||
children.push(createFoldGroup(foldMessages));
|
||||
|
||||
return html`
|
||||
<div class="logline-list">
|
||||
<div class="logline-list" role="list">
|
||||
${children}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -185,7 +185,13 @@ export default class Composer extends Component {
|
||||
promises.push(this.uploadFile(file));
|
||||
}
|
||||
|
||||
let urls = await Promise.all(promises);
|
||||
let urls;
|
||||
try {
|
||||
urls = await Promise.all(promises);
|
||||
} catch (err) {
|
||||
this.props.onError(new Error("Failed to upload files", { cause: err }));
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState((state) => {
|
||||
if (state.text) {
|
||||
|
||||
@@ -47,11 +47,11 @@ export default class Dialog extends Component {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="dialog" onClick=${this.handleBackdropClick}>
|
||||
<div class="dialog" onClick=${this.handleBackdropClick} role="dialog" aria-modal="true">
|
||||
<div class="dialog-body" ref=${this.body}>
|
||||
<div class="dialog-header">
|
||||
<h2>${this.props.title}</h2>
|
||||
<button class="dialog-close" onClick=${this.handleCloseClick}>×</button>
|
||||
<button class="dialog-close" onClick=${this.handleCloseClick} title="Close">×</button>
|
||||
</div>
|
||||
${this.props.children}
|
||||
</div>
|
||||
|
||||
@@ -65,7 +65,7 @@ export default class SwitcherForm extends Component {
|
||||
|
||||
this.handleInput = this.handleInput.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleKeyUp = this.handleKeyUp.bind(this);
|
||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||
}
|
||||
|
||||
getSuggestions() {
|
||||
@@ -106,7 +106,7 @@ export default class SwitcherForm extends Component {
|
||||
this.props.onSubmit(this.getSuggestions()[this.state.selected]);
|
||||
}
|
||||
|
||||
handleKeyUp(event) {
|
||||
handleKeyDown(event) {
|
||||
switch (event.key) {
|
||||
case "ArrowUp":
|
||||
event.stopPropagation();
|
||||
@@ -152,7 +152,7 @@ export default class SwitcherForm extends Component {
|
||||
<form
|
||||
onInput=${this.handleInput}
|
||||
onSubmit=${this.handleSubmit}
|
||||
onKeyUp=${this.handleKeyUp}
|
||||
onKeyDown=${this.handleKeyDown}
|
||||
>
|
||||
<input
|
||||
type="search"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import globals from "globals";
|
||||
import js from "@eslint/js";
|
||||
import stylisticJs from "@stylistic/eslint-plugin-js";
|
||||
import stylistic from "@stylistic/eslint-plugin";
|
||||
|
||||
export default [
|
||||
{
|
||||
@@ -14,7 +14,7 @@ export default [
|
||||
"process": "readonly",
|
||||
},
|
||||
},
|
||||
plugins: { "@stylistic/js": stylisticJs },
|
||||
plugins: { "@stylistic": stylistic },
|
||||
rules: {
|
||||
"no-case-declarations": "off",
|
||||
"no-unused-vars": ["error", {
|
||||
@@ -35,22 +35,22 @@ export default [
|
||||
"object-shorthand": "warn",
|
||||
"curly": "warn",
|
||||
"camelcase": "warn",
|
||||
"@stylistic/js/indent": ["warn", "tab"],
|
||||
"@stylistic/js/quotes": ["warn", "double"],
|
||||
"@stylistic/js/semi": "warn",
|
||||
"@stylistic/js/brace-style": ["warn", "1tbs"],
|
||||
"@stylistic/js/comma-dangle": ["warn", "always-multiline"],
|
||||
"@stylistic/js/comma-spacing": "warn",
|
||||
"@stylistic/js/arrow-parens": "warn",
|
||||
"@stylistic/js/arrow-spacing": "warn",
|
||||
"@stylistic/js/block-spacing": "warn",
|
||||
"@stylistic/js/object-curly-spacing": ["warn", "always"],
|
||||
"@stylistic/js/object-curly-newline": ["warn", {
|
||||
"@stylistic/indent": ["warn", "tab", { SwitchCase: 0 }],
|
||||
"@stylistic/quotes": ["warn", "double"],
|
||||
"@stylistic/semi": "warn",
|
||||
"@stylistic/brace-style": ["warn", "1tbs"],
|
||||
"@stylistic/comma-dangle": ["warn", "always-multiline"],
|
||||
"@stylistic/comma-spacing": "warn",
|
||||
"@stylistic/arrow-parens": "warn",
|
||||
"@stylistic/arrow-spacing": "warn",
|
||||
"@stylistic/block-spacing": "warn",
|
||||
"@stylistic/object-curly-spacing": ["warn", "always"],
|
||||
"@stylistic/object-curly-newline": ["warn", {
|
||||
multiline: true,
|
||||
consistent: true,
|
||||
}],
|
||||
"@stylistic/js/array-bracket-spacing": ["warn", "never"],
|
||||
"@stylistic/js/array-bracket-newline": ["warn", "consistent"],
|
||||
"@stylistic/array-bracket-spacing": ["warn", "never"],
|
||||
"@stylistic/array-bracket-newline": ["warn", "consistent"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<title>gamja IRC client</title>
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<script type="module" src="./main.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content">
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -32,9 +32,33 @@ const RECONNECT_MAX_DELAY_MSEC = 10 * 60 * 1000; // 10min
|
||||
|
||||
// WebSocket status codes
|
||||
// https://www.rfc-editor.org/rfc/rfc6455.html#section-7.4.1
|
||||
const NORMAL_CLOSURE = 1000;
|
||||
const GOING_AWAY = 1001;
|
||||
const UNSUPPORTED_DATA = 1003;
|
||||
const WEBSOCKET_CLOSE_CODES = {
|
||||
NORMAL_CLOSURE: 1000,
|
||||
GOING_AWAY: 1001,
|
||||
PROTOCOL_ERROR: 1002,
|
||||
UNSUPPORTED_DATA: 1003,
|
||||
NO_STATUS_CODE: 1005,
|
||||
ABNORMAL_CLOSURE: 1006,
|
||||
INVALID_FRAME_PAYLOAD_DATA: 1007,
|
||||
POLICY_VIOLATION: 1008,
|
||||
MESSAGE_TOO_BIG: 1009,
|
||||
MISSING_MANDATORY_EXT: 1010,
|
||||
INTERNAL_SERVER_ERROR: 1011,
|
||||
TLS_HANDSHAKE_FAILED: 1015,
|
||||
};
|
||||
const WEBSOCKET_CLOSE_CODE_NAMES = {
|
||||
[WEBSOCKET_CLOSE_CODES.GOING_AWAY]: "going away",
|
||||
[WEBSOCKET_CLOSE_CODES.PROTOCOL_ERROR]: "protocol error",
|
||||
[WEBSOCKET_CLOSE_CODES.UNSUPPORTED_DATA]: "unsupported data",
|
||||
[WEBSOCKET_CLOSE_CODES.NO_STATUS_CODE]: "no status code received",
|
||||
[WEBSOCKET_CLOSE_CODES.ABNORMAL_CLOSURE]: "abnormal closure",
|
||||
[WEBSOCKET_CLOSE_CODES.INVALID_FRAME_PAYLOAD_DATA]: "invalid frame payload data",
|
||||
[WEBSOCKET_CLOSE_CODES.POLICY_VIOLATION]: "policy violation",
|
||||
[WEBSOCKET_CLOSE_CODES.MESSAGE_TOO_BIG]: "message too big",
|
||||
[WEBSOCKET_CLOSE_CODES.MISSING_MANDATORY_EXT]: "missing mandatory extension",
|
||||
[WEBSOCKET_CLOSE_CODES.INTERNAL_SERVER_ERROR]: "internal server error",
|
||||
[WEBSOCKET_CLOSE_CODES.TLS_HANDSHAKE_FAILED]: "TLS handshake failed",
|
||||
};
|
||||
|
||||
// See https://github.com/quakenet/snircd/blob/master/doc/readme.who
|
||||
// Sorted by order of appearance in RPL_WHOSPCRPL
|
||||
@@ -69,6 +93,18 @@ class IRCError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
class WebSocketError extends Error {
|
||||
constructor(code) {
|
||||
let text = "Connection error";
|
||||
let name = WEBSOCKET_CLOSE_CODE_NAMES[code];
|
||||
if (name) {
|
||||
text += " (" + name + ")";
|
||||
}
|
||||
|
||||
super(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements a simple exponential backoff.
|
||||
*/
|
||||
@@ -189,8 +225,8 @@ export default class Client extends EventTarget {
|
||||
this.ws.addEventListener("close", (event) => {
|
||||
console.log("Connection closed (code: " + event.code + ")");
|
||||
|
||||
if (event.code !== NORMAL_CLOSURE && event.code !== GOING_AWAY) {
|
||||
this.dispatchError(new Error("Connection error"));
|
||||
if (event.code !== WEBSOCKET_CLOSE_CODES.NORMAL_CLOSURE && event.code !== WEBSOCKET_CLOSE_CODES.GOING_AWAY) {
|
||||
this.dispatchError(new WebSocketError(event.code));
|
||||
}
|
||||
|
||||
this.ws = null;
|
||||
@@ -237,7 +273,7 @@ export default class Client extends EventTarget {
|
||||
this.setPingInterval(0);
|
||||
|
||||
if (this.ws) {
|
||||
this.ws.close(NORMAL_CLOSURE);
|
||||
this.ws.close(WEBSOCKET_CLOSE_CODES.NORMAL_CLOSURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +333,7 @@ export default class Client extends EventTarget {
|
||||
handleMessage(event) {
|
||||
if (typeof event.data !== "string") {
|
||||
console.error("Received unsupported data type:", event.data);
|
||||
this.ws.close(UNSUPPORTED_DATA);
|
||||
this.ws.close(WEBSOCKET_CLOSE_CODES.UNSUPPORTED_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ import { h } from "../node_modules/preact/dist/preact.module.js";
|
||||
import htm from "../node_modules/htm/dist/htm.module.js";
|
||||
export const html = htm.bind(h);
|
||||
|
||||
import * as linkifyjs from "../node_modules/linkifyjs/dist/linkify.es.js";
|
||||
import * as linkifyjs from "../node_modules/linkifyjs/dist/linkify.mjs";
|
||||
export { linkifyjs };
|
||||
|
||||
2352
package-lock.json
generated
2352
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,9 @@
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@parcel/packager-raw-url": "^2.0.0",
|
||||
"@parcel/transformer-webmanifest": "^2.0.0",
|
||||
"@stylistic/eslint-plugin-js": "^4.2.0",
|
||||
"@stylistic/eslint-plugin": "^5.1.0",
|
||||
"eslint": "^9.11.1",
|
||||
"globals": "^16.0.0",
|
||||
"globals": "^17.0.0",
|
||||
"node-static": "^0.7.11",
|
||||
"parcel": "^2.0.0",
|
||||
"split": "^1.0.1",
|
||||
|
||||
19
state.js
19
state.js
@@ -611,11 +611,12 @@ export const State = {
|
||||
if (buf.server !== serverID) {
|
||||
return;
|
||||
}
|
||||
if (!buf.members.has(msg.prefix.name)) {
|
||||
let membership = members.get(msg.prefix.name);
|
||||
if (membership === undefined) {
|
||||
return;
|
||||
}
|
||||
let members = new irc.CaseMapMap(buf.members);
|
||||
members.set(newNick, members.get(msg.prefix.name));
|
||||
members.set(newNick, membership);
|
||||
members.delete(msg.prefix.name);
|
||||
buffers.set(buf.id, { ...buf, members });
|
||||
});
|
||||
@@ -670,12 +671,16 @@ export const State = {
|
||||
let members = new irc.CaseMapMap(buf.members);
|
||||
|
||||
irc.forEachChannelModeUpdate(msg, client.isupport, (mode, add, arg) => {
|
||||
if (prefixByMode.has(mode)) {
|
||||
let nick = arg;
|
||||
let membership = members.get(nick);
|
||||
let letter = prefixByMode.get(mode);
|
||||
members.set(nick, updateMembership(membership, letter, add, client));
|
||||
if (!prefixByMode.has(mode)) {
|
||||
return;
|
||||
}
|
||||
let nick = arg;
|
||||
let membership = members.get(nick);
|
||||
if (membership === undefined) {
|
||||
return;
|
||||
}
|
||||
let letter = prefixByMode.get(mode);
|
||||
members.set(nick, updateMembership(membership, letter, add, client));
|
||||
});
|
||||
|
||||
return { members };
|
||||
|
||||
@@ -359,6 +359,9 @@ form input[type="search"] {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
form label input[type="checkbox"], form label input[type="radio"] {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--green);
|
||||
@@ -396,7 +399,7 @@ details summary[role="button"] {
|
||||
white-space: pre-wrap;
|
||||
overflow: auto;
|
||||
}
|
||||
#buffer .talk, #buffer .motd {
|
||||
#buffer .talk, #buffer .motd, #buffer .nag {
|
||||
color: var(--main-color);
|
||||
}
|
||||
#buffer .error {
|
||||
|
||||
Reference in New Issue
Block a user