Live updates of chat messages with websocket on watchOS

This commit is contained in:
2025-10-31 00:11:24 +08:00
parent 3edcdd72af
commit 0ca801d963
2 changed files with 42 additions and 16 deletions

View File

@@ -26,7 +26,7 @@ struct AppInfoHeaderView : View {
// Display WebSocket connection status // Display WebSocket connection status
Text(webSocketStatusMessage) Text(webSocketStatusMessage)
.font(.caption2) .font(.system(size: 10))
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
} }

View File

@@ -259,18 +259,22 @@ struct ChatRoomView: View {
@State private var messages: [SnChatMessage] = [] @State private var messages: [SnChatMessage] = []
@State private var isLoading = false @State private var isLoading = false
@State private var error: Error? @State private var error: Error?
@State private var webSocketConnectionState: WebSocketState = .disconnected // New state for WebSocket status @State private var wsState: WebSocketState = .disconnected // New state for WebSocket status
@State private var cancellables = Set<AnyCancellable>() // For managing subscriptions @State private var cancellables = Set<AnyCancellable>() // For managing subscriptions
var body: some View { var body: some View {
VStack { VStack {
// Display WebSocket connection status // Display WebSocket connection status
Text(webSocketStatusMessage) if (wsState != .connected)
.font(.caption2) {
.foregroundColor(.secondary) Text(webSocketStatusMessage)
.padding(.vertical, 2) .font(.caption2)
.animation(.easeInOut, value: webSocketConnectionState) // Animate status changes .foregroundColor(.secondary)
.padding(.vertical, 2)
.animation(.easeInOut, value: wsState) // Animate status changes
.transition(.opacity)
}
if isLoading { if isLoading {
ProgressView() ProgressView()
@@ -336,7 +340,7 @@ struct ChatRoomView: View {
} }
private var webSocketStatusMessage: String { private var webSocketStatusMessage: String {
switch webSocketConnectionState { switch wsState {
case .connected: return "Connected" case .connected: return "Connected"
case .connecting: return "Connecting..." case .connecting: return "Connecting..."
case .disconnected: return "Disconnected" case .disconnected: return "Disconnected"
@@ -368,6 +372,15 @@ struct ChatRoomView: View {
isLoading = false isLoading = false
} }
private func sendReadReceipt() {
let data: [String: Any] = ["chat_room_id": room.id]
let packet: [String: Any] = ["type": "messages.read", "data": data, "endpoint": "sphere"]
if let jsonData = try? JSONSerialization.data(withJSONObject: packet, options: []),
let jsonString = String(data: jsonData, encoding: .utf8) {
appState.networkService.sendWebSocketMessage(message: jsonString)
}
}
private func setupWebSocketListeners() { private func setupWebSocketListeners() {
// Listen for WebSocket packets (new messages) // Listen for WebSocket packets (new messages)
appState.networkService.packetStream appState.networkService.packetStream
@@ -377,20 +390,33 @@ struct ChatRoomView: View {
print("[ChatRoomView] WebSocket packet stream error: \(err.localizedDescription)") print("[ChatRoomView] WebSocket packet stream error: \(err.localizedDescription)")
} }
}, receiveValue: { packet in }, receiveValue: { packet in
// Assuming 'message.created' is the type for new messages if ["messages.new", "messages.update", "messages.delete"].contains(packet.type),
if packet.type == "message.created",
let messageData = packet.data { let messageData = packet.data {
do { do {
let jsonData = try JSONSerialization.data(withJSONObject: messageData, options: []) let jsonData = try JSONSerialization.data(withJSONObject: messageData, options: [])
let decoder = JSONDecoder() let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601 decoder.dateDecodingStrategy = .iso8601
decoder.keyDecodingStrategy = .convertFromSnakeCase decoder.keyDecodingStrategy = .convertFromSnakeCase
let newMessage = try decoder.decode(SnChatMessage.self, from: jsonData) let message = try decoder.decode(SnChatMessage.self, from: jsonData)
if newMessage.chatRoomId == room.id { if message.chatRoomId == room.id {
// Avoid adding duplicates switch packet.type {
if !messages.contains(where: { $0.id == newMessage.id }) { case "messages.new":
messages.append(newMessage) if message.type.hasPrefix("call") {
// TODO: Handle ongoing call
}
if !messages.contains(where: { $0.id == message.id }) {
messages.append(message)
}
sendReadReceipt()
case "messages.update":
if let index = messages.firstIndex(where: { $0.id == message.id }) {
messages[index] = message
}
case "messages.delete":
messages.removeAll(where: { $0.id == message.id })
default:
break
} }
} }
} catch { } catch {
@@ -404,7 +430,7 @@ struct ChatRoomView: View {
appState.networkService.stateStream appState.networkService.stateStream
.receive(on: DispatchQueue.main) // Ensure UI updates on main thread .receive(on: DispatchQueue.main) // Ensure UI updates on main thread
.sink { state in .sink { state in
webSocketConnectionState = state wsState = state
} }
.store(in: &cancellables) .store(in: &cancellables)
} }