@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.
@@ -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: linguConfig.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
- if (messages.length > 0) {
77
- console.log(`${LOG_PREFIX} Saving ${messages.length} messages before session end`);
78
- await StorageManager.saveMessages(sessionId, messages);
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
- if (currentSessionIdRef.current && messages.length > 0) {
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, messages);
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, linguConfig.baseURL]);
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} Restored ${savedMessages.length} messages from previous session`);
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} Failed to restore previous session:`, error);
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,
@@ -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.uselingu.app/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
  */
@@ -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;