Removed unused captions code.
This commit is contained in:
@@ -22,17 +22,12 @@ server.
|
|||||||
|
|
||||||
## Workflow
|
## Workflow
|
||||||
|
|
||||||
- Use `todo.txt` as the source of truth for staged work.
|
- Skald is production software; prefer small, scoped maintenance changes with
|
||||||
- Preserve the todo status markers:
|
focused verification.
|
||||||
- `-` means open.
|
- Use a temporary local plan file only for large or risky batches that need a
|
||||||
- `+` means implemented, needs testing.
|
resumable checklist.
|
||||||
- `X` means tested and done.
|
- Building the Arch package updates `distro-packages/Arch-Linux/skald-git/PKGBUILD`
|
||||||
- Work in small batches and update `todo.txt` only for items actually completed
|
with the generated `pkgver`, so this file is often dirty after package builds.
|
||||||
or tested.
|
|
||||||
- Before beginning a new phase or section, review all earlier `+` and `X`
|
|
||||||
sections in `todo.txt`; confirm their status still matches the code, tests,
|
|
||||||
docs, and user-facing surfaces, and downgrade stale `X` markers when evidence
|
|
||||||
no longer supports them.
|
|
||||||
- Keep changes scoped to the current batch. Do not mix in unrelated cleanup.
|
- Keep changes scoped to the current batch. Do not mix in unrelated cleanup.
|
||||||
- Before behavior changes, inspect the current code path and tests rather than
|
- Before behavior changes, inspect the current code path and tests rather than
|
||||||
assuming Galene behavior still applies.
|
assuming Galene behavior still applies.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# shellcheck shell=bash disable=SC2034,SC2154
|
# shellcheck shell=bash disable=SC2034,SC2154
|
||||||
|
|
||||||
pkgname=skald-git
|
pkgname=skald-git
|
||||||
pkgver=2026.07.08
|
pkgver=2026.07.08.r0.g9a9742e
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc='Audio-only hall-based conferencing server'
|
pkgdesc='Audio-only hall-based conferencing server'
|
||||||
arch=('x86_64' 'aarch64')
|
arch=('x86_64' 'aarch64')
|
||||||
|
|||||||
+1
-2
@@ -34,11 +34,10 @@ type Permissions struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var permissionsMap = map[string][]string{
|
var permissionsMap = map[string][]string{
|
||||||
"op": {"op", "present", "message", "caption", "token"},
|
"op": {"op", "present", "message", "token"},
|
||||||
"present": {"present", "message"},
|
"present": {"present", "message"},
|
||||||
"message": {"message"},
|
"message": {"message"},
|
||||||
"observe": {},
|
"observe": {},
|
||||||
"caption": {"caption"},
|
|
||||||
"admin": {"admin"},
|
"admin": {"admin"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-14
@@ -396,8 +396,6 @@ var james = "james"
|
|||||||
var paul = "paul"
|
var paul = "paul"
|
||||||
var peter = "peter"
|
var peter = "peter"
|
||||||
var admin = "admin"
|
var admin = "admin"
|
||||||
var stt = "speech-to-text"
|
|
||||||
|
|
||||||
var badClients = []ClientCredentials{
|
var badClients = []ClientCredentials{
|
||||||
{Username: &jch, Password: "foo"},
|
{Username: &jch, Password: "foo"},
|
||||||
{Username: &john, Password: "foo"},
|
{Username: &john, Password: "foo"},
|
||||||
@@ -418,9 +416,7 @@ var desc2JSON = `
|
|||||||
"john": {"password": "secret", "permissions": "present"},
|
"john": {"password": "secret", "permissions": "present"},
|
||||||
"james": {"password": "secret2", "permissions": "message"},
|
"james": {"password": "secret2", "permissions": "message"},
|
||||||
"peter": {"password": "secret4"},
|
"peter": {"password": "secret4"},
|
||||||
"admin": {"password": "admin", "permissions": "admin"},
|
"admin": {"password": "admin", "permissions": "admin"}
|
||||||
"speech-to-text": {"password": {"type": "wildcard"},
|
|
||||||
"permissions": "caption"}
|
|
||||||
},
|
},
|
||||||
"wildcard-user":
|
"wildcard-user":
|
||||||
{"permissions": "message", "password": {"type":"wildcard"}}
|
{"permissions": "message", "password": {"type":"wildcard"}}
|
||||||
@@ -429,7 +425,7 @@ var desc2JSON = `
|
|||||||
var goodClients = []credPerm{
|
var goodClients = []credPerm{
|
||||||
{
|
{
|
||||||
ClientCredentials{Username: &jch, Password: "topsecret"},
|
ClientCredentials{Username: &jch, Password: "topsecret"},
|
||||||
[]string{"op", "present", "message", "caption", "token"},
|
[]string{"op", "present", "message", "token"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ClientCredentials{Username: &john, Password: "secret"},
|
ClientCredentials{Username: &john, Password: "secret"},
|
||||||
@@ -451,10 +447,6 @@ var goodClients = []credPerm{
|
|||||||
ClientCredentials{Username: &admin, Password: "admin"},
|
ClientCredentials{Username: &admin, Password: "admin"},
|
||||||
[]string{"admin"},
|
[]string{"admin"},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
ClientCredentials{Username: &stt},
|
|
||||||
[]string{"caption"},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPermissions(t *testing.T) {
|
func TestPermissions(t *testing.T) {
|
||||||
@@ -513,7 +505,7 @@ func TestExtraPermissions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doit("jch", []string{"op", "token", "present", "message", "caption"})
|
doit("jch", []string{"op", "token", "present", "message"})
|
||||||
doit("john", []string{"present", "message"})
|
doit("john", []string{"present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
|
|
||||||
@@ -521,7 +513,7 @@ func TestExtraPermissions(t *testing.T) {
|
|||||||
d.UnrestrictedTokens = false
|
d.UnrestrictedTokens = false
|
||||||
|
|
||||||
doit("jch", []string{
|
doit("jch", []string{
|
||||||
"op", "record", "token", "present", "message", "caption",
|
"op", "record", "token", "present", "message",
|
||||||
})
|
})
|
||||||
doit("john", []string{"present", "message"})
|
doit("john", []string{"present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
@@ -529,7 +521,7 @@ func TestExtraPermissions(t *testing.T) {
|
|||||||
d.AllowRecording = false
|
d.AllowRecording = false
|
||||||
d.UnrestrictedTokens = true
|
d.UnrestrictedTokens = true
|
||||||
|
|
||||||
doit("jch", []string{"op", "token", "present", "message", "caption"})
|
doit("jch", []string{"op", "token", "present", "message"})
|
||||||
doit("john", []string{"token", "present", "message"})
|
doit("john", []string{"token", "present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
|
|
||||||
@@ -537,7 +529,7 @@ func TestExtraPermissions(t *testing.T) {
|
|||||||
d.UnrestrictedTokens = true
|
d.UnrestrictedTokens = true
|
||||||
|
|
||||||
doit("jch", []string{
|
doit("jch", []string{
|
||||||
"op", "record", "token", "present", "message", "caption",
|
"op", "record", "token", "present", "message",
|
||||||
})
|
})
|
||||||
doit("john", []string{"token", "present", "message"})
|
doit("john", []string{"token", "present", "message"})
|
||||||
doit("james", []string{})
|
doit("james", []string{})
|
||||||
|
|||||||
@@ -1606,11 +1606,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
|
|||||||
return c.error(hall.UserError("join a hall first"))
|
return c.error(hall.UserError("join a hall first"))
|
||||||
}
|
}
|
||||||
|
|
||||||
required := "message"
|
if !member("message", c.permissions) {
|
||||||
if m.Type == "chat" && m.Kind == "caption" {
|
|
||||||
required = "caption"
|
|
||||||
}
|
|
||||||
if !member(required, c.permissions) {
|
|
||||||
return c.error(hall.UserError("not authorised"))
|
return c.error(hall.UserError("not authorised"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-4
@@ -306,7 +306,7 @@ A chat message may be sent using a `chat` message.
|
|||||||
```javascript
|
```javascript
|
||||||
{
|
{
|
||||||
type: 'chat',
|
type: 'chat',
|
||||||
kind: null or 'me' or 'caption',
|
kind: null or 'me',
|
||||||
source: source-id,
|
source: source-id,
|
||||||
username: username,
|
username: username,
|
||||||
dest: dest-id,
|
dest: dest-id,
|
||||||
@@ -320,9 +320,7 @@ A chat message may be sent using a `chat` message.
|
|||||||
The field `kind` can have one of the following values:
|
The field `kind` can have one of the following values:
|
||||||
|
|
||||||
- `null` or the empty string, a normal chat message;
|
- `null` or the empty string, a normal chat message;
|
||||||
- `'me'`, an IRC-style first-person message;
|
- `'me'`, an IRC-style first-person message.
|
||||||
- `'caption'`, a caption or subtitle (this requires the sender to have
|
|
||||||
the `caption` permission).
|
|
||||||
|
|
||||||
If `dest` is empty, the message is a broadcast message, destined to all of
|
If `dest` is empty, the message is a broadcast message, destined to all of
|
||||||
the clients in the hall. If `source` is empty, then the message was
|
the clients in the hall. If `source` is empty, then the message was
|
||||||
|
|||||||
@@ -434,7 +434,6 @@ following strings:
|
|||||||
- `message`: a user with the right to send chat messages;
|
- `message`: a user with the right to send chat messages;
|
||||||
- `observe`: a user that receives media streams and chat messages, but
|
- `observe`: a user that receives media streams and chat messages, but
|
||||||
is not allowed to send them;
|
is not allowed to send them;
|
||||||
- `caption`: a user with the right to display captions (only);
|
|
||||||
- `admin`: a user with the right to administer the hall (only).
|
- `admin`: a user with the right to administer the hall (only).
|
||||||
|
|
||||||
Usernames must be non-empty and unique among connected non-system clients in a
|
Usernames must be non-empty and unique among connected non-system clients in a
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ func TestInteractiveHall(t *testing.T) {
|
|||||||
|
|
||||||
func TestFormatPermissions(t *testing.T) {
|
func TestFormatPermissions(t *testing.T) {
|
||||||
tests := []struct{ j, v, p string }{
|
tests := []struct{ j, v, p string }{
|
||||||
{`"op"`, "op", "[cmopt]"},
|
{`"op"`, "op", "[mopt]"},
|
||||||
{`"present"`, "present", "[mp]"},
|
{`"present"`, "present", "[mp]"},
|
||||||
{`"observe"`, "observe", "[]"},
|
{`"observe"`, "observe", "[]"},
|
||||||
{`"admin"`, "admin", "[a]"},
|
{`"admin"`, "admin", "[a]"},
|
||||||
|
|||||||
@@ -1220,26 +1220,6 @@ header .collapse:hover {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#captions-container {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 40px;
|
|
||||||
width: 100%;
|
|
||||||
pointer-events: none;
|
|
||||||
z-index: 1390;
|
|
||||||
alpha: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#captions {
|
|
||||||
margin: auto;
|
|
||||||
padding: 0 0.4rem 0 0.4rem;
|
|
||||||
width: max-content;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
text-align: center;
|
|
||||||
pointer-events: none;
|
|
||||||
background-color: rgba(255,255,255,.7);
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--skald-menu-bg: #eee;
|
--skald-menu-bg: #eee;
|
||||||
--skald-menu-shadow: 1px 1px 1px #444;
|
--skald-menu-shadow: 1px 1px 1px #444;
|
||||||
|
|||||||
@@ -84,9 +84,6 @@
|
|||||||
<button class="chat-btn show-chat invisible" id="show-chat" aria-label="Show chat">
|
<button class="chat-btn show-chat invisible" id="show-chat" aria-label="Show chat">
|
||||||
<i class="far fa-comment-alt icon-chat" aria-hidden="true"></i>
|
<i class="far fa-comment-alt icon-chat" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
<div id="captions-container" class="invisible">
|
|
||||||
<div id="captions"></div>
|
|
||||||
</div>
|
|
||||||
<div class="login-container invisible" id="login-container">
|
<div class="login-container invisible" id="login-container">
|
||||||
<div class="login-box">
|
<div class="login-box">
|
||||||
<form id="loginform" class="loginform">
|
<form id="loginform" class="loginform">
|
||||||
|
|||||||
@@ -2881,11 +2881,6 @@ function announceUrgent(text) {
|
|||||||
* @param {string|HTMLElement} message
|
* @param {string|HTMLElement} message
|
||||||
*/
|
*/
|
||||||
function addToChatbox(id, peerId, dest, nick, time, privileged, history, kind, message) {
|
function addToChatbox(id, peerId, dest, nick, time, privileged, history, kind, message) {
|
||||||
if(kind === 'caption') {
|
|
||||||
displayCaption(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let row = document.createElement('div');
|
let row = document.createElement('div');
|
||||||
row.classList.add('message-row');
|
row.classList.add('message-row');
|
||||||
let container = document.createElement('div');
|
let container = document.createElement('div');
|
||||||
@@ -3046,38 +3041,6 @@ function chatMessageMenu(elt, trigger) {
|
|||||||
openActionMenu(items, trigger, `Actions for message from ${u}`);
|
openActionMenu(items, trigger, `Actions for message from ${u}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string|HTMLElement} message
|
|
||||||
*/
|
|
||||||
function setCaption(message) {
|
|
||||||
let container = document.getElementById('captions-container');
|
|
||||||
let captions = document.getElementById('captions');
|
|
||||||
if(!message) {
|
|
||||||
captions.replaceChildren();
|
|
||||||
container.classList.add('invisible');
|
|
||||||
} else {
|
|
||||||
if(message instanceof HTMLElement)
|
|
||||||
captions.replaceChildren(message);
|
|
||||||
else
|
|
||||||
captions.textContent = message;
|
|
||||||
container.classList.remove('invisible');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let captionsTimer = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string|HTMLElement} message
|
|
||||||
*/
|
|
||||||
function displayCaption(message) {
|
|
||||||
if(captionsTimer != null) {
|
|
||||||
clearTimeout(captionsTimer);
|
|
||||||
captionsTimer = null;
|
|
||||||
}
|
|
||||||
setCaption(message);
|
|
||||||
captionsTimer = setTimeout(() => setCaption(null), 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string|HTMLElement} message
|
* @param {string|HTMLElement} message
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -69,6 +69,16 @@ func TestStaticLiveRegionAnnouncementsStayWired(t *testing.T) {
|
|||||||
requireFunctionContains(t, js, "setUserStatus", "announceUrgent(`${username}: hand lowered`)")
|
requireFunctionContains(t, js, "setUserStatus", "announceUrgent(`${username}: hand lowered`)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCaptionOverlayHasBeenRemoved(t *testing.T) {
|
||||||
|
html := readStaticFile(t, "skald.html")
|
||||||
|
js := readStaticFile(t, "skald.js")
|
||||||
|
css := readStaticFile(t, "skald.css")
|
||||||
|
|
||||||
|
requireNotContains(t, html, "captions", "hall page")
|
||||||
|
requireNotContains(t, js, "caption", "hall script")
|
||||||
|
requireNotContains(t, css, "caption", "hall styles")
|
||||||
|
}
|
||||||
|
|
||||||
func TestHallHeadingIncludesCurrentStatus(t *testing.T) {
|
func TestHallHeadingIncludesCurrentStatus(t *testing.T) {
|
||||||
html := readStaticFile(t, "skald.html")
|
html := readStaticFile(t, "skald.html")
|
||||||
js := readStaticFile(t, "skald.js")
|
js := readStaticFile(t, "skald.js")
|
||||||
|
|||||||
Reference in New Issue
Block a user