@akhilpulse/samadhaan-session-replay 0.2.0 → 0.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akhilpulse/samadhaan-session-replay",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "React Native SDK for Samadhaan Guided Support — session recording, live remote screen-share, take-control, and guided-tour overlays.",
5
5
  "main": "src/index.ts",
6
6
  "module": "src/index.ts",
@@ -35,7 +35,7 @@ import AsyncStorage from "@react-native-async-storage/async-storage";
35
35
  import { captureRef } from "react-native-view-shot";
36
36
 
37
37
  const STORAGE_KEY = "@akhilpulse/samadhaan-session-replay/sessionId";
38
- const SDK_VERSION = "0.2.0";
38
+ const SDK_VERSION = "0.2.1";
39
39
 
40
40
  /** Default Samadhaan production endpoint. Override via `config.serverUrl` for staging. */
41
41
  export const DEFAULT_SERVER_URL = "https://samadhaan.pulseenergy.io";
@@ -124,6 +124,8 @@ export function SessionReplayProvider({ config, children }: { config: Config; ch
124
124
  }, [flushEvents]);
125
125
 
126
126
  // ---- Session init / resume ----
127
+ // initTick is bumped whenever we recycle (sessionId went to null at runtime) so this effect re-fires.
128
+ const [initTick, setInitTick] = useState(0);
127
129
  useEffect(() => {
128
130
  let cancelled = false;
129
131
  (async () => {
@@ -134,8 +136,17 @@ export function SessionReplayProvider({ config, children }: { config: Config; ch
134
136
  const r = await fetch(`${serverUrl}/api/guided-support/sessions/${stored}/status`);
135
137
  if (r.ok) {
136
138
  const j = await r.json();
137
- if (!cancelled) { setSessionId(stored); setStatus(j.status || "active"); setIngestToken(j.ingestToken); }
138
- return;
139
+ // If the stored session was already ended (server-side idle timeout while app
140
+ // was backgrounded), drop it and fall through to create a fresh one.
141
+ if (j.status === "ended") {
142
+ try { await AsyncStorage.removeItem(STORAGE_KEY); } catch {}
143
+ } else {
144
+ if (!cancelled) { setSessionId(stored); setStatus(j.status || "active"); setIngestToken(j.ingestToken); }
145
+ return;
146
+ }
147
+ } else {
148
+ // 404 / not found — clear stale id and create fresh.
149
+ try { await AsyncStorage.removeItem(STORAGE_KEY); } catch {}
139
150
  }
140
151
  }
141
152
  const res = await fetch(`${serverUrl}/api/guided-support/sessions`, {
@@ -164,7 +175,7 @@ export function SessionReplayProvider({ config, children }: { config: Config; ch
164
175
  }
165
176
  })();
166
177
  return () => { cancelled = true; };
167
- }, [config.appId, serverUrl, config.appVersion, config.user]);
178
+ }, [config.appId, serverUrl, config.appVersion, config.user, initTick]);
168
179
 
169
180
  // ---- Keyboard tracking (iOS auto-pause) ----
170
181
  useEffect(() => {
@@ -182,11 +193,12 @@ export function SessionReplayProvider({ config, children }: { config: Config; ch
182
193
  if (r.ok) {
183
194
  const j = await r.json();
184
195
  if (j.status === "ended") {
185
- // Server idle-ended this session. Drop it and create a fresh one on next mount cycle.
196
+ // Server idle-ended this session. Drop it and immediately recycle (init effect re-fires via initTick).
186
197
  try { await AsyncStorage.removeItem(STORAGE_KEY); } catch {}
187
198
  setSessionId(null);
188
199
  setIngestToken(null);
189
200
  setStatus("idle");
201
+ setInitTick(t => t + 1);
190
202
  } else {
191
203
  setStatus(j.status);
192
204
  }
@@ -208,7 +220,14 @@ export function SessionReplayProvider({ config, children }: { config: Config; ch
208
220
  try {
209
221
  const ref = rootRef.current;
210
222
  if (ref) {
211
- const uri = await captureRef(ref as any, { format: "jpg", quality: liveMode ? 0.4 : 0.7, result: "base64" });
223
+ // Cap capture width to keep payloads small. Dashboard viewport is 960px;
224
+ // 540px wide JPEGs render fine and load 4-6× faster than full-resolution captures.
225
+ const uri = await captureRef(ref as any, {
226
+ format: "jpg",
227
+ quality: liveMode ? 0.3 : 0.45,
228
+ result: "base64",
229
+ width: liveMode ? 480 : 540,
230
+ });
212
231
  // upload via request-url then PUT
213
232
  const blob = base64ToUint8Array(uri);
214
233
  let path: string | null = null;