Files
luma/Luma/RelativeTimeText.swift
Ole André Vadla Ravnås 60fc7bb706 Add lab pictures and refresh timestamps
Lab pictures are now uploadable by the owner — pick an image in
the CollaborationPanel (NSOpenPanel on Mac, GtkFileDialog shim
on GTK) and it becomes the lab's avatar on every client and on
the browser /l/<id> page. PNG, JPEG, WebP, and GIF are accepted;
the server enforces a 512 KiB cap. When no picture is set we
fall through to the owner's GitHub avatar, so a lab that nobody
bothers customizing still shows a real face. Clients get
pictures inline in the .join snapshot (bytes appended after the
notebook blob) and live updates via +update on
/labs/<id>/picture; CollaborationSession gains setLabPicture /
removeLabPicture RPCs.

Two other fixes in the same pass:

- Relative timestamps now refresh. Mac wraps each label in a
  TimelineView(.periodic(..., by: 60)); GTK runs a single 60 s
  Task that rewrites the string on every registered Label. So
  "1 min ago" actually advances to "2 min ago" without a
  rebuild.
- Closing a document window now stops collaboration. Previously
  the session stayed joined until the whole app quit — Mac via
  .onDisappear on MainWindowView, GTK via the existing
  closeHandler calling engine.collaboration.stop() before
  persisting state.
2026-04-20 10:17:50 +02:00

22 lines
736 B
Swift

import LumaCore
import SwiftUI
/// Live-updating "time ago" label. Wraps a SwiftUI TimelineView so the
/// rendered string refreshes once a minute — matching the smallest
/// bucket `RelativeTime` emits ("1 min ago"). Longer buckets don't
/// change any faster than every minute either, so this keeps every
/// visible timestamp honest without extra machinery.
struct RelativeTimeText: View {
let date: Date
var font: Font = .caption2
var color: Color = .secondary
var body: some View {
TimelineView(.periodic(from: .now, by: 60)) { context in
Text(LumaCore.RelativeTime.string(from: date, now: context.date))
.font(font)
.foregroundStyle(color)
}
}
}