Skip to content

Commit 3c8d3de

Browse files
committed
refactor: consolidate all API functions into client.ts
Move all WAHA API calls from view files and services into src/client.ts for centralized session management. Functions now use getSession() to retrieve session from appState internally. Moved functions: - loadSessions, logoutSession, deleteSession (from SessionsView) - loadChats (from ChatsView) - loadContacts, loadChatDetails, loadOlderMessages, sendMessage (from ConversationView) - pollChats, fetchMyProfile (from PollingService) Updated imports and call signatures across: - src/index.ts - src/views/ChatsView.ts - src/views/ConversationView.ts - src/views/SessionsView.ts - src/views/QRCodeView.ts - src/views/ChatListManager.ts - src/services/PollingService.ts Also added copyToClipboard utility for platform-specific clipboard operations.
1 parent 241e6fd commit 3c8d3de

9 files changed

Lines changed: 486 additions & 403 deletions

File tree

src/client.ts

Lines changed: 420 additions & 23 deletions
Large diffs are not rendered by default.

src/index.ts

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,23 @@ import {
1717
pinMessage,
1818
deleteMessage,
1919
reactToMessage,
20+
copyToClipboard,
21+
loadMessages,
22+
loadChats,
23+
loadSessions,
24+
logoutSession,
25+
deleteSession,
26+
loadContacts,
27+
loadOlderMessages,
2028
} from "./client"
2129
import { appState } from "./state/AppState"
2230
import { Footer } from "./components/Footer"
2331
import { ContextMenu, handleContextMenuKey, getSelectedMenuItem } from "./components/ContextMenu"
24-
import { deleteSession, logoutSession, SessionsView } from "./views/SessionsView"
25-
import { loadSessions } from "./views/SessionsView"
26-
import { loadChats, focusSearchInput, blurSearchInput, clearSearchInput } from "./views/ChatsView"
32+
import { SessionsView } from "./views/SessionsView"
33+
import { focusSearchInput, blurSearchInput, clearSearchInput } from "./views/ChatsView"
2734
import {
28-
loadMessages,
29-
loadContacts,
3035
scrollConversation,
3136
destroyConversationScrollBox,
32-
loadOlderMessages,
3337
focusMessageInput,
3438
blurMessageInput,
3539
} from "./views/ConversationView"
@@ -234,7 +238,7 @@ async function main() {
234238
debugLog("App", `Found working session: ${workingSession.name}, switching to chats view`)
235239
appState.setCurrentSession(workingSession.name)
236240
appState.setCurrentView("chats")
237-
await loadChats(workingSession.name)
241+
await loadChats()
238242
pollingService.start(workingSession.name)
239243
} else {
240244
// No working session - show QR view for login
@@ -337,9 +341,7 @@ async function main() {
337341

338342
// Execute context menu action
339343
const executeContextMenuAction = async (actionId: string, contextMenu: ContextMenuState) => {
340-
if (!state.currentSession || !contextMenu?.targetId) return
341-
342-
const session = state.currentSession
344+
if (!contextMenu?.targetId) return
343345
const targetId = contextMenu.targetId
344346

345347
debugLog("ContextMenu", `Executing action: ${actionId} on ${contextMenu.type} ${targetId}`)
@@ -352,21 +354,21 @@ async function main() {
352354
const chat = contextMenu.targetData as { _chat?: { archived?: boolean } }
353355
const isArchivedChat = chat?._chat?.archived === true
354356
if (isArchivedChat) {
355-
await unarchiveChat(session, targetId)
357+
await unarchiveChat(targetId)
356358
} else {
357-
await archiveChat(session, targetId)
359+
await archiveChat(targetId)
358360
}
359361
// Refresh chat list
360-
await loadChats(session)
362+
await loadChats()
361363
break
362364
}
363365
case "unread":
364-
await markChatUnread(session, targetId)
365-
await loadChats(session)
366+
await markChatUnread(targetId)
367+
await loadChats()
366368
break
367369
case "delete":
368-
await deleteChat(session, targetId)
369-
await loadChats(session)
370+
await deleteChat(targetId)
371+
await loadChats()
370372
break
371373
}
372374
} else if (contextMenu.type === "message") {
@@ -384,45 +386,53 @@ async function main() {
384386
break
385387
}
386388
case "copy": {
387-
// Copy message text to clipboard
389+
// Copy message text to system clipboard
388390
const message = contextMenu.targetData as { body?: string }
389391
if (message?.body) {
390-
// For terminal apps, we can't easily access clipboard
391-
// But we can log it or implement native clipboard later
392-
debugLog("ContextMenu", `Copy text: ${message.body}`)
392+
const copied = await copyToClipboard(message.body)
393+
debugLog("ContextMenu", copied ? `Copied to clipboard` : `Clipboard copy failed`)
393394
}
394395
break
395396
}
396397
case "star": {
397398
const message = contextMenu.targetData as { isStarred?: boolean }
398399
const isStarred = message?.isStarred === true
399400
if (state.currentChatId) {
400-
await starMessage(session, targetId, state.currentChatId, !isStarred)
401+
await starMessage(targetId, state.currentChatId, !isStarred)
401402
// Refresh messages
402-
await loadMessages(session, state.currentChatId)
403+
await loadMessages(state.currentChatId)
403404
}
404405
break
405406
}
406407
case "pin": {
407408
if (state.currentChatId) {
408-
await pinMessage(session, state.currentChatId, targetId)
409-
await loadMessages(session, state.currentChatId)
409+
await pinMessage(state.currentChatId, targetId)
410+
await loadMessages(state.currentChatId)
410411
}
411412
break
412413
}
413414
case "react": {
414415
// For now, add a thumbs up reaction
415416
// Could show a sub-menu for emoji selection later
416-
await reactToMessage(session, targetId, "👍")
417+
await reactToMessage(targetId, "👍")
417418
break
418419
}
419420
case "delete": {
420421
if (state.currentChatId) {
421-
await deleteMessage(session, state.currentChatId, targetId)
422-
await loadMessages(session, state.currentChatId)
422+
await deleteMessage(state.currentChatId, targetId)
423+
await loadMessages(state.currentChatId)
423424
}
424425
break
425426
}
427+
case "forward": {
428+
// TODO: Implement chat picker UI for selecting destination
429+
// For now, just log that forward was requested
430+
debugLog(
431+
"ContextMenu",
432+
`Forward requested for message: ${targetId} - need chat picker UI`
433+
)
434+
break
435+
}
426436
}
427437
}
428438
} catch (error) {
@@ -459,7 +469,7 @@ async function main() {
459469
if (state.currentView === "sessions") {
460470
await loadSessions()
461471
} else if (state.currentView === "chats" && state.currentSession) {
462-
await loadChats(state.currentSession)
472+
await loadChats()
463473
}
464474
}
465475

@@ -623,7 +633,7 @@ async function main() {
623633
appState.setCurrentSession(selectedSession.name)
624634
appState.setCurrentView("chats")
625635
appState.setSelectedChatIndex(0) // Reset chat selection
626-
await loadChats(selectedSession.name)
636+
await loadChats()
627637
pollingService.start(selectedSession.name)
628638
}
629639
} else if (state.currentView === "chats" && state.chats.length > 0) {
@@ -648,8 +658,8 @@ async function main() {
648658
// Destroy old scroll box before loading new messages
649659
destroyConversationScrollBox()
650660
// Load contacts in background to populate cache
651-
loadContacts(state.currentSession)
652-
await loadMessages(state.currentSession, chatId)
661+
loadContacts()
662+
await loadMessages(chatId)
653663
}
654664
}
655665
}
@@ -822,7 +832,7 @@ async function main() {
822832
if (state.currentSession) {
823833
appState.setCurrentView("chats")
824834
appState.setSelectedChatIndex(0)
825-
await loadChats(state.currentSession)
835+
await loadChats()
826836
}
827837
}
828838
})

