@samirify/messenger 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-42TCO5DV.cjs +214 -0
- package/dist/chunk-42TCO5DV.cjs.map +1 -0
- package/dist/chunk-M2F3K6UX.cjs +82 -0
- package/dist/chunk-M2F3K6UX.cjs.map +1 -0
- package/dist/chunk-O5XNJHGZ.js +198 -0
- package/dist/chunk-O5XNJHGZ.js.map +1 -0
- package/dist/chunk-OTJZXVON.js +77 -0
- package/dist/chunk-OTJZXVON.js.map +1 -0
- package/dist/index.cjs +78 -281
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -26
- package/dist/index.d.ts +5 -26
- package/dist/index.js +3 -263
- package/dist/index.js.map +1 -1
- package/dist/native/index.cjs +34 -206
- package/dist/native/index.cjs.map +1 -1
- package/dist/native/index.js +6 -178
- package/dist/native/index.js.map +1 -1
- package/dist/uiController-CM_cznOa.d.cts +26 -0
- package/dist/uiController-CNCLZw4I.d.ts +26 -0
- package/dist/web/index.cjs +63 -259
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts +1 -0
- package/dist/web/index.d.ts +1 -0
- package/dist/web/index.js +10 -228
- package/dist/web/index.js.map +1 -1
- package/package.json +1 -1
package/dist/web/index.js
CHANGED
|
@@ -1,187 +1,11 @@
|
|
|
1
|
+
import { useMessengerUi, useUnreadTotal } from '../chunk-OTJZXVON.js';
|
|
2
|
+
export { MessengerUiProvider, useMessengerUi, useOptionalMessengerUi } from '../chunk-OTJZXVON.js';
|
|
3
|
+
import { useMessengerConfig, useThreads, useOpenThread, MessengerI18n, useThread, useSendMessage } from '../chunk-O5XNJHGZ.js';
|
|
4
|
+
export { MessengerProvider, useMessengerConfig, useOptionalMessengerConfig } from '../chunk-O5XNJHGZ.js';
|
|
1
5
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
2
|
-
import { createContext, useState, useRef, useEffect, useContext, useMemo } from 'react';
|
|
3
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import {
|
|
7
|
+
import { useState, useRef, useEffect } from 'react';
|
|
5
8
|
|
|
6
|
-
// src/web/ChatDock.tsx
|
|
7
|
-
var MessengerContext = createContext(null);
|
|
8
|
-
function useMessengerConfig() {
|
|
9
|
-
const ctx = useContext(MessengerContext);
|
|
10
|
-
if (!ctx) {
|
|
11
|
-
throw new Error(
|
|
12
|
-
"@gymlium/messenger: useMessengerConfig must be used within MessengerProvider"
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
return ctx;
|
|
16
|
-
}
|
|
17
|
-
function useOptionalMessengerConfig() {
|
|
18
|
-
return useContext(MessengerContext);
|
|
19
|
-
}
|
|
20
|
-
var MessengerUiContext = createContext(null);
|
|
21
|
-
function useMessengerUi() {
|
|
22
|
-
const ctx = useContext(MessengerUiContext);
|
|
23
|
-
if (!ctx) {
|
|
24
|
-
throw new Error(
|
|
25
|
-
"@gymlium/messenger: useMessengerUi must be used within MessengerUiProvider"
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
return ctx;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// src/api.ts
|
|
32
|
-
async function listThreads(http) {
|
|
33
|
-
const res = await http.getJson(
|
|
34
|
-
"/api/messages/threads"
|
|
35
|
-
);
|
|
36
|
-
return { ...res, data: res.data?.threads ?? [] };
|
|
37
|
-
}
|
|
38
|
-
async function openThread(http, clientUserId) {
|
|
39
|
-
return http.postJson("/api/messages/threads", {
|
|
40
|
-
...clientUserId ? { client_user_id: clientUserId } : {}
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
async function getThread(http, threadId, limit = 50, beforeId) {
|
|
44
|
-
const params = new URLSearchParams({ limit: String(limit) });
|
|
45
|
-
return http.getJson(`/api/messages/threads/${threadId}?${params.toString()}`);
|
|
46
|
-
}
|
|
47
|
-
async function sendMessage(http, threadId, body, image) {
|
|
48
|
-
const path = `/api/messages/threads/${threadId}/messages`;
|
|
49
|
-
if (image?.uri || image?.file) {
|
|
50
|
-
const form = new FormData();
|
|
51
|
-
if (body.trim()) form.append("body", body.trim());
|
|
52
|
-
const filename = image.name ?? image.uri?.split("/").pop() ?? "photo.jpg";
|
|
53
|
-
const match = /\.(\w+)$/.exec(filename);
|
|
54
|
-
const type = image.type ?? (match ? `image/${match[1]}` : "image/jpeg");
|
|
55
|
-
if (image.file) {
|
|
56
|
-
form.append("image", image.file, filename);
|
|
57
|
-
} else if (image.uri.startsWith("blob:") || image.uri.startsWith("data:")) {
|
|
58
|
-
const blob = await fetch(image.uri).then((r) => r.blob());
|
|
59
|
-
form.append("image", blob, filename);
|
|
60
|
-
} else {
|
|
61
|
-
form.append("image", {
|
|
62
|
-
uri: image.uri,
|
|
63
|
-
name: filename,
|
|
64
|
-
type
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
return http.postForm(path, form);
|
|
68
|
-
}
|
|
69
|
-
return http.postJson(path, { body });
|
|
70
|
-
}
|
|
71
|
-
function sumUnread(threads) {
|
|
72
|
-
return threads.reduce((sum, th) => sum + Number(th.unread_count ?? 0), 0);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// src/queryKeys.ts
|
|
76
|
-
var messengerKeys = {
|
|
77
|
-
all: ["messenger"],
|
|
78
|
-
threads: () => [...messengerKeys.all, "threads"],
|
|
79
|
-
thread: (id) => [...messengerKeys.all, "thread", id]
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
// src/hooks/useUnreadTotal.ts
|
|
83
|
-
function useUnreadTotal(options) {
|
|
84
|
-
const cfg = useOptionalMessengerConfig();
|
|
85
|
-
const featureOn = cfg?.enabled === true;
|
|
86
|
-
const http = cfg?.http;
|
|
87
|
-
const enabled = (options?.enabled ?? true) && featureOn && !!http;
|
|
88
|
-
const query = useQuery({
|
|
89
|
-
queryKey: messengerKeys.threads(),
|
|
90
|
-
queryFn: async () => {
|
|
91
|
-
const res = await listThreads(http);
|
|
92
|
-
if (!res.success) {
|
|
93
|
-
throw new Error(res.message ?? "Failed to load threads");
|
|
94
|
-
}
|
|
95
|
-
return res.data;
|
|
96
|
-
},
|
|
97
|
-
enabled
|
|
98
|
-
});
|
|
99
|
-
const total = useMemo(
|
|
100
|
-
() => query.data ? sumUnread(query.data) : 0,
|
|
101
|
-
[query.data]
|
|
102
|
-
);
|
|
103
|
-
return {
|
|
104
|
-
total,
|
|
105
|
-
isLoading: query.isLoading,
|
|
106
|
-
isFetching: query.isFetching,
|
|
107
|
-
refetch: query.refetch,
|
|
108
|
-
enabled: featureOn
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// src/i18nKeys.ts
|
|
113
|
-
var MessengerI18n = {
|
|
114
|
-
off: {
|
|
115
|
-
key: "mobile.messenger.off",
|
|
116
|
-
fallback: "Messaging is not enabled for this account."
|
|
117
|
-
},
|
|
118
|
-
chat: { key: "mobile.messenger.chat", fallback: "Chat" },
|
|
119
|
-
/** Dock / list title — prefer over inbox for web UI copy. */
|
|
120
|
-
messages: { key: "mobile.messenger.messages", fallback: "Chat" },
|
|
121
|
-
messagesHint: {
|
|
122
|
-
key: "mobile.messenger.messagesHint",
|
|
123
|
-
fallback: "Message your coach anytime"
|
|
124
|
-
},
|
|
125
|
-
messageCoach: {
|
|
126
|
-
key: "mobile.messenger.messageCoach",
|
|
127
|
-
fallback: "Message your coach"
|
|
128
|
-
},
|
|
129
|
-
empty: {
|
|
130
|
-
key: "mobile.messenger.empty",
|
|
131
|
-
fallback: "No conversations yet"
|
|
132
|
-
},
|
|
133
|
-
yourCoach: {
|
|
134
|
-
key: "mobile.messenger.yourCoach",
|
|
135
|
-
fallback: "Your coach"
|
|
136
|
-
},
|
|
137
|
-
photo: { key: "mobile.messenger.photo", fallback: "Photo" },
|
|
138
|
-
noMessages: {
|
|
139
|
-
key: "mobile.messenger.noMessages",
|
|
140
|
-
fallback: "No messages yet"
|
|
141
|
-
},
|
|
142
|
-
startConversation: {
|
|
143
|
-
key: "mobile.messenger.startConversation",
|
|
144
|
-
fallback: "Say hello \u2014 send the first message."
|
|
145
|
-
},
|
|
146
|
-
typeMessage: {
|
|
147
|
-
key: "mobile.messenger.typeMessage",
|
|
148
|
-
fallback: "Type a message\u2026"
|
|
149
|
-
},
|
|
150
|
-
client: { key: "mobile.trainer.client", fallback: "Client" },
|
|
151
|
-
back: { key: "common.back", fallback: "Back" }
|
|
152
|
-
};
|
|
153
|
-
function useThreads(options) {
|
|
154
|
-
const { http, enabled: featureOn } = useMessengerConfig();
|
|
155
|
-
const enabled = featureOn;
|
|
156
|
-
return useQuery({
|
|
157
|
-
queryKey: messengerKeys.threads(),
|
|
158
|
-
queryFn: async () => {
|
|
159
|
-
const res = await listThreads(http);
|
|
160
|
-
if (!res.success) {
|
|
161
|
-
throw new Error(res.message ?? "Failed to load threads");
|
|
162
|
-
}
|
|
163
|
-
return res.data;
|
|
164
|
-
},
|
|
165
|
-
enabled
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
function useOpenThread() {
|
|
169
|
-
const { http, navigation } = useMessengerConfig();
|
|
170
|
-
const qc = useQueryClient();
|
|
171
|
-
return useMutation({
|
|
172
|
-
mutationFn: async (clientUserId) => {
|
|
173
|
-
const res = await openThread(http, clientUserId);
|
|
174
|
-
if (!res.success || !res.data?.thread?.id) {
|
|
175
|
-
throw new Error(res.message ?? "Could not open thread");
|
|
176
|
-
}
|
|
177
|
-
return res.data.thread;
|
|
178
|
-
},
|
|
179
|
-
onSuccess: (thread) => {
|
|
180
|
-
void qc.invalidateQueries({ queryKey: messengerKeys.threads() });
|
|
181
|
-
navigation.openThread(thread.id);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
9
|
function initials(name) {
|
|
186
10
|
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
187
11
|
if (parts.length === 0) return "?";
|
|
@@ -327,7 +151,7 @@ function ThreadList({
|
|
|
327
151
|
}) {
|
|
328
152
|
const { user, t, theme } = useMessengerConfig();
|
|
329
153
|
const threadsQuery = useThreads();
|
|
330
|
-
const
|
|
154
|
+
const openThread = useOpenThread();
|
|
331
155
|
const threads = threadsQuery.data ?? [];
|
|
332
156
|
return /* @__PURE__ */ jsxs(
|
|
333
157
|
"div",
|
|
@@ -344,8 +168,8 @@ function ThreadList({
|
|
|
344
168
|
"button",
|
|
345
169
|
{
|
|
346
170
|
type: "button",
|
|
347
|
-
onClick: () =>
|
|
348
|
-
disabled:
|
|
171
|
+
onClick: () => openThread.mutate(void 0),
|
|
172
|
+
disabled: openThread.isPending,
|
|
349
173
|
style: {
|
|
350
174
|
width: "100%",
|
|
351
175
|
padding: "13px 16px",
|
|
@@ -361,10 +185,10 @@ function ThreadList({
|
|
|
361
185
|
fontFamily: theme.fonts.bodySemiBold,
|
|
362
186
|
fontWeight: 600,
|
|
363
187
|
fontSize: 14,
|
|
364
|
-
cursor:
|
|
188
|
+
cursor: openThread.isPending ? "default" : "pointer",
|
|
365
189
|
boxShadow: `0 10px 24px color-mix(in srgb, ${theme.accentColor} 32%, transparent)`
|
|
366
190
|
},
|
|
367
|
-
children:
|
|
191
|
+
children: openThread.isPending ? "\u2026" : t(
|
|
368
192
|
MessengerI18n.messageCoach.key,
|
|
369
193
|
MessengerI18n.messageCoach.fallback
|
|
370
194
|
)
|
|
@@ -461,48 +285,6 @@ function ThreadList({
|
|
|
461
285
|
}
|
|
462
286
|
);
|
|
463
287
|
}
|
|
464
|
-
var POLL_MS = 18e3;
|
|
465
|
-
function useThread(threadId, options) {
|
|
466
|
-
const { http, enabled: featureOn } = useMessengerConfig();
|
|
467
|
-
const enabled = featureOn && Number.isFinite(threadId) && threadId > 0;
|
|
468
|
-
const poll = true;
|
|
469
|
-
return useQuery({
|
|
470
|
-
queryKey: messengerKeys.thread(threadId),
|
|
471
|
-
queryFn: async () => {
|
|
472
|
-
const res = await getThread(http, threadId);
|
|
473
|
-
if (!res.success) {
|
|
474
|
-
throw new Error(res.message ?? "Failed to load thread");
|
|
475
|
-
}
|
|
476
|
-
return res.data;
|
|
477
|
-
},
|
|
478
|
-
enabled,
|
|
479
|
-
refetchInterval: enabled && poll ? POLL_MS : false
|
|
480
|
-
});
|
|
481
|
-
}
|
|
482
|
-
function useSendMessage(threadId) {
|
|
483
|
-
const { http } = useMessengerConfig();
|
|
484
|
-
const qc = useQueryClient();
|
|
485
|
-
return useMutation({
|
|
486
|
-
mutationFn: async (input) => {
|
|
487
|
-
const res = await sendMessage(http, threadId, input.body, input.image);
|
|
488
|
-
if (!res.success || !res.data?.message) {
|
|
489
|
-
throw new Error(res.message ?? "Send failed");
|
|
490
|
-
}
|
|
491
|
-
return res.data.message;
|
|
492
|
-
},
|
|
493
|
-
onSuccess: (message) => {
|
|
494
|
-
qc.setQueryData(
|
|
495
|
-
messengerKeys.thread(threadId),
|
|
496
|
-
(prev) => {
|
|
497
|
-
if (!prev) return prev;
|
|
498
|
-
if (prev.messages.some((m) => m.id === message.id)) return prev;
|
|
499
|
-
return { ...prev, messages: [...prev.messages, message] };
|
|
500
|
-
}
|
|
501
|
-
);
|
|
502
|
-
void qc.invalidateQueries({ queryKey: messengerKeys.threads() });
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
288
|
function ChatBubbleIcon({ size = 26 }) {
|
|
507
289
|
return /* @__PURE__ */ jsxs(
|
|
508
290
|
"svg",
|