@uselingu/react-native-sdk 0.1.4 → 0.1.5
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/README.md +28 -0
- package/lib/commonjs/hooks/useLinguChat.js +57 -15
- package/lib/commonjs/hooks/useLinguChat.js.map +1 -1
- package/lib/commonjs/index.js +23 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/services/LinguAPI.js +32 -1
- package/lib/commonjs/services/LinguAPI.js.map +1 -1
- package/lib/commonjs/types/index.js +34 -0
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/module/hooks/useLinguChat.js +57 -15
- package/lib/module/hooks/useLinguChat.js.map +1 -1
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/services/LinguAPI.js +32 -1
- package/lib/module/services/LinguAPI.js.map +1 -1
- package/lib/module/types/index.js +27 -0
- package/lib/module/types/index.js.map +1 -1
- package/lib/typescript/hooks/useLinguChat.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/services/LinguAPI.d.ts +16 -0
- package/lib/typescript/services/LinguAPI.d.ts.map +1 -1
- package/lib/typescript/types/index.d.ts +28 -0
- package/lib/typescript/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/hooks/useLinguChat.ts +59 -15
- package/src/index.ts +8 -0
- package/src/services/LinguAPI.ts +44 -1
- package/src/types/index.ts +38 -0
|
@@ -7,6 +7,7 @@ import { useState, useEffect, useCallback, useRef } from 'react';
|
|
|
7
7
|
import { LinguAPI } from '../services/LinguAPI';
|
|
8
8
|
import { SessionManager } from '../services/SessionManager';
|
|
9
9
|
import { StorageManager } from '../services/StorageManager';
|
|
10
|
+
import { resolveBaseURL } from '../types';
|
|
10
11
|
import type { Message, WidgetConfig, LinguConfig } from '../types';
|
|
11
12
|
|
|
12
13
|
const LOG_PREFIX = '[useLinguChat]';
|
|
@@ -23,9 +24,15 @@ interface UseLinguChatReturn {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export const useLinguChat = (linguConfig: LinguConfig): UseLinguChatReturn => {
|
|
27
|
+
const baseURL = resolveBaseURL({
|
|
28
|
+
baseURL: linguConfig.baseURL,
|
|
29
|
+
environment: linguConfig.environment,
|
|
30
|
+
});
|
|
31
|
+
|
|
26
32
|
console.log(`${LOG_PREFIX} Initializing hook with config:`, {
|
|
27
33
|
apiKey: linguConfig.apiKey?.substring(0, 20) + '...',
|
|
28
|
-
baseURL
|
|
34
|
+
baseURL,
|
|
35
|
+
environment: linguConfig.environment ?? 'production',
|
|
29
36
|
autoOpen: linguConfig.autoOpen,
|
|
30
37
|
});
|
|
31
38
|
|
|
@@ -37,12 +44,19 @@ export const useLinguChat = (linguConfig: LinguConfig): UseLinguChatReturn => {
|
|
|
37
44
|
const apiRef = useRef<LinguAPI | null>(null);
|
|
38
45
|
const sessionManagerRef = useRef<SessionManager | null>(null);
|
|
39
46
|
const currentSessionIdRef = useRef<string | null>(null);
|
|
47
|
+
// Mirror `messages` into a ref so callbacks created once in the init effect
|
|
48
|
+
// (onSessionEnd, cleanup) always read the latest messages instead of the
|
|
49
|
+
// empty array captured at mount.
|
|
50
|
+
const messagesRef = useRef<Message[]>([]);
|
|
51
|
+
|
|
52
|
+
// Keep the messages ref in sync with state.
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
messagesRef.current = messages;
|
|
55
|
+
}, [messages]);
|
|
40
56
|
|
|
41
57
|
// Initialize API client and session manager
|
|
42
58
|
useEffect(() => {
|
|
43
59
|
console.log(`${LOG_PREFIX} Setting up API client and session manager`);
|
|
44
|
-
|
|
45
|
-
const baseURL = linguConfig.baseURL || 'https://api.uselingu.app/api';
|
|
46
60
|
console.log(`${LOG_PREFIX} API Base URL:`, baseURL);
|
|
47
61
|
|
|
48
62
|
const api = new LinguAPI(linguConfig.apiKey, baseURL);
|
|
@@ -72,10 +86,11 @@ export const useLinguChat = (linguConfig: LinguConfig): UseLinguChatReturn => {
|
|
|
72
86
|
onSessionEnd: async (sessionId) => {
|
|
73
87
|
console.log(`${LOG_PREFIX} Session ending:`, sessionId);
|
|
74
88
|
|
|
75
|
-
// Save messages before ending
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
// Save messages before ending (read from ref for latest value)
|
|
90
|
+
const currentMessages = messagesRef.current;
|
|
91
|
+
if (currentMessages.length > 0) {
|
|
92
|
+
console.log(`${LOG_PREFIX} Saving ${currentMessages.length} messages before session end`);
|
|
93
|
+
await StorageManager.saveMessages(sessionId, currentMessages);
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
currentSessionIdRef.current = null;
|
|
@@ -110,15 +125,16 @@ export const useLinguChat = (linguConfig: LinguConfig): UseLinguChatReturn => {
|
|
|
110
125
|
// Cleanup on unmount
|
|
111
126
|
return () => {
|
|
112
127
|
console.log(`${LOG_PREFIX} Component unmounting, cleaning up...`);
|
|
113
|
-
// Save messages before cleanup
|
|
114
|
-
|
|
128
|
+
// Save messages before cleanup (read from ref for latest value)
|
|
129
|
+
const currentMessages = messagesRef.current;
|
|
130
|
+
if (currentSessionIdRef.current && currentMessages.length > 0) {
|
|
115
131
|
console.log(`${LOG_PREFIX} Saving messages on unmount`);
|
|
116
|
-
StorageManager.saveMessages(currentSessionIdRef.current,
|
|
132
|
+
StorageManager.saveMessages(currentSessionIdRef.current, currentMessages);
|
|
117
133
|
}
|
|
118
134
|
sessionManagerRef.current?.cleanup();
|
|
119
135
|
console.log(`${LOG_PREFIX} Cleanup complete`);
|
|
120
136
|
};
|
|
121
|
-
}, [linguConfig.apiKey,
|
|
137
|
+
}, [linguConfig.apiKey, baseURL]);
|
|
122
138
|
|
|
123
139
|
// Save messages whenever they change
|
|
124
140
|
useEffect(() => {
|
|
@@ -129,18 +145,46 @@ export const useLinguChat = (linguConfig: LinguConfig): UseLinguChatReturn => {
|
|
|
129
145
|
}, [messages]);
|
|
130
146
|
|
|
131
147
|
/**
|
|
132
|
-
* Try to restore previous session
|
|
148
|
+
* Try to restore previous session from backend
|
|
133
149
|
*/
|
|
134
150
|
const restorePreviousSession = async () => {
|
|
135
151
|
try {
|
|
136
152
|
const previousSessionId = await StorageManager.getCurrentSessionId();
|
|
137
|
-
if (previousSessionId) {
|
|
153
|
+
if (previousSessionId && apiRef.current) {
|
|
138
154
|
console.log(`${LOG_PREFIX} Found previous session:`, previousSessionId);
|
|
155
|
+
|
|
156
|
+
// Try to fetch fresh history from backend
|
|
157
|
+
try {
|
|
158
|
+
const history = await apiRef.current.getChatHistory(previousSessionId);
|
|
159
|
+
|
|
160
|
+
if (!history.isNewSession && history.messages.length > 0) {
|
|
161
|
+
console.log(`${LOG_PREFIX} Restored ${history.messages.length} messages from backend`);
|
|
162
|
+
|
|
163
|
+
// Convert backend format to our Message format
|
|
164
|
+
const restoredMessages: Message[] = history.messages.map((msg, index) => ({
|
|
165
|
+
id: `restored-${index}-${Date.now()}`,
|
|
166
|
+
text: msg.content,
|
|
167
|
+
sender: msg.role === 'assistant' ? 'bot' : 'user',
|
|
168
|
+
timestamp: new Date(msg.timestamp),
|
|
169
|
+
}));
|
|
170
|
+
|
|
171
|
+
setMessages(restoredMessages);
|
|
172
|
+
currentSessionIdRef.current = previousSessionId;
|
|
173
|
+
apiRef.current.resumeSession(previousSessionId);
|
|
174
|
+
setIsSessionActive(true);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
} catch (apiError) {
|
|
178
|
+
console.warn(`${LOG_PREFIX} Failed to fetch from backend, trying local storage:`, apiError);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Fallback to local storage
|
|
139
182
|
const savedMessages = await StorageManager.loadMessages(previousSessionId);
|
|
140
183
|
if (savedMessages.length > 0) {
|
|
141
|
-
console.log(`${LOG_PREFIX}
|
|
184
|
+
console.log(`${LOG_PREFIX} Restored ${savedMessages.length} messages from local storage`);
|
|
142
185
|
setMessages(savedMessages);
|
|
143
186
|
currentSessionIdRef.current = previousSessionId;
|
|
187
|
+
setIsSessionActive(true);
|
|
144
188
|
} else {
|
|
145
189
|
console.log(`${LOG_PREFIX} Previous session had no messages`);
|
|
146
190
|
}
|
|
@@ -148,7 +192,7 @@ export const useLinguChat = (linguConfig: LinguConfig): UseLinguChatReturn => {
|
|
|
148
192
|
console.log(`${LOG_PREFIX} No previous session found`);
|
|
149
193
|
}
|
|
150
194
|
} catch (error) {
|
|
151
|
-
console.error(`${LOG_PREFIX}
|
|
195
|
+
console.error(`${LOG_PREFIX} Failed to restore previous session:`, error);
|
|
152
196
|
}
|
|
153
197
|
};
|
|
154
198
|
|
package/src/index.ts
CHANGED
|
@@ -28,9 +28,17 @@ export { StorageManager } from './services/StorageManager';
|
|
|
28
28
|
// Utils
|
|
29
29
|
export * from './utils';
|
|
30
30
|
|
|
31
|
+
// Environment helpers
|
|
32
|
+
export {
|
|
33
|
+
LINGU_API_URLS,
|
|
34
|
+
DEFAULT_LINGU_ENVIRONMENT,
|
|
35
|
+
resolveBaseURL,
|
|
36
|
+
} from './types';
|
|
37
|
+
|
|
31
38
|
// Types
|
|
32
39
|
export type {
|
|
33
40
|
LinguConfig,
|
|
41
|
+
LinguEnvironment,
|
|
34
42
|
ChatPosition,
|
|
35
43
|
ThemeMode,
|
|
36
44
|
Message,
|
package/src/services/LinguAPI.ts
CHANGED
|
@@ -18,7 +18,7 @@ export class LinguAPI {
|
|
|
18
18
|
private apiKey: string;
|
|
19
19
|
private sessionId: string | null = null;
|
|
20
20
|
|
|
21
|
-
constructor(apiKey: string, baseURL: string = 'https://api.
|
|
21
|
+
constructor(apiKey: string, baseURL: string = 'https://lingu-api-prod.fly.dev/api') {
|
|
22
22
|
console.log(`${LOG_PREFIX} Initializing API client:`, {
|
|
23
23
|
baseURL,
|
|
24
24
|
apiKeyPrefix: apiKey.substring(0, 20) + '...',
|
|
@@ -133,6 +133,49 @@ export class LinguAPI {
|
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Resume an existing session (sets sessionId without creating new)
|
|
138
|
+
*/
|
|
139
|
+
resumeSession(sessionId: string): string {
|
|
140
|
+
console.log(`${LOG_PREFIX} Resuming session:`, sessionId);
|
|
141
|
+
this.sessionId = sessionId;
|
|
142
|
+
return sessionId;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get chat history for a session from the backend
|
|
147
|
+
*/
|
|
148
|
+
async getChatHistory(sessionId: string): Promise<{
|
|
149
|
+
sessionId: string;
|
|
150
|
+
messages: Array<{
|
|
151
|
+
role: 'user' | 'assistant';
|
|
152
|
+
content: string;
|
|
153
|
+
timestamp: string;
|
|
154
|
+
}>;
|
|
155
|
+
isNewSession: boolean;
|
|
156
|
+
}> {
|
|
157
|
+
console.log(`${LOG_PREFIX} Fetching chat history for session:`, sessionId);
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
const startTime = Date.now();
|
|
161
|
+
const response = await this.client.get(
|
|
162
|
+
`/chat/widget/history?apiKey=${encodeURIComponent(this.apiKey)}&sessionId=${encodeURIComponent(sessionId)}`
|
|
163
|
+
);
|
|
164
|
+
const duration = Date.now() - startTime;
|
|
165
|
+
|
|
166
|
+
const data = response.data.data;
|
|
167
|
+
console.log(`${LOG_PREFIX} Chat history loaded in ${duration}ms:`, {
|
|
168
|
+
messageCount: data.messages?.length || 0,
|
|
169
|
+
isNewSession: data.isNewSession,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return data;
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error(`${LOG_PREFIX} Failed to get chat history:`, error);
|
|
175
|
+
throw new Error('Failed to load chat history');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
136
179
|
/**
|
|
137
180
|
* Send a message to the AI
|
|
138
181
|
*/
|
package/src/types/index.ts
CHANGED
|
@@ -2,9 +2,47 @@
|
|
|
2
2
|
* Core types for Lingu React Native SDK
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Which Lingu backend the SDK talks to.
|
|
7
|
+
* Ignored when an explicit `baseURL` is provided.
|
|
8
|
+
*/
|
|
9
|
+
export type LinguEnvironment = 'production' | 'staging';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Predefined API base URLs per environment.
|
|
13
|
+
* An explicit `baseURL` on LinguConfig always overrides these.
|
|
14
|
+
*/
|
|
15
|
+
export const LINGU_API_URLS: Record<LinguEnvironment, string> = {
|
|
16
|
+
production: 'https://lingu-api-prod.fly.dev/api',
|
|
17
|
+
staging: 'https://lingu-api.fly.dev/api',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const DEFAULT_LINGU_ENVIRONMENT: LinguEnvironment = 'production';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the API base URL from config.
|
|
24
|
+
* Precedence: explicit `baseURL` > `environment` mapping > production default.
|
|
25
|
+
*/
|
|
26
|
+
export const resolveBaseURL = (opts: {
|
|
27
|
+
baseURL?: string;
|
|
28
|
+
environment?: LinguEnvironment;
|
|
29
|
+
}): string => {
|
|
30
|
+
if (opts.baseURL) return opts.baseURL;
|
|
31
|
+
return LINGU_API_URLS[opts.environment ?? DEFAULT_LINGU_ENVIRONMENT];
|
|
32
|
+
};
|
|
33
|
+
|
|
5
34
|
export interface LinguConfig {
|
|
6
35
|
apiKey: string;
|
|
36
|
+
/**
|
|
37
|
+
* Explicit API base URL. Overrides `environment` when set.
|
|
38
|
+
* Use this for local development (e.g. http://127.0.0.1:3003/api).
|
|
39
|
+
*/
|
|
7
40
|
baseURL?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Select which hosted backend to use: 'production' (default) or 'staging'.
|
|
43
|
+
* Ignored if `baseURL` is provided.
|
|
44
|
+
*/
|
|
45
|
+
environment?: LinguEnvironment;
|
|
8
46
|
autoOpen?: boolean;
|
|
9
47
|
onSessionStart?: (sessionId: string) => void;
|
|
10
48
|
onSessionEnd?: (sessionId: string) => void;
|