src/services/PollingService.ts

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44
*/
55

66
import { appState } from "../state/AppState"
7-
import { getClient } from "../client"
8-
import { loadMessages } from "../views/ConversationView"
7+
import { loadMessages, pollChats, fetchMyProfile } from "../client"
98
import { debugLog } from "../utils/debug"
10-
import type { ChatSummary } from "@muhammedaksam/waha-node"
119

1210
// Polling intervals in milliseconds
1311
const CHATS_POLL_INTERVAL = 3000 // 3 seconds for chat list
@@ -24,13 +22,13 @@ class PollingService {
2422
debugLog("Polling", `Starting polling service for session: ${sessionName}`)
2523

2624
// Fetch current user's profile for self-chat detection
27-
this.fetchMyProfile(sessionName)
25+
fetchMyProfile()
2826

2927
// Start chats polling
30-
this.chatsTimer = setInterval(() => this.pollChats(sessionName), CHATS_POLL_INTERVAL)
28+
this.chatsTimer = setInterval(() => this.doPollChats(), CHATS_POLL_INTERVAL)
3129

3230
// Start messages polling (will only actually poll if a chat is selected)
33-
this.messagesTimer = setInterval(() => this.pollMessages(sessionName), MESSAGES_POLL_INTERVAL)
31+
this.messagesTimer = setInterval(() => this.pollMessages(), MESSAGES_POLL_INTERVAL)
3432
}
3533

