diff --git a/main.go b/main.go index 6a8fcdd..04a5f37 100644 --- a/main.go +++ b/main.go @@ -65,6 +65,7 @@ type client struct { Name string `json:"name"` Admin bool `json:"admin"` Active bool `json:"active"` + ActiveStart int64 `json:"active_start"` clientId string room *room @@ -84,6 +85,7 @@ type adminEvent struct { type standardEvent struct { Active bool `json:"active"` + ActiveStart int64 `json:"active_start"` TimerStart int64 `json:"timer_start"` AdminSecret string `json:"admin_secret"` } @@ -223,6 +225,11 @@ func active(w http.ResponseWriter, r *http.Request) { } c.Active = req.Active + if c.Active { + c.ActiveStart = time.Now().Unix() + } else { + c.ActiveStart = 0 + } c.update() rm.sendAdminEvent(&adminEvent{ Client: c, @@ -467,6 +474,7 @@ func (c *client) update() { e := &event{ StandardEvent: &standardEvent{ Active: c.Active, + ActiveStart: c.ActiveStart, TimerStart: c.room.timerStart.Unix(), }, } diff --git a/static/remote.css b/static/remote.css index 4f2cb66..80d6018 100644 --- a/static/remote.css +++ b/static/remote.css @@ -79,9 +79,13 @@ tfoot tr { } .action { + color: var(--highlight-color); cursor: pointer; margin-left: 10px; - color: var(--highlight-color); +} + +.users tbody tr td:nth-child(2) { + text-align: right; } .github { diff --git a/static/remote.js b/static/remote.js index f530537..0fef224 100644 --- a/static/remote.js +++ b/static/remote.js @@ -104,18 +104,22 @@ function renderControls(roomId, clientId, adminSecret, prnt, es) { } function renderTimers(roomId, adminSecret, prnt, es) { let overallStart = null; + let meStart = null; es.addEventListener("message", (e) => { const event = JSON.parse(e.data); if (!event.standard_event) { return; } overallStart = parseInt(event.standard_event.timer_start || "0", 10) || null; + meStart = parseInt(event.standard_event.active_start || "0", 10) || null; }); const width = 10; const clockDiv = create(prnt, "div", "Clock: ".padStart(width, "\u00a0")); const clock = create(clockDiv, "span"); const overallDiv = create(prnt, "div", "Overall: ".padStart(width, "\u00a0")); const overall = create(overallDiv, "span"); + const meDiv = create(prnt, "div", "Me: ".padStart(width, "\u00a0")); + const me = create(meDiv, "span"); if (adminSecret) { const reset = create(overallDiv, "span", "↺", ["action"]); reset.addEventListener("click", () => { @@ -142,6 +146,13 @@ function renderTimers(roomId, adminSecret, prnt, es) { else { overall.innerText = ""; } + if (meStart) { + const d = Math.trunc(now.getTime() / 1000 - meStart); + me.innerText = `${Math.trunc(d / 3600).toString().padStart(2, "0")}h${Math.trunc(d % 3600 / 60).toString().padStart(2, "0")}m${Math.trunc(d % 60).toString().padStart(2, "0")}s`; + } + else { + me.innerText = ""; + } }, 250); } function renderAdmin(roomId, adminSecret, prnt, es) { @@ -149,10 +160,15 @@ function renderAdmin(roomId, adminSecret, prnt, es) { const head = create(table, "thead"); const head1 = create(head, "tr"); create(head1, "th", "Name"); + create(head1, "th", "Active Time"); create(head1, "th", "👑"); create(head1, "th", "👆"); const body = create(table, "tbody"); const rows = new Map(); + es.addEventListener("open", () => { + rows.clear(); + body.innerHTML = ""; + }); es.addEventListener("message", (e) => { const event = JSON.parse(e.data); if (!event.admin_event) { @@ -169,6 +185,7 @@ function renderAdmin(roomId, adminSecret, prnt, es) { } row = document.createElement("tr"); row.dataset.name = client.name; + row.dataset.activeStart = client.active_start; let before = null; for (const iter of body.children) { const iterRow = iter; @@ -179,6 +196,7 @@ function renderAdmin(roomId, adminSecret, prnt, es) { } body.insertBefore(row, before); create(row, "td", client.name); + create(row, "td"); const adminCell = create(row, "td", "👑", client.admin ? ["admin", "enable"] : ["admin"]); adminCell.addEventListener("click", () => { if (!client.admin) { @@ -194,6 +212,17 @@ function renderAdmin(roomId, adminSecret, prnt, es) { }); rows.set(client.public_client_id, row); }); + setInterval(() => { + const now = new Date(); + for (const row of rows.values()) { + const cell = row.children[1]; + const as = parseInt(row.dataset.activeStart || "0", 10) || null; + if (as) { + const d = Math.trunc(now.getTime() / 1000 - as); + cell.innerText = `${Math.trunc(d / 3600).toString().padStart(2, "0")}h${Math.trunc(d % 3600 / 60).toString().padStart(2, "0")}m${Math.trunc(d % 60).toString().padStart(2, "0")}s`; + } + } + }, 250); } function active(roomId, adminSecret, publicClientId, val) { const req = { diff --git a/static/remote.ts b/static/remote.ts index ce85e86..fb5296f 100644 --- a/static/remote.ts +++ b/static/remote.ts @@ -47,6 +47,7 @@ interface Event { interface StandardEvent { timer_start?: string; active?: boolean; + active_start?: string; admin_secret?: string; } @@ -60,6 +61,7 @@ interface Client { name: string; admin: boolean; active: boolean; + active_start: string; } function main() { @@ -191,7 +193,8 @@ function renderControls(roomId: string, clientId: string, adminSecret: string | } function renderTimers(roomId: string, adminSecret: string | null, prnt: HTMLElement, es: EventSource) { - let overallStart: number | null = null; + let overallStart: number | null = null; + let meStart: number | null = null; es.addEventListener("message", (e) => { const event = JSON.parse(e.data) as Event; @@ -201,6 +204,7 @@ function renderTimers(roomId: string, adminSecret: string | null, prnt: HTMLElem } overallStart = parseInt(event.standard_event.timer_start || "0", 10) || null; + meStart = parseInt(event.standard_event.active_start || "0", 10) || null; }); const width = 10; @@ -211,6 +215,9 @@ function renderTimers(roomId: string, adminSecret: string | null, prnt: HTMLElem const overallDiv = create(prnt, "div", "Overall: ".padStart(width, "\u00a0")); const overall = create(overallDiv, "span"); + const meDiv = create(prnt, "div", "Me: ".padStart(width, "\u00a0")); + const me = create(meDiv, "span"); + if (adminSecret) { const reset = create(overallDiv, "span", "↺", ["action"]); reset.addEventListener("click", () => { @@ -239,6 +246,13 @@ function renderTimers(roomId: string, adminSecret: string | null, prnt: HTMLElem } else { overall.innerText = ""; } + + if (meStart) { + const d = Math.trunc(now.getTime() / 1000 - meStart); + me.innerText = `${Math.trunc(d / 3600).toString().padStart(2, "0")}h${Math.trunc(d % 3600 / 60).toString().padStart(2, "0")}m${Math.trunc(d % 60).toString().padStart(2, "0")}s`; + } else { + me.innerText = ""; + } }, 250); } @@ -247,6 +261,7 @@ function renderAdmin(roomId: string, adminSecret: string, prnt: HTMLElement, es: const head = create(table, "thead"); const head1 = create(head, "tr"); create(head1, "th", "Name"); + create(head1, "th", "Active Time"); create(head1, "th", "👑"); create(head1, "th", "👆"); @@ -254,6 +269,11 @@ function renderAdmin(roomId: string, adminSecret: string, prnt: HTMLElement, es: const rows: Map = new Map(); + es.addEventListener("open", () => { + rows.clear(); + body.innerHTML = ""; + }); + es.addEventListener("message", (e) => { const event = JSON.parse(e.data) as Event; @@ -275,6 +295,7 @@ function renderAdmin(roomId: string, adminSecret: string, prnt: HTMLElement, es: row = document.createElement("tr") as HTMLTableRowElement; row.dataset.name = client.name; + row.dataset.activeStart = client.active_start; let before = null; for (const iter of body.children) { @@ -287,6 +308,7 @@ function renderAdmin(roomId: string, adminSecret: string, prnt: HTMLElement, es: body.insertBefore(row, before); create(row, "td", client.name); + create(row, "td"); const adminCell = create(row, "td", "👑", client.admin ? ["admin", "enable"] : ["admin"]) as HTMLTableCellElement; adminCell.addEventListener("click", () => { @@ -305,6 +327,19 @@ function renderAdmin(roomId: string, adminSecret: string, prnt: HTMLElement, es: rows.set(client.public_client_id, row); }); + + setInterval(() => { + const now = new Date(); + + for (const row of rows.values()) { + const cell = row.children[1] as HTMLTableCellElement; + const as = parseInt(row.dataset.activeStart || "0", 10) || null; + if (as) { + const d = Math.trunc(now.getTime() / 1000 - as); + cell.innerText = `${Math.trunc(d / 3600).toString().padStart(2, "0")}h${Math.trunc(d % 3600 / 60).toString().padStart(2, "0")}m${Math.trunc(d % 60).toString().padStart(2, "0")}s`; + } + } + }, 250); } function active(roomId: string, adminSecret: string, publicClientId: string, val: boolean) {