version 1.0

This commit is contained in:
diyaa 2026-07-26 21:43:39 +02:00
parent 833a9bfec2
commit e973d850ed
5 changed files with 59 additions and 1 deletions

View File

@ -32,9 +32,11 @@ struct ChatView: View {
}
.onAppear {
scrollToLatest(using: proxy)
Task { await session.markVisibleMessagesRead() }
}
.onChange(of: session.messages.last?.id) { _, _ in
scrollToLatest(using: proxy)
Task { await session.markVisibleMessagesRead() }
}
}

View File

@ -53,6 +53,12 @@ struct SettingsView: View {
Text(appVersion)
}
}
Section {
Button("Quit Fchati", role: .destructive) {
NSApplication.shared.terminate(nil)
}
}
}
.formStyle(.grouped)
.padding()

View File

@ -101,6 +101,17 @@ final class AppSession: ObservableObject {
try await sendAndStore(message)
}
func markVisibleMessagesRead() async {
let unread = messages.filter { !$0.isRead && $0.from != KeychainStore.installationID }
guard !unread.isEmpty else { return }
let ids = unread.map { $0.id }
ids.forEach { markMessageAsRead(id: $0) }
let receipt = WSMessage(type: "read", messageIDs: ids)
try? await webSocket.send(receipt)
}
func unpair() {
incomingMessagesTask?.cancel()
incomingMessagesTask = nil
@ -192,6 +203,14 @@ final class AppSession: ObservableObject {
await storeIncomingMessage(chatMessage)
NotificationManager.shared.notify(from: chatMessage.fromName, body: chatMessage.body)
case "peer.joined":
// The partner just came online save their identity and transition to connected.
if let peerID = message.peerID, let peerName = message.fromName {
KeychainStore.peerID = peerID
KeychainStore.peerName = peerName
state = .connected(peerID: peerID, peerName: peerName)
}
case "read":
for id in message.messageIDs ?? [] {
markMessageAsRead(id: id)

View File

@ -225,6 +225,30 @@
---
## Bug Fixes — Full Code Audit ✅
**Applied by:** Claude
### Fix 3 — Creator never reaches `.connected` state (critical)
**Files:**
- `relay-server/src/index.js` — after WebSocket auth, notify the partner with `{ type: "peer.joined", peerID, peerName }`
- `FchatiApp/Sources/FchatiApp/Session/AppSession.swift` — handle `peer.joined`: save peerID/peerName to Keychain, transition state to `.connected`
**Problem:** When the creator generates a pairing code and connects via WebSocket, their state gets stuck at `.connecting` forever. The server never told the creator that the joiner joined — it only responded to the joiner's HTTP request. The creator had no way to learn the joiner's identity or transition to `.connected`.
**Fix:** Server now sends `{ type: "peer.joined", peerID, peerName }` to the online partner whenever any peer authenticates on WebSocket. Client handles this event and saves credentials + transitions state.
---
### Fix 4 — Read receipts were never sent (important)
**Files:**
- `FchatiApp/Sources/FchatiApp/Session/AppSession.swift` — added `markVisibleMessagesRead()`: marks all incoming unread messages as read and sends `{ type: "read", messageIDs: [...] }` via WebSocket
- `FchatiApp/Sources/FchatiApp/Features/Chat/ChatView.swift` — calls `markVisibleMessagesRead()` on appear and when new messages arrive
**Problem:** The code handled *incoming* read receipts correctly but never *sent* them. The partner's messages were never acknowledged as read, so the partner never saw "read" status on their messages.
## Pending
Nothing remaining in the codebase. Next step: deploy relay server to production.
Nothing remaining.

View File

@ -417,6 +417,13 @@ wss.on('connection', (ws) => {
send(ws, { type: 'auth.ok', peerID });
console.log(`[ws] connected: ${peerName} (${peerID})`);
// Notify the partner that this peer is now online
const partnerIDOnAuth = peerPartnerID(session, peerID);
const partnerConnOnAuth = connections.get(partnerIDOnAuth);
if (partnerConnOnAuth?.ws.readyState === 1) {
send(partnerConnOnAuth.ws, { type: 'peer.joined', peerID, peerName });
}
// Deliver messages that arrived while this peer was offline
flushQueue(peerID, ws);
return;