feat(conversation-assistant): add contacts count to webapp stats display

Add totalContacts to sync stats API and display in macOS client webapp,
showing Messages Synced, Conversations, Contacts, and Last Sync.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Quinn Ftw 2025-12-30 16:01:43 -08:00
parent 8972f44a15
commit e9ab11023f
6 changed files with 20 additions and 2 deletions

View file

@ -16,6 +16,7 @@ let state = {
syncErrorMessage: null,
messageCount: 0,
conversationCount: 0,
contactCount: 0,
lastSync: null,
needsFullDiskAccess: false,
isSyncing: false,
@ -62,6 +63,7 @@ function cacheElements() {
elements.activitySection = document.getElementById('activity-section');
elements.statMessages = document.getElementById('stat-messages');
elements.statConversations = document.getElementById('stat-conversations');
elements.statContacts = document.getElementById('stat-contacts');
elements.statLastSync = document.getElementById('stat-last-sync');
elements.activityLog = document.getElementById('activity-log');
@ -252,6 +254,7 @@ function updateFromStatus(status) {
state.syncErrorMessage = status.syncErrorMessage;
state.messageCount = status.messageCount || 0;
state.conversationCount = status.conversationCount || 0;
state.contactCount = status.contactCount || 0;
state.lastSync = status.lastSync;
state.needsFullDiskAccess = status.needsFullDiskAccess || false;
state.isSyncing = status.isSyncing || false;
@ -431,6 +434,7 @@ function renderConnectedView() {
// Stats
elements.statMessages.textContent = formatNumber(state.messageCount);
elements.statConversations.textContent = formatNumber(state.conversationCount);
elements.statContacts.textContent = formatNumber(state.contactCount);
elements.statLastSync.textContent = formatRelativeTime(state.lastSync);
// Activity log

View file

@ -81,6 +81,10 @@
<span class="stat-label">Conversations</span>
<span id="stat-conversations" class="stat-value">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Contacts</span>
<span id="stat-contacts" class="stat-value">0</span>
</div>
<div class="stat-row">
<span class="stat-label">Last Sync</span>
<span id="stat-last-sync" class="stat-value">Never</span>

View file

@ -200,6 +200,7 @@ class APIClient {
return SyncStatsResponse(
totalMessages: json["data"]["totalMessages"].intValue,
totalConversations: json["data"]["totalConversations"].intValue,
totalContacts: json["data"]["totalContacts"].intValue,
lastSyncAt: lastSyncAt
)
}
@ -391,6 +392,7 @@ struct SyncMessagePayload: Encodable {
struct SyncStatsResponse {
let totalMessages: Int
let totalConversations: Int
let totalContacts: Int
let lastSyncAt: Date?
}

View file

@ -193,6 +193,7 @@ class LocalWebServer {
"syncErrorMessage": syncErrorMessage as Any,
"messageCount": syncManager.stats.messageCount,
"conversationCount": syncManager.stats.conversationCount,
"contactCount": syncManager.stats.contactCount,
"lastSync": syncManager.lastSync.map { ISO8601DateFormatter().string(from: $0) } as Any,
"needsFullDiskAccess": syncManager.syncError == .fullDiskAccessRequired,
"isSyncing": syncManager.isSyncing,

View file

@ -5,6 +5,7 @@ import Foundation
struct SyncStats {
var messageCount: Int = 0
var conversationCount: Int = 0
var contactCount: Int = 0
}
enum SyncError: Equatable {
@ -313,12 +314,13 @@ class SyncManager: ObservableObject {
NSLog("SyncManager: fetchStats starting")
do {
let response = try await apiClient.getStats()
NSLog("SyncManager: fetchStats - got totalMessages: \(response.totalMessages), totalConversations: \(response.totalConversations)")
NSLog("SyncManager: fetchStats - got totalMessages: \(response.totalMessages), totalConversations: \(response.totalConversations), totalContacts: \(response.totalContacts)")
// Update stats from server response
let newStats = SyncStats(
messageCount: response.totalMessages,
conversationCount: response.totalConversations
conversationCount: response.totalConversations,
contactCount: response.totalContacts
)
stats = newStats

View file

@ -255,6 +255,7 @@ export class SyncService {
async getStats(deviceId: string): Promise<{
totalMessages: number;
totalConversations: number;
totalContacts: number;
lastSyncAt: string | null;
}> {
// Count conversations for this device
@ -277,12 +278,16 @@ export class SyncService {
.getCount();
}
// Count all contacts (global, not per-device)
const totalContacts = await this.contactRepository.count();
// Get last sync time
const lastSync = await this.getLastSync(deviceId);
return {
totalMessages,
totalConversations,
totalContacts,
lastSyncAt: lastSync?.toISOString() ?? null,
};
}