✨ Live updates of chat messages with websocket on watchOS
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user