1import { useAuthStore } from "@/stores/auth";
2import { globalStore } from "@/stores/globalStore";
3import { reactive, ref, watch } from "vue";
4
5const currentViewers = reactive<Record<string, string[]> | null>({});
6
7export function useActiveViewers(ticketId: string) {
8 const { $socket } = globalStore();
9
10 const { userId } = useAuthStore();
11 $socket.on("ticket_viewers", (data: { ticket_id: string; users: string }) => {
12 if (data.ticket_id === ticketId) {
13 const viewers = JSON.parse(data.users).filter(
14 (u: string) => u !== userId
15 );
16 currentViewers[ticketId] = viewers;
17 }
18 });
19
20 const handleBeforeUnload = (_ticketId: string) => {
21 $socket.emit("stop_view_ticket", _ticketId);
22 };
23 const startViewing = (_ticketId: string) => {
24 $socket.emit("view_ticket", _ticketId);
25 window.addEventListener("beforeunload", () =>
26 handleBeforeUnload(_ticketId)
27 );
28 };
29
30 const stopViewing = (_ticketId: string) => {
31 $socket.emit("stop_view_ticket", _ticketId);
32
33 window.removeEventListener("beforeunload", () =>
34 handleBeforeUnload(_ticketId)
35 );
36 };
37
38 return {
39 currentViewers,
40 startViewing,
41 stopViewing,
42 };
43}
44
45export function useNotifyTicketUpdate(ticketId: string) {
46 const { $socket } = globalStore();
47 const notifyTicketUpdate = (field: string, value: string) => {
48 $socket.emit("notify_ticket_update", ticketId, field, value);
49 };
50 // how to check if $socket is already listening to avoid multiple listeners
51 return { notifyTicketUpdate };
52}
53
54export function useTyping(ticketId: string) {
55 const { $socket } = globalStore();
56 const { userId } = useAuthStore();
57
58 const typingCounter = ref(0);
59 const isTyping = ref(false);
60 const typingUsers = reactive<string[]>([]);
61 let timeout: any = null;
62
63 // Listen for typing events from other users
64 const onTyping = (data: { ticket_id: string; user: string }) => {
65 if (data.ticket_id === ticketId && data.user !== userId) {
66 // Add user to typing list if not already present
67 if (!typingUsers.includes(data.user)) {
68 typingUsers.push(data.user);
69 }
70 }
71 };
72 $socket.on("opendesk_ticket_typing", onTyping);
73
74 // Listen for typing stopped events
75 const onTypingStopped = (data: { ticket_id: string; user: string }) => {
76 if (data.ticket_id === ticketId) {
77 // Remove user from typing list
78 const index = typingUsers.indexOf(data.user);
79 if (index > -1) {
80 typingUsers.splice(index, 1);
81 }
82 }
83 };
84 $socket.on("opendesk_ticket_typing_stopped", onTypingStopped);
85
86 const emitStartTyping = () => {
87 $socket?.emit("opendesk_ticket_typing", ticketId);
88 };
89
90 const emitStopTyping = () => {
91 $socket?.emit("opendesk_ticket_typing_stopped", ticketId);
92 };
93
94 const onUserType = () => {
95 if (!isTyping.value) {
96 isTyping.value = true;
97 typingCounter.value = typingCounter.value + 1;
98 emitStartTyping();
99 }
100 };
101
102 // Reset the typing state after 10 seconds of inactivity
103 // If agent is not typing for 10 seconds, we assume they have stopped typing
104 watch(typingCounter, (newVal) => {
105 if (timeout) {
106 clearTimeout(timeout);
107 }
108
109 if (newVal > 0) {
110 timeout = setTimeout(() => {
111 isTyping.value = false;
112 typingCounter.value = 0;
113 emitStopTyping();
114 }, 10000);
115 }
116 });
117
118 const stopTyping = () => {
119 emitStopTyping();
120 isTyping.value = false;
121 typingCounter.value = 0;
122 if (timeout) {
123 clearTimeout(timeout);
124 }
125 };
126
127 const cleanup = () => {
128 stopTyping();
129 if (timeout) {
130 clearTimeout(timeout);
131 }
132 // Remove socket listeners
133 $socket.off("opendesk_ticket_typing", onTyping);
134 $socket.off("opendesk_ticket_typing_stopped", onTypingStopped);
135 };
136
137 return {
138 stopTyping,
139 onUserType,
140 typingUsers,
141 cleanup,
142 };
143}
144