Files
stemify/ios/Stemify/UI/Screens/WebRadioScreen.swift
T

117 lines
5.3 KiB
Swift

import SwiftUI
/// Saved internet radio stations. Tap to play, swipe to delete, long press to edit.
struct WebRadioScreen: View {
@Environment(\.palette) private var p
@State private var editing: WebStation?
private var g: AppGraph { .shared }
var body: some View {
let s = g.settings
let radio = g.radio
let list = s.stations
List {
if let on = radio.station {
Section {
HStack(spacing: 14) {
SettingsIcon(symbol: "antenna.radiowaves.left.and.right", bg: 0xFF2D55)
VStack(alignment: .leading, spacing: 1) {
Text(on.name).foregroundStyle(p.label).lineLimit(1)
Text(radio.playing ? "Live" : "Paused").font(.footnote).foregroundStyle(p.label2)
}
Spacer()
Button { radio.toggle() } label: {
Image(systemName: radio.playing ? "pause.fill" : "play.fill").font(.system(size: 20)).frame(width: 40, height: 40)
}
.buttonStyle(.borderless)
Button { radio.stop() } label: {
Image(systemName: "stop.fill").font(.system(size: 18)).frame(width: 40, height: 40)
}
.buttonStyle(.borderless)
}
.themedRow()
} header: { Text("Now Playing").foregroundStyle(p.label2) }
}
Section {
ForEach(list) { st in
Button { radio.play(st) } label: {
HStack(spacing: 14) {
SettingsIcon(symbol: "dot.radiowaves.left.and.right", bg: 0x5856D6)
VStack(alignment: .leading, spacing: 1) {
Text(st.name).foregroundStyle(p.label).lineLimit(1)
Text(st.url).font(.footnote).foregroundStyle(p.label2).lineLimit(1)
}
Spacer()
if radio.station?.id == st.id { PlayingBars(playing: radio.playing) }
}
}
.themedRow()
.swipeActions(edge: .leading) { Button("Edit") { editing = st }.tint(.orange) }
.contextMenu {
Button { editing = st } label: { Label("Edit", systemImage: "pencil") }
Button(role: .destructive) { remove(st) } label: { Label("Delete", systemImage: "trash") }
}
}
.onDelete { rows in rows.map { list[$0] }.forEach(remove) }
Button { editing = WebStation(name: "", url: "") } label: { Label("Add Station", systemImage: "plus.circle.fill") }
.themedRow()
} header: { Text("Stations").foregroundStyle(p.label2) } footer: {
Text("Any direct stream URL works (MP3, AAC or HLS). Playing a station pauses your queue, and playing a song stops the station.")
.foregroundStyle(p.label2)
}
}
.listStyle(.insetGrouped)
.themedList()
.navigationTitle("Web Radio")
.sheet(item: $editing) { st in StationSheet(station: st) }
}
private func remove(_ st: WebStation) {
if g.radio.station?.id == st.id { g.radio.stop() }
g.settings.stations.removeAll { $0.id == st.id }
}
}
/// Name and stream URL for a new or existing station.
private struct StationSheet: View {
let station: WebStation
@Environment(\.dismiss) private var dismiss
@State private var name = ""
@State private var url = ""
var body: some View {
let n = name.trimmingCharacters(in: .whitespaces)
let u = url.trimmingCharacters(in: .whitespaces)
let valid = !n.isEmpty && URL(string: u).map { ["http", "https"].contains($0.scheme?.lowercased() ?? "") } == true
NavigationStack {
List {
Section {
TextField("Name", text: $name).themedRow()
TextField("Stream URL", text: $url)
.keyboardType(.URL).textInputAutocapitalization(.never).autocorrectionDisabled()
.themedRow()
}
}
.listStyle(.insetGrouped)
.themedList()
.navigationTitle(station.name.isEmpty ? "New Station" : "Edit Station")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } }
ToolbarItem(placement: .confirmationAction) {
Button("Save") {
let s = AppGraph.shared.settings
var list = s.stations
let st = WebStation(id: station.id, name: n, url: u)
if let i = list.firstIndex(where: { $0.id == station.id }) { list[i] = st } else { list.append(st) }
s.stations = list
dismiss()
}
.disabled(!valid)
}
}
}
.onAppear { name = station.name; url = station.url }
}
}