3634
public stop(): void {
@@ -45,30 +43,20 @@ class PollingService {
4543
debugLog("Polling", "Stopped polling service")
4644
}
4745

48-
private async pollChats(sessionName: string): Promise<void> {
46+
private async doPollChats(): Promise<void> {
4947
if (this.isPollingChats) return
5048
this.isPollingChats = true
5149

5250
try {
53-
const client = getClient()
54-
const response = await client.chats.chatsControllerGetChatsOverview(sessionName, {
55-
limit: 1000,
56-
})
57-
const chats = (response.data as unknown as ChatSummary[]) || []
58-
59-
// Update state - Reactivity will handle UI updates if data changed
60-
// Note: We might need to optimize this if it causes too many re-renders
61-
// But ChatListManager has its own hash check to prevent unnecessary rebuilds
62-
appState.setChats(chats)
51+
await pollChats()
6352
} catch {
64-
// Silent error for polling to avoid spamming logs
65-
// debugLog("Polling", `Error polling chats: ${error}`)
53+
// Silent error for polling
6654
} finally {
6755
this.isPollingChats = false
6856
}
6957
}
7058

71-
private async pollMessages(sessionName: string): Promise<void> {
59+
private async pollMessages(): Promise<void> {
7260
if (this.isPollingMessages) return
7361

7462
const state = appState.getState()
@@ -84,26 +72,13 @@ class PollingService {
8472
try {
8573
// We use loadMessages which already updates the state
8674
// It fetches the latest 50 messages, which should include any new ones
87-
await loadMessages(sessionName, currentChatId)
75+
await loadMessages(currentChatId)
8876
} catch {
8977
// Silent error
9078
} finally {
9179
this.isPollingMessages = false
9280
}
9381
}
94-
95-
private async fetchMyProfile(sessionName: string): Promise<void> {
96-
try {
97-
const client = getClient()
98-
const response = await client.profile.profileControllerGetMyProfile(sessionName)
99-
if (response.data) {
100-
appState.setMyProfile(response.data)
101-
debugLog("Polling", `Fetched my profile: ${response.data.name} (${response.data.id})`)
102-
}
103-
} catch {
104-
debugLog("Polling", "Failed to fetch profile")
105-
}
106-
}
10782
}
10883

10984
export const pollingService = new PollingService()

src/utils/contacts.ts

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/views/ChatListManager.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ import {
2222
} from "../utils/formatters"
2323
import { debugLog } from "../utils/debug"
2424
import { appState } from "../state/AppState"
25-
import { loadMessages, loadContacts, destroyConversationScrollBox } from "./ConversationView"
25+
import { destroyConversationScrollBox } from "./ConversationView"
2626
import { ROW_HEIGHT } from "../utils/chatListScroll"
27+
import { loadContacts, loadMessages } from "../client"
2728

2829
interface ChatRowData {
2930
box: BoxRenderable
@@ -242,8 +243,8 @@ class ChatListManager {
242243
appState.setSelectedChatIndex(chatIndex)
243244

244245
// Load contacts and messages
245-
loadContacts(currentState.currentSession)
246-
loadMessages(currentState.currentSession, chatId)
246+
loadContacts()
247+
loadMessages(chatId)
247248
}
248249
}
249250
},

src/views/ChatsView.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ import { appState } from "../state/AppState"
99
import { getRenderer } from "../state/RendererContext"
1010
import { WhatsAppTheme, Icons } from "../config/theme"
1111
import { debugLog } from "../utils/debug"
12-
import { getClient } from "../client"
1312
import type { ActiveFilter } from "../state/AppState"
1413
import type { ChatSummary } from "@muhammedaksam/waha-node"
1514
import { chatListManager } from "./ChatListManager"
1615
import { Logo } from "../components/Logo"
1716
import { filterChats, countUnreadInArchived, isArchived } from "../utils/filterChats"
18-
import { loadAllContacts } from "../utils/contacts"
1917
import {
2018
searchChatsWithSections,
2119
flattenSearchResults,
@@ -418,22 +416,3 @@ export function ChatsView() {
418416
...components
419417
)
420418
}
421-
422-
// Load chats from WAHA API
423-
export async function loadChats(sessionName: string): Promise<void> {
424-
try {
425-
debugLog("Chats", `Loading chats for session: ${sessionName}`)
426-
const client = getClient()
427-
const response = await client.chats.chatsControllerGetChats(sessionName, {})
428-
const chats = (response.data as unknown as ChatSummary[]) || []
429-
debugLog("Chats", `Loaded ${chats.length} chats`)
430-
appState.setChats(chats)
431-
432-
// Also load all contacts for comprehensive search
433-
const allContacts = await loadAllContacts(sessionName)
434-
appState.setAllContacts(allContacts)
435-
} catch (error) {
436-
debugLog("Chats", `Failed to load chats: ${error}`)
437-
appState.setChats([])
438-
}
439-
}

0 commit comments

Comments
 (0)