ios: add error handling
This commit is contained in:
@@ -14,17 +14,19 @@ struct MediaListItem: Identifiable
|
||||
let filename: String
|
||||
let index: Int?
|
||||
let isCurrent: Bool
|
||||
let playbackError: String?
|
||||
|
||||
var id: String {
|
||||
_id + filename // temporary: we get duplicate ids from the server sometimes...
|
||||
}
|
||||
|
||||
init(id: String, title: String, filename: String, index: Int? = nil, isCurrent: Bool = false) {
|
||||
init(id: String, title: String, filename: String, index: Int? = nil, isCurrent: Bool = false, playbackError: String? = nil) {
|
||||
self._id = id
|
||||
self.title = title
|
||||
self.filename = filename
|
||||
self.index = index
|
||||
self.isCurrent = isCurrent
|
||||
self.playbackError = playbackError
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +47,7 @@ class MediaListViewModel
|
||||
var onQueue: (MediaListItem) -> Void = { _ in }
|
||||
var onEdit: (MediaListItem) -> Void = { _ in }
|
||||
var onFavorite: (MediaListItem) -> Void = { _ in }
|
||||
var onDelete: (MediaListItem) -> Void = { _ in }
|
||||
|
||||
init(mode: MediaListMode) {
|
||||
self.mode = mode
|
||||
@@ -54,7 +57,8 @@ class MediaListViewModel
|
||||
struct MediaListView: View
|
||||
{
|
||||
@Binding var model: MediaListViewModel
|
||||
|
||||
@State private var errorAlertItem: MediaListItem? = nil
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if model.items.isEmpty {
|
||||
@@ -68,22 +72,30 @@ struct MediaListView: View
|
||||
List($model.items, editActions: .delete) { item in
|
||||
let item = item.wrappedValue
|
||||
let state = item.isCurrent ? (model.isPlaying ? MediaItemCell.State.playing : MediaItemCell.State.paused) : .queued
|
||||
|
||||
|
||||
Button {
|
||||
switch model.mode {
|
||||
case .playlist:
|
||||
model.onSeek(item)
|
||||
case .favorites:
|
||||
model.onPlay(item)
|
||||
if let _ = item.playbackError {
|
||||
errorAlertItem = item
|
||||
} else {
|
||||
switch model.mode {
|
||||
case .playlist:
|
||||
model.onSeek(item)
|
||||
case .favorites:
|
||||
model.onPlay(item)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
MediaItemCell(
|
||||
title: item.title,
|
||||
subtitle: item.filename,
|
||||
state: state
|
||||
state: state,
|
||||
playbackError: item.playbackError
|
||||
)
|
||||
}
|
||||
.listRowBackground((model.mode == .playlist && state != .queued) ? Color.accentColor.opacity(0.10) : nil)
|
||||
.listRowBackground(
|
||||
item.playbackError != nil ? Color.red.opacity(0.15) :
|
||||
(model.mode == .playlist && state != .queued ? Color.accentColor.opacity(0.10) : nil)
|
||||
)
|
||||
.contextMenu {
|
||||
Button(.copyTitle) {
|
||||
UIPasteboard.general.string = item.title
|
||||
@@ -123,6 +135,28 @@ struct MediaListView: View
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(.playbackError, isPresented: Binding(get: {
|
||||
errorAlertItem != nil
|
||||
}, set: { newValue in
|
||||
if !newValue { errorAlertItem = nil }
|
||||
}), actions: {
|
||||
Button(.cancel, role: .cancel) {
|
||||
errorAlertItem = nil
|
||||
}
|
||||
if let item = errorAlertItem {
|
||||
Button(.delete, role: .destructive) {
|
||||
model.items.removeAll { $0.id == item.id }
|
||||
model.onDelete(item)
|
||||
errorAlertItem = nil
|
||||
}
|
||||
}
|
||||
}, message: {
|
||||
if let message = errorAlertItem?.playbackError {
|
||||
Text(message)
|
||||
} else {
|
||||
Text("Unknown error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,17 +168,12 @@ struct MediaItemCell: View
|
||||
let title: String
|
||||
let subtitle: String
|
||||
let state: State
|
||||
|
||||
let playbackError: String?
|
||||
|
||||
var body: some View {
|
||||
let icon: String = switch state {
|
||||
case .queued: "play.fill"
|
||||
case .playing: "speaker.wave.3.fill"
|
||||
case .paused: "speaker.fill"
|
||||
}
|
||||
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
.tint(Color.primary)
|
||||
Image(systemName: iconName)
|
||||
.tint(playbackError == nil ? Color.primary : Color.orange)
|
||||
.frame(width: 15.0)
|
||||
.padding(.trailing, 10.0)
|
||||
|
||||
@@ -166,6 +195,18 @@ struct MediaItemCell: View
|
||||
.padding([.top, .bottom], 4.0)
|
||||
}
|
||||
|
||||
private var iconName: String {
|
||||
if let playbackError {
|
||||
return "exclamationmark.triangle.fill"
|
||||
}
|
||||
|
||||
switch state {
|
||||
case .queued: return "play.fill"
|
||||
case .playing: return "speaker.wave.3.fill"
|
||||
case .paused: return "speaker.fill"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Types
|
||||
|
||||
enum State {
|
||||
@@ -174,4 +215,3 @@ struct MediaItemCell: View
|
||||
case paused
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user