forked from CringeStudios/gamja
Compare commits
24 Commits
unnecessar
...
master
Author | SHA1 | Date | |
---|---|---|---|
d7c990d5d8 | |||
137a7d9014 | |||
38ac1fa83f | |||
![]() |
fcc80a85e3 | ||
![]() |
76b6931ebb | ||
![]() |
7d068fd1fe | ||
![]() |
735dd8fd8c | ||
![]() |
c461f4903e | ||
![]() |
f897e7d11b | ||
![]() |
95749ba516 | ||
![]() |
39a2bc4a3d | ||
![]() |
614ed5c895 | ||
![]() |
8d96f93fb5 | ||
![]() |
9922d11654 | ||
![]() |
57c5f2b1cc | ||
![]() |
0cc1c53fa4 | ||
![]() |
93d7d22726 | ||
![]() |
136353b2b5 | ||
![]() |
7dd21177bc | ||
![]() |
ca0cfdcc28 | ||
![]() |
1e3903c014 | ||
![]() |
5146b0cad8 | ||
![]() |
513cf825a5 | ||
![]() |
9fef11564d |
@ -1,5 +1,4 @@
|
||||
# TODO switch back to alpine/latest once the "npm install" deadlock is fixed
|
||||
image: alpine/edge
|
||||
image: alpine/latest
|
||||
packages:
|
||||
- npm
|
||||
- rsync
|
||||
@ -7,6 +6,8 @@ sources:
|
||||
- https://codeberg.org/emersion/gamja.git
|
||||
secrets:
|
||||
- 7a146c8e-aeb4-46e7-99bf-05af7486bbe9 # deploy SSH key
|
||||
artifacts:
|
||||
- gamja/gamja.tar.gz
|
||||
tasks:
|
||||
- setup: |
|
||||
cd gamja
|
||||
@ -14,6 +15,7 @@ tasks:
|
||||
- build: |
|
||||
cd gamja
|
||||
npm run build
|
||||
tar -czf gamja.tar.gz -C dist .
|
||||
- lint: |
|
||||
cd gamja
|
||||
npm run -- lint --max-warnings 0
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
A simple IRC web client.
|
||||
|
||||
<img src="https://codeberg.org/attachments/117ff232-7e73-46c7-a21d-2b59ffa3765a" alt="Screenshot" width="800">
|
||||
<img src="https://fs.emersion.fr/protected/img/gamja/main.png" alt="Screenshot" width="800">
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -124,7 +124,7 @@ const commands = [
|
||||
if (args.length) {
|
||||
params.push(args.join(" "));
|
||||
}
|
||||
getActiveClient(app).send({command: "AWAY", params});
|
||||
getActiveClient(app).send({ command: "AWAY", params });
|
||||
},
|
||||
},
|
||||
ban,
|
||||
@ -190,9 +190,10 @@ const commands = [
|
||||
throw new Error("Missing nick");
|
||||
}
|
||||
let activeChannel = getActiveChannel(app);
|
||||
getActiveClient(app).send({ command: "INVITE", params: [
|
||||
nick, activeChannel,
|
||||
]});
|
||||
getActiveClient(app).send({
|
||||
command: "INVITE",
|
||||
params: [nick, activeChannel],
|
||||
});
|
||||
},
|
||||
},
|
||||
{ ...join, name: "j" },
|
||||
|
@ -762,7 +762,7 @@ export default class App extends Component {
|
||||
|
||||
// Open a new buffer if the message doesn't come from me or is a
|
||||
// self-message
|
||||
if ((!client.isMyNick(msg.prefix.name) || client.isMyNick(bufName)) && (msg.command !== "PART" && msg.comand !== "QUIT" && msg.command !== irc.RPL_MONONLINE && msg.command !== irc.RPL_MONOFFLINE)) {
|
||||
if ((!client.isMyNick(msg.prefix.name) || client.isMyNick(bufName)) && (msg.command !== "PART" && msg.command !== "QUIT" && msg.command !== irc.RPL_MONONLINE && msg.command !== irc.RPL_MONOFFLINE)) {
|
||||
this.createBuffer(serverID, bufName);
|
||||
}
|
||||
|
||||
@ -1075,6 +1075,7 @@ export default class App extends Component {
|
||||
case "ACK":
|
||||
case "BOUNCER":
|
||||
case "MARKREAD":
|
||||
case "REDACT":
|
||||
// Ignore these
|
||||
return [];
|
||||
default:
|
||||
@ -2005,7 +2006,9 @@ export default class App extends Component {
|
||||
this.lastFocusPingDate = now;
|
||||
|
||||
for (let client of this.clients.values()) {
|
||||
client.send({ command: "PING", params: ["gamja"] });
|
||||
if (client.status === Client.Status.REGISTERED) {
|
||||
client.send({ command: "PING", params: ["gamja"] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ function canFoldMessage(msg) {
|
||||
|
||||
class LogLine extends Component {
|
||||
shouldComponentUpdate(nextProps) {
|
||||
return this.props.message !== nextProps.message;
|
||||
return this.props.message !== nextProps.message || this.props.redacted !== nextProps.redacted;
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -143,12 +143,18 @@ class LogLine extends Component {
|
||||
`;
|
||||
}
|
||||
} else {
|
||||
lineClass = "talk";
|
||||
let prefix = "<", suffix = ">";
|
||||
if (msg.command === "NOTICE") {
|
||||
lineClass += " notice";
|
||||
prefix = suffix = "-";
|
||||
}
|
||||
content = html`${prefix}${createNick(msg.prefix.name)}${suffix} ${linkify(stripANSI(text), onChannelClick)}`;
|
||||
if (this.props.redacted) {
|
||||
content = html`<i>This message has been deleted.</i>`;
|
||||
} else {
|
||||
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}`;
|
||||
}
|
||||
|
||||
let allowedPrefixes = server.statusMsg;
|
||||
@ -709,6 +715,7 @@ export default class Buffer extends Component {
|
||||
message=${msg}
|
||||
buffer=${buf}
|
||||
server=${server}
|
||||
redacted=${buf.redacted.has(msg.tags.msgid)}
|
||||
onChannelClick=${onChannelClick}
|
||||
onNickClick=${onNickClick}
|
||||
onVerifyClick=${onVerifyClick}
|
||||
@ -814,7 +821,7 @@ export default class Buffer extends Component {
|
||||
|
||||
if (sep.length > 0) {
|
||||
children.push(createFoldGroup(foldMessages));
|
||||
children.push(sep);
|
||||
children.push(...sep);
|
||||
foldMessages = [];
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@ function KeyBindingsHelp() {
|
||||
}
|
||||
|
||||
function CommandsHelp() {
|
||||
let l = Object.keys(commands).map((name) => {
|
||||
let cmd = commands[name];
|
||||
let l = [...commands.keys()].map((name) => {
|
||||
let cmd = commands.get(name);
|
||||
|
||||
let usage = [html`<strong>/${name}</strong>`];
|
||||
if (cmd.usage) {
|
||||
|
@ -34,11 +34,23 @@ export default [
|
||||
"no-implicit-coercion": "warn",
|
||||
"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", {
|
||||
multiline: true,
|
||||
consistent: true,
|
||||
}],
|
||||
"@stylistic/js/array-bracket-spacing": ["warn", "never"],
|
||||
"@stylistic/js/array-bracket-newline": ["warn", "consistent"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
14
index.html
14
index.html
@ -3,7 +3,19 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; frame-src 'none'; object-src 'none'; connect-src *;">
|
||||
<title>gamja IRC client</title>
|
||||
<title>Cringe Studios Internet IRC Relay Chat Client</title>
|
||||
|
||||
<meta name="description" content="Cringe Studios Internet IRC Relay Chat Client">
|
||||
<meta property="og:image" content="https://nsfw.cringe-studios.com/Wikimedia_Community_Logo-IRC.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://irc.cringe-studios.com/">
|
||||
<meta property="og:title" content="Cringe Studios Internet IRC Relay Chat Client">
|
||||
<meta property="og:description" content="Cringe Studios Internet IRC Relay Chat Client">
|
||||
|
||||
<meta name="theme-color" content="#8000f0" data-react-helmet="true">
|
||||
<link rel="icon" href="https://nsfw.cringe-studios.com/Wikimedia_Community_Logo-IRC.png">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<script type="module" src="./main.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
@ -21,6 +21,7 @@ const permanentCaps = [
|
||||
"draft/account-registration",
|
||||
"draft/chathistory",
|
||||
"draft/extended-monitor",
|
||||
"draft/message-redaction",
|
||||
"draft/read-marker",
|
||||
|
||||
"soju.im/bouncer-networks",
|
||||
|
@ -43,9 +43,9 @@ export function redirectAuthorize({ serverMetadata, clientId, redirectUri, scope
|
||||
// TODO: use the state param to prevent cross-site request
|
||||
// forgery
|
||||
let params = {
|
||||
response_type: "code",
|
||||
client_id: clientId,
|
||||
redirect_uri: redirectUri,
|
||||
"response_type": "code",
|
||||
"client_id": clientId,
|
||||
"redirect_uri": redirectUri,
|
||||
};
|
||||
if (scope) {
|
||||
params.scope = scope;
|
||||
@ -66,12 +66,12 @@ function buildPostHeaders(clientId, clientSecret) {
|
||||
|
||||
export async function exchangeCode({ serverMetadata, redirectUri, code, clientId, clientSecret }) {
|
||||
let data = {
|
||||
grant_type: "authorization_code",
|
||||
"grant_type": "authorization_code",
|
||||
code,
|
||||
redirect_uri: redirectUri,
|
||||
"redirect_uri": redirectUri,
|
||||
};
|
||||
if (!clientSecret) {
|
||||
data.client_id = clientId;
|
||||
data["client_id"] = clientId;
|
||||
}
|
||||
|
||||
let resp = await fetch(serverMetadata.token_endpoint, {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gamja IRC client",
|
||||
"short_name": "gamja",
|
||||
"name": "Cringe Studios Internet IRC Relay Chat Client",
|
||||
"short_name": "IRC",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"scope": "."
|
||||
|
1243
package-lock.json
generated
1243
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
||||
"@eslint/js": "^9.11.1",
|
||||
"@parcel/packager-raw-url": "^2.0.0",
|
||||
"@parcel/transformer-webmanifest": "^2.0.0",
|
||||
"@stylistic/eslint-plugin-js": "^2.8.0",
|
||||
"@stylistic/eslint-plugin-js": "^3.1.0",
|
||||
"eslint": "^9.11.1",
|
||||
"globals": "^15.9.0",
|
||||
"node-static": "^0.7.11",
|
||||
|
29
state.js
29
state.js
@ -153,10 +153,24 @@ function trimStartCharacter(s, c) {
|
||||
return s.substring(i);
|
||||
}
|
||||
|
||||
function getBouncerNetworkNameFromBuffer(state, buffer) {
|
||||
let server = state.servers.get(buffer.server);
|
||||
let network = state.bouncerNetworks.get(server.bouncerNetID);
|
||||
if (!network) {
|
||||
return null;
|
||||
}
|
||||
return getServerName(server, network);
|
||||
}
|
||||
|
||||
/* Returns 1 if a should appear after b, -1 if a should appear before b, or
|
||||
* 0 otherwise. */
|
||||
function compareBuffers(a, b) {
|
||||
function compareBuffers(state, a, b) {
|
||||
if (a.server !== b.server) {
|
||||
let aServerName = getBouncerNetworkNameFromBuffer(state, a);
|
||||
let bServerName = getBouncerNetworkNameFromBuffer(state, b);
|
||||
if (aServerName && bServerName && aServerName !== bServerName) {
|
||||
return aServerName.localeCompare(bServerName);
|
||||
}
|
||||
return a.server > b.server ? 1 : -1;
|
||||
}
|
||||
if (isServerBuffer(a) !== isServerBuffer(b)) {
|
||||
@ -217,7 +231,7 @@ function insertMessage(list, msg) {
|
||||
}
|
||||
console.assert(insertBefore >= 0, "");
|
||||
|
||||
list = [ ...list ];
|
||||
list = [...list];
|
||||
list.splice(insertBefore, 0, msg);
|
||||
return list;
|
||||
}
|
||||
@ -361,10 +375,11 @@ export const State = {
|
||||
hasInitialWho: false, // if channel
|
||||
members: new irc.CaseMapMap(null, client.cm), // if channel
|
||||
messages: [],
|
||||
redacted: new Set(),
|
||||
unread: Unread.NONE,
|
||||
prevReadReceipt: null,
|
||||
});
|
||||
bufferList = bufferList.sort(compareBuffers);
|
||||
bufferList = bufferList.sort((a, b) => compareBuffers(state, a, b));
|
||||
let buffers = new Map(bufferList.map((buf) => [buf.id, buf]));
|
||||
return [id, { buffers }];
|
||||
},
|
||||
@ -665,6 +680,14 @@ export const State = {
|
||||
|
||||
return { members };
|
||||
});
|
||||
case "REDACT":
|
||||
target = msg.params[0];
|
||||
if (client.isMyNick(target)) {
|
||||
target = msg.prefix.name;
|
||||
}
|
||||
return updateBuffer(target, (buf) => {
|
||||
return { redacted: new Set(buf.redacted).add(msg.params[1]) };
|
||||
});
|
||||
case irc.RPL_MONONLINE:
|
||||
case irc.RPL_MONOFFLINE:
|
||||
targets = msg.params[1].split(",");
|
||||
|
Loading…
x
Reference in New Issue
Block a user