Resolve various connection issues

This commit is contained in:
2025-10-05 18:19:51 -07:00
parent 839ec53c17
commit 4021881f11
5 changed files with 49 additions and 20 deletions

View File

@@ -54,10 +54,12 @@ struct NowPlayingInfo: Codable
let volume: Int
}
struct API
actor API
{
let baseURL: URL
private var pingTask: Task<(), any Swift.Error>? = nil
init(baseURL: URL) {
self.baseURL = baseURL
}
@@ -168,17 +170,20 @@ struct API
}
public func events() async throws -> AsyncStream<StreamEvent> {
let requestBuilder: () -> RequestBuilder = request
return AsyncStream { continuation in
var websocketTask: URLSessionWebSocketTask = spawnWebsocketTask(with: continuation)
let websocketTask: URLSessionWebSocketTask = API.spawnWebsocketTask(requestBuilder: requestBuilder, with: continuation)
Task {
while true {
var pingLoopEnabled = true
while pingLoopEnabled {
try await Task.sleep(for: .seconds(5))
websocketTask.sendPing { error in
if let error {
notifyError(error, continuation: continuation)
websocketTask = spawnWebsocketTask(with: continuation)
API.notifyError(error, continuation: continuation)
pingLoopEnabled = false
} else {
continuation.yield(.event(Event(type: .receivedWebsocketPong)))
}
@@ -188,11 +193,12 @@ struct API
}
}
private func spawnWebsocketTask(
private static func spawnWebsocketTask(
requestBuilder: () -> RequestBuilder,
with continuation: AsyncStream<StreamEvent>.Continuation
) -> URLSessionWebSocketTask
{
let url = request()
let url = requestBuilder()
.path("/events")
.websocket()
@@ -225,8 +231,9 @@ struct API
return websocketTask
}
private func notifyError(_ error: any Swift.Error, continuation: AsyncStream<StreamEvent>.Continuation) {
private static func notifyError(_ error: any Swift.Error, continuation: AsyncStream<StreamEvent>.Continuation) {
print("Websocket Error: \(error)")
var shouldNotifyObservers = true
let nsError = error as NSError
if nsError.code == 53 {

View File

@@ -98,10 +98,17 @@ extension ContentView
for await streamEvent in try await api.events() {
switch streamEvent {
case .event(let event):
model.connectionError = nil
await clearConnectionErrorIfNecessary()
await handle(event: event)
case .error(let error):
model.connectionError = error
Task { @MainActor in
try await Task.sleep(for: .seconds(1.0))
websocketRestartTrigger += 1
}
break
}
}
} catch {
@@ -128,10 +135,14 @@ extension ContentView
case .receivedWebsocketPong:
// This means we're online.
if model.connectionError != nil {
model.connectionError = nil
await refresh([.playlist, .nowPlaying, .favorites])
}
await clearConnectionErrorIfNecessary()
}
}
private func clearConnectionErrorIfNecessary() async {
if model.connectionError != nil {
model.connectionError = nil
await refresh([.playlist, .nowPlaying, .favorites])
}
}

View File

@@ -149,10 +149,14 @@ class MainViewModel
withObservationTracking {
_ = nowPlayingViewModel.volume
} onChange: { [weak self] in
guard let self else { return }
let isRefreshing = isRefreshingFromAPI
Task {
guard let self else { return }
await self.withModificationsViaAPI { api in
try await api.setVolume(self.nowPlayingViewModel.volume)
if !isRefreshing {
await self.withModificationsViaAPI { api in
try await api.setVolume(self.nowPlayingViewModel.volume)
}
}
await MainActor.run { self.observeNowPlayingModel() }

View File

@@ -12,7 +12,11 @@ struct NowPlayingMiniView: View {
let onTap: () -> Void
@GestureState private var tapGestureState = false
private var nothingQueued: Bool { model.title == nil && model.subtitle == nil }
private var nothingQueued: Bool {
guard let title = model.title, let subtitle = model.subtitle else { return true }
return title.isEmpty && subtitle.isEmpty
}
var body: some View {
let playPauseImageName = model.isPlaying ? "pause.fill" : "play.fill"
@@ -26,14 +30,14 @@ struct NowPlayingMiniView: View {
HStack {
VStack(alignment: .leading) {
if let title = model.title {
if let title = model.title, !title.isEmpty {
Text(title)
.font(.caption)
.lineLimit(1)
.bold()
}
if let subtitle = model.subtitle {
if let subtitle = model.subtitle, !subtitle.isEmpty {
Text(subtitle)
.lineLimit(1)
.font(.caption)

View File

@@ -175,7 +175,10 @@ struct AddServerView: View
_ = try await api.fetchNowPlayingInfo()
self.validationState = .valid
self.serverURL = self.validationURL
if validationURL != serverURL {
self.serverURL = self.validationURL
}
} catch {
print("Validation failed: \(error)")