89 lines
3.2 KiB
Swift
89 lines
3.2 KiB
Swift
|
|
import SwiftUI
|
||
|
|
import VoiceCatCore
|
||
|
|
|
||
|
|
/// The drilled-into view for a single channel: a Join control, the people currently in the
|
||
|
|
/// channel, and any sub-channels (each drilling deeper via a nested `ChannelDetailView`).
|
||
|
|
struct ChannelDetailView: View {
|
||
|
|
let channel: Channel
|
||
|
|
@Bindable var session: SessionState
|
||
|
|
|
||
|
|
@State private var showPasswordPrompt = false
|
||
|
|
@State private var password = ""
|
||
|
|
|
||
|
|
private var isCurrent: Bool { session.currentChannelId == channel.id }
|
||
|
|
private var people: [User] { session.users.filter { $0.channelId == channel.id } }
|
||
|
|
private var subchannels: [Channel] {
|
||
|
|
session.channels.filter { $0.parentId == channel.id }.sorted { $0.name < $1.name }
|
||
|
|
}
|
||
|
|
|
||
|
|
var body: some View {
|
||
|
|
List {
|
||
|
|
Section {
|
||
|
|
if isCurrent {
|
||
|
|
Label("You're here", systemImage: "checkmark.circle.fill")
|
||
|
|
.foregroundStyle(.green)
|
||
|
|
.accessibilityLabel("You are in this channel")
|
||
|
|
} else {
|
||
|
|
Button {
|
||
|
|
join()
|
||
|
|
} label: {
|
||
|
|
Label("Join Channel", systemImage: "arrow.right.circle.fill")
|
||
|
|
}
|
||
|
|
.accessibilityLabel("Join \(channel.name)")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Section("People") {
|
||
|
|
if people.isEmpty {
|
||
|
|
Text("No one here yet.")
|
||
|
|
.foregroundStyle(.secondary)
|
||
|
|
} else {
|
||
|
|
ForEach(people) { user in
|
||
|
|
UserRow(user: user, session: session)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if !subchannels.isEmpty {
|
||
|
|
Section("Channels") {
|
||
|
|
ForEach(subchannels) { sub in
|
||
|
|
NavigationLink {
|
||
|
|
ChannelDetailView(channel: sub, session: session)
|
||
|
|
} label: {
|
||
|
|
ChannelRow(channel: sub, session: session)
|
||
|
|
}
|
||
|
|
.swipeActions(edge: .trailing) {
|
||
|
|
if session.permissions.isAdmin {
|
||
|
|
Button(role: .destructive) {
|
||
|
|
session.deleteChannel(sub.id)
|
||
|
|
} label: {
|
||
|
|
Label("Delete", systemImage: "trash")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
.navigationTitle(channel.name)
|
||
|
|
.navigationBarTitleDisplayMode(.inline)
|
||
|
|
.alert("Channel Password", isPresented: $showPasswordPrompt) {
|
||
|
|
SecureField("Password", text: $password)
|
||
|
|
.accessibilityLabel("Channel password")
|
||
|
|
Button("Join") {
|
||
|
|
session.joinChannel(channel.id, password: password)
|
||
|
|
password = ""
|
||
|
|
}
|
||
|
|
Button("Cancel", role: .cancel) { password = "" }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private func join() {
|
||
|
|
if channel.passwordProtected {
|
||
|
|
showPasswordPrompt = true
|
||
|
|
} else {
|
||
|
|
session.joinChannel(channel.id)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|