Files
damus/damus/Features/Live/LiveStream/Views/LiveStreamView.swift
ericholguin b8c664d354 Damus Live
This PR adds Live Streaming and Live Chat to Damus via Damus Labs.

Changelog-Added: Added live stream timeline
Changelog-Added: Added live chat timeline
Changelog-Added: Added ability to create live chat event
Changelog-Added: Damus Labs Toggle

Signed-off-by: ericholguin <ericholguin@apache.org>
2025-11-14 15:36:43 -08:00

151 lines
5.3 KiB
Swift

//
// LiveStreamView.swift
// damus
//
// Created by eric on 7/25/25.
//
import SwiftUI
import Kingfisher
struct LiveStreamView: View {
let state: DamusState
let event: LiveEvent
@StateObject var model: LiveEventModel
@Environment(\.colorScheme) var colorScheme
@Environment(\.presentationMode) var presentationMode
@ObservedObject var artifacts: NoteArtifactsModel
@State private var dragOffset: CGSize = .zero
@State private var isDragging = false
@State private var currentVideoModel: DamusVideoPlayer?
init(state: DamusState, ev: LiveEvent, model: LiveEventModel) {
self.state = state
self.event = ev
self._model = StateObject(wrappedValue: model)
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.event.id).artifacts_model)
}
init(state: DamusState, ev: NostrEvent, model: LiveEventModel) {
self.state = state
self.event = LiveEvent.parse(from: ev)
self._model = StateObject(wrappedValue: model)
self._artifacts = ObservedObject(wrappedValue: state.events.get_cache_data(ev.id).artifacts_model)
}
func content_filter(_ pubkeys: [Pubkey]) -> ((NostrEvent) -> Bool) {
var filters = ContentFilters.defaults(damus_state: self.state)
filters.append({ pubkeys.contains($0.pubkey) })
return ContentFilters(filters: filters).filter
}
func setupVideoModel() {
if let streamingURL = event.streaming {
currentVideoModel = state.video.get_player(for: streamingURL)
// currentVideoModel = state.video.get_player(for: streamingURL, title: event.title ?? "Untitled", link: streamingURL.absoluteString, artist: "Nostrich", artwork: event.image?.absoluteString ?? "")
} else if let recordingURL = event.recording {
currentVideoModel = model.damus_state.video.get_player(for: recordingURL)
// currentVideoModel = model.damus_state.video.get_player(for: recordingURL, title: event.title ?? "Untitled", link: recordingURL.absoluteString, artist: "Nostrich", artwork: event.image?.absoluteString ?? "")
}
}
var dragGesture: some Gesture {
DragGesture()
.onChanged { value in
if value.translation.height > 0 {
dragOffset = value.translation
isDragging = true
}
}
.onEnded { value in
isDragging = false
if value.translation.height > 100 {
withAnimation(.easeOut(duration: 0.3)) {
dragOffset.height = UIScreen.main.bounds.height
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
presentationMode.wrappedValue.dismiss()
}
} else {
withAnimation(.spring()) {
dragOffset = .zero
}
}
}
}
var body: some View {
VStack(spacing: 0) {
LiveEventHeader
.highPriorityGesture(dragGesture, including: .all)
LiveChatHomeView(state: state, event: event, model: LiveChatModel(damus_state: state, root: event.event.pubkey.hex(), dtag: event.uuid ?? ""))
.scrollDismissesKeyboard(.immediately)
}
.offset(y: dragOffset.height)
.opacity(isDragging ? Double(1 - min(abs(dragOffset.height) / 250, 0.5)) : 1.0)
.animation(.interactiveSpring(), value: dragOffset)
.navigationBarBackButtonHidden(true)
.onAppear {
notify(.display_tabbar(false))
model.subscribe()
setupVideoModel()
}
.onDisappear {
notify(.display_tabbar(true))
model.unsubscribe()
}
}
var LiveEventHeader: some View {
VStack(alignment: .leading, spacing: 0) {
ZStack {
if let videoModel = currentVideoModel {
DamusVideoPlayerView(
model: videoModel,
coordinator: state.video,
style: .preview(on_tap: {})
)
} else {
LiveStreamBanner(state: state, options: EventViewOptions(), image: event.image, preview: false)
}
}
.frame(width: UIScreen.main.bounds.width, height: 250)
.fixedSize(horizontal: true, vertical: true)
.background(Color.black)
if !event.publicKeys.isEmpty {
LiveStreamProfile(state: state, pubkey: event.publicKeys[0], size: 35)
} else {
LiveStreamProfile(state: state, pubkey: event.event.pubkey, size: 35)
}
if let title = event.title {
Text(title)
.fixedSize(horizontal: false, vertical: true)
.fontWeight(.bold)
.padding(.horizontal, 5)
}
// TO DO: Add description in sheet
}
}
}
struct LiveStreamView_Previews: PreviewProvider {
static var previews: some View {
LiveStreamView(state: test_damus_state, ev: test_live_event, model: LiveEventModel(damus_state: test_damus_state))
.environmentObject(OrientationTracker())
}
}