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.
22 lines
736 B
Swift
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)
|
|
}
|
|
}
|
|
}
|