@multiplayer-app/session-recorder-react-native 1.0.1-beta.4 → 1.0.1-beta.6
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/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeConfig.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativeModule.kt +9 -9
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/SessionRecorderNativePackage.kt +2 -2
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/model/TargetInfo.kt +1 -1
- package/android/src/main/java/com/{multiplayer/sessionrecordernative → sessionrecordernative}/util/ViewUtils.kt +2 -2
- package/lib/module/SessionRecorderNativeSpec.js +5 -0
- package/lib/module/SessionRecorderNativeSpec.js.map +1 -0
- package/lib/module/components/SessionRecorderWidget/ErrorBanner.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/ModalHeader.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/SessionRecorderWidget.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/icons.js.map +1 -1
- package/lib/module/components/SessionRecorderWidget/styles.js.map +1 -1
- package/lib/module/config/constants.js.map +1 -1
- package/lib/module/config/defaults.js.map +1 -1
- package/lib/module/config/masking.js.map +1 -1
- package/lib/module/config/session-recorder.js.map +1 -1
- package/lib/module/config/validators.js.map +1 -1
- package/lib/module/config/widget.js.map +1 -1
- package/lib/module/context/SessionRecorderStore.js.map +1 -1
- package/lib/module/context/useSessionRecorderStore.js.map +1 -1
- package/lib/module/context/useStoreSelector.js.map +1 -1
- package/lib/module/native/SessionRecorderNative.js +16 -11
- package/lib/module/native/SessionRecorderNative.js.map +1 -1
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/otel/helpers.js +1 -1
- package/lib/module/otel/helpers.js.map +1 -1
- package/lib/module/otel/index.js.map +1 -1
- package/lib/module/otel/instrumentations/index.js.map +1 -1
- package/lib/module/patch/xhr.js.map +1 -1
- package/lib/module/recorder/eventExporter.js.map +1 -1
- package/lib/module/recorder/gestureRecorder.js.map +1 -1
- package/lib/module/recorder/index.js.map +1 -1
- package/lib/module/recorder/navigationTracker.js.map +1 -1
- package/lib/module/recorder/screenRecorder.js.map +1 -1
- package/lib/module/services/api.service.js.map +1 -1
- package/lib/module/services/network.service.js.map +1 -1
- package/lib/module/services/screenMaskingService.js +1 -1
- package/lib/module/services/screenMaskingService.js.map +1 -1
- package/lib/module/services/storage.service.js.map +1 -1
- package/lib/module/session-recorder.js.map +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/types/session-recorder.js.map +1 -1
- package/lib/module/utils/app-metadata.js +2 -2
- package/lib/module/utils/constants.optional.js.map +1 -1
- package/lib/module/utils/createStore.js.map +1 -1
- package/lib/module/utils/logger.js +0 -8
- package/lib/module/utils/logger.js.map +1 -1
- package/lib/module/utils/platform.js +1 -1
- package/lib/module/utils/platform.js.map +1 -1
- package/lib/module/utils/rrweb-events.js.map +1 -1
- package/lib/module/utils/session.js.map +1 -1
- package/lib/module/utils/shallowEqual.js.map +1 -1
- package/lib/module/utils/time.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts +42 -0
- package/lib/typescript/src/SessionRecorderNativeSpec.d.ts.map +1 -0
- package/lib/typescript/src/components/ScreenRecorderView/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ErrorBanner.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/ModalHeader.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/SessionRecorderWidget.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/icons.d.ts.map +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/index.d.ts +1 -1
- package/lib/typescript/src/components/SessionRecorderWidget/styles.d.ts.map +1 -1
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/config/constants.d.ts.map +1 -1
- package/lib/typescript/src/config/defaults.d.ts.map +1 -1
- package/lib/typescript/src/config/index.d.ts.map +1 -1
- package/lib/typescript/src/config/masking.d.ts.map +1 -1
- package/lib/typescript/src/config/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/config/validators.d.ts.map +1 -1
- package/lib/typescript/src/config/widget.d.ts +1 -1
- package/lib/typescript/src/config/widget.d.ts.map +1 -1
- package/lib/typescript/src/context/SessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts +3 -3
- package/lib/typescript/src/context/useSessionRecorderStore.d.ts.map +1 -1
- package/lib/typescript/src/context/useStoreSelector.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/SessionRecorderNative.d.ts +3 -54
- package/lib/typescript/src/native/SessionRecorderNative.d.ts.map +1 -1
- package/lib/typescript/src/native/index.d.ts +1 -1
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/helpers.d.ts.map +1 -1
- package/lib/typescript/src/otel/index.d.ts.map +1 -1
- package/lib/typescript/src/otel/instrumentations/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/index.d.ts.map +1 -1
- package/lib/typescript/src/patch/xhr.d.ts.map +1 -1
- package/lib/typescript/src/recorder/eventExporter.d.ts.map +1 -1
- package/lib/typescript/src/recorder/gestureRecorder.d.ts.map +1 -1
- package/lib/typescript/src/recorder/index.d.ts.map +1 -1
- package/lib/typescript/src/recorder/navigationTracker.d.ts.map +1 -1
- package/lib/typescript/src/recorder/screenRecorder.d.ts.map +1 -1
- package/lib/typescript/src/services/api.service.d.ts.map +1 -1
- package/lib/typescript/src/services/network.service.d.ts.map +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts +1 -1
- package/lib/typescript/src/services/screenMaskingService.d.ts.map +1 -1
- package/lib/typescript/src/services/storage.service.d.ts.map +1 -1
- package/lib/typescript/src/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/configs.d.ts.map +1 -1
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/lib/typescript/src/types/session-recorder.d.ts +4 -4
- package/lib/typescript/src/types/session-recorder.d.ts.map +1 -1
- package/lib/typescript/src/types/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/app-metadata.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.d.ts.map +1 -1
- package/lib/typescript/src/utils/constants.optional.expo.d.ts.map +1 -1
- package/lib/typescript/src/utils/createStore.d.ts.map +1 -1
- package/lib/typescript/src/utils/index.d.ts.map +1 -1
- package/lib/typescript/src/utils/logger.d.ts +1 -1
- package/lib/typescript/src/utils/logger.d.ts.map +1 -1
- package/lib/typescript/src/utils/platform.d.ts.map +1 -1
- package/lib/typescript/src/utils/request-utils.d.ts.map +1 -1
- package/lib/typescript/src/utils/rrweb-events.d.ts.map +1 -1
- package/lib/typescript/src/utils/session.d.ts.map +1 -1
- package/lib/typescript/src/utils/shallowEqual.d.ts.map +1 -1
- package/lib/typescript/src/utils/time.d.ts.map +1 -1
- package/lib/typescript/src/utils/type-utils.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/SessionRecorderNativeSpec.ts +53 -0
- package/src/components/ScreenRecorderView/index.ts +1 -1
- package/src/components/SessionRecorderWidget/ErrorBanner.tsx +14 -14
- package/src/components/SessionRecorderWidget/ModalHeader.tsx +11 -9
- package/src/components/SessionRecorderWidget/SessionRecorderWidget.tsx +70 -56
- package/src/components/SessionRecorderWidget/icons.tsx +58 -30
- package/src/components/SessionRecorderWidget/index.ts +1 -1
- package/src/components/SessionRecorderWidget/styles.ts +17 -18
- package/src/components/index.ts +2 -2
- package/src/config/constants.ts +19 -20
- package/src/config/defaults.ts +35 -31
- package/src/config/index.ts +5 -5
- package/src/config/masking.ts +44 -18
- package/src/config/session-recorder.ts +54 -26
- package/src/config/validators.ts +43 -20
- package/src/config/widget.ts +24 -15
- package/src/context/SessionRecorderStore.ts +19 -18
- package/src/context/useSessionRecorderStore.ts +17 -10
- package/src/context/useStoreSelector.ts +20 -18
- package/src/index.ts +7 -7
- package/src/native/SessionRecorderNative.ts +68 -112
- package/src/native/index.ts +5 -1
- package/src/otel/helpers.ts +109 -93
- package/src/otel/index.ts +46 -49
- package/src/otel/instrumentations/index.ts +44 -41
- package/src/patch/index.ts +1 -1
- package/src/patch/xhr.ts +77 -78
- package/src/recorder/eventExporter.ts +63 -68
- package/src/recorder/gestureRecorder.ts +359 -212
- package/src/recorder/index.ts +75 -62
- package/src/recorder/navigationTracker.ts +120 -97
- package/src/recorder/screenRecorder.ts +214 -163
- package/src/services/api.service.ts +49 -48
- package/src/services/network.service.ts +67 -58
- package/src/services/screenMaskingService.ts +81 -50
- package/src/services/storage.service.ts +99 -70
- package/src/session-recorder.ts +270 -214
- package/src/types/configs.ts +53 -31
- package/src/types/expo-constants.d.ts +2 -2
- package/src/types/index.ts +16 -18
- package/src/types/session-recorder.ts +106 -111
- package/src/types/session.ts +45 -45
- package/src/utils/app-metadata.ts +9 -9
- package/src/utils/constants.optional.expo.ts +3 -3
- package/src/utils/constants.optional.ts +14 -12
- package/src/utils/createStore.ts +23 -20
- package/src/utils/index.ts +7 -7
- package/src/utils/logger.ts +87 -58
- package/src/utils/platform.ts +149 -118
- package/src/utils/request-utils.ts +15 -15
- package/src/utils/rrweb-events.ts +47 -34
- package/src/utils/session.ts +15 -12
- package/src/utils/shallowEqual.ts +16 -10
- package/src/utils/time.ts +7 -4
- package/src/utils/type-utils.ts +36 -36
- package/src/version.ts +1 -1
- package/android/src/main/java/com/multiplayer/sessionrecordernative/SessionRecorderNativeModuleSpec.kt +0 -51
- package/android/src/main/java/com/xxx/XxxModule.kt +0 -23
- package/ios/Xxx.h +0 -5
- package/ios/Xxx.mm +0 -21
package/src/config/validators.ts
CHANGED
|
@@ -2,30 +2,53 @@
|
|
|
2
2
|
* Validation helper functions for configuration objects
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
export const isValidStringOrRegExp = (
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
export const isValidStringOrRegExp = (
|
|
6
|
+
value: string | RegExp | undefined,
|
|
7
|
+
defaultValue: string | RegExp
|
|
8
|
+
) => {
|
|
9
|
+
return typeof value === 'string' || value instanceof RegExp
|
|
10
|
+
? value
|
|
11
|
+
: defaultValue;
|
|
12
|
+
};
|
|
8
13
|
|
|
9
|
-
export const isValidString = <T extends string>(
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
export const isValidString = <T extends string>(
|
|
15
|
+
value: string | undefined | T,
|
|
16
|
+
defaultValue: string
|
|
17
|
+
) => {
|
|
18
|
+
return typeof value === 'string' ? value.trim() : defaultValue;
|
|
19
|
+
};
|
|
12
20
|
|
|
13
|
-
export const isValidNumber = (
|
|
14
|
-
|
|
15
|
-
|
|
21
|
+
export const isValidNumber = (
|
|
22
|
+
value: number | undefined,
|
|
23
|
+
defaultValue: number
|
|
24
|
+
) => {
|
|
25
|
+
return typeof value === 'number' ? value : defaultValue;
|
|
26
|
+
};
|
|
16
27
|
|
|
17
|
-
export const isValidBoolean = (
|
|
18
|
-
|
|
19
|
-
|
|
28
|
+
export const isValidBoolean = (
|
|
29
|
+
value: boolean | undefined,
|
|
30
|
+
defaultValue: boolean
|
|
31
|
+
) => {
|
|
32
|
+
return typeof value === 'boolean' ? value : defaultValue;
|
|
33
|
+
};
|
|
20
34
|
|
|
21
|
-
export const isValidArray = <T>(
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
export const isValidArray = <T>(
|
|
36
|
+
value: ReadonlyArray<T> | undefined,
|
|
37
|
+
defaultValue: ReadonlyArray<T>
|
|
38
|
+
): T[] => {
|
|
39
|
+
return Array.isArray(value)
|
|
40
|
+
? ([...value] as T[])
|
|
41
|
+
: ([...defaultValue] as T[]);
|
|
42
|
+
};
|
|
24
43
|
|
|
25
|
-
export const isValidEnum = <T>(
|
|
26
|
-
|
|
27
|
-
|
|
44
|
+
export const isValidEnum = <T>(
|
|
45
|
+
value: any | T,
|
|
46
|
+
defaultValue: T,
|
|
47
|
+
enumValues: T[]
|
|
48
|
+
): T => {
|
|
49
|
+
return enumValues.includes(value as T) ? (value as T) : defaultValue;
|
|
50
|
+
};
|
|
28
51
|
|
|
29
52
|
export const isValidFunction = (value: any, defaultValue: any) => {
|
|
30
|
-
return typeof value === 'function' ? value : defaultValue
|
|
31
|
-
}
|
|
53
|
+
return typeof value === 'function' ? value : defaultValue;
|
|
54
|
+
};
|
package/src/config/widget.ts
CHANGED
|
@@ -1,38 +1,47 @@
|
|
|
1
|
-
import { type SessionRecorderOptions, WidgetButtonPlacement } from
|
|
2
|
-
import { BASE_CONFIG } from
|
|
3
|
-
import { isValidBoolean, isValidEnum, isValidString } from
|
|
1
|
+
import { type SessionRecorderOptions, WidgetButtonPlacement } from '../types';
|
|
2
|
+
import { BASE_CONFIG } from './defaults';
|
|
3
|
+
import { isValidBoolean, isValidEnum, isValidString } from './validators';
|
|
4
4
|
|
|
5
5
|
export const getWidgetConfig = (config: SessionRecorderOptions['widget']) => {
|
|
6
|
-
const textOverrides = getTextOverridesConfig(
|
|
6
|
+
const textOverrides = getTextOverridesConfig(
|
|
7
|
+
config?.textOverrides,
|
|
8
|
+
BASE_CONFIG.widget.textOverrides
|
|
9
|
+
);
|
|
7
10
|
|
|
8
11
|
const def = {
|
|
9
12
|
enabled: true,
|
|
10
13
|
button: { visible: true, placement: WidgetButtonPlacement.bottomRight },
|
|
11
14
|
textOverrides,
|
|
12
|
-
}
|
|
15
|
+
};
|
|
13
16
|
|
|
14
|
-
const placementCandidate = config?.button?.placement || def.button.placement
|
|
17
|
+
const placementCandidate = config?.button?.placement || def.button.placement;
|
|
15
18
|
|
|
16
19
|
return {
|
|
17
20
|
textOverrides,
|
|
18
21
|
enabled: isValidBoolean(config && config.enabled, def.enabled),
|
|
19
22
|
button: {
|
|
20
|
-
visible: isValidBoolean(
|
|
23
|
+
visible: isValidBoolean(
|
|
24
|
+
config && config.button && config.button.visible,
|
|
25
|
+
def.button.visible
|
|
26
|
+
),
|
|
21
27
|
placement: isValidEnum<WidgetButtonPlacement>(
|
|
22
28
|
placementCandidate,
|
|
23
29
|
def.button.placement,
|
|
24
30
|
Object.values(WidgetButtonPlacement)
|
|
25
31
|
),
|
|
26
32
|
},
|
|
27
|
-
}
|
|
28
|
-
}
|
|
33
|
+
};
|
|
34
|
+
};
|
|
29
35
|
|
|
30
36
|
const getTextOverridesConfig = (config: any, defaultConfig: any) => {
|
|
31
37
|
if (!config || typeof config !== 'object') {
|
|
32
|
-
return defaultConfig
|
|
38
|
+
return defaultConfig;
|
|
33
39
|
}
|
|
34
|
-
return Object.keys(defaultConfig).reduce(
|
|
35
|
-
acc
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
40
|
+
return Object.keys(defaultConfig).reduce(
|
|
41
|
+
(acc, key) => {
|
|
42
|
+
acc[key] = isValidString(config[key], defaultConfig[key]);
|
|
43
|
+
return acc;
|
|
44
|
+
},
|
|
45
|
+
{} as Record<string, unknown>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { createStore, type Store } from '../utils/createStore'
|
|
2
|
-
import { SessionType } from '@multiplayer-app/session-recorder-common'
|
|
3
|
-
import { SessionState } from '../types'
|
|
1
|
+
import { createStore, type Store } from '../utils/createStore';
|
|
2
|
+
import { SessionType } from '@multiplayer-app/session-recorder-common';
|
|
3
|
+
import { SessionState } from '../types';
|
|
4
4
|
|
|
5
5
|
export type SessionRecorderState = {
|
|
6
|
-
isInitialized: boolean
|
|
7
|
-
sessionType: SessionType | null
|
|
8
|
-
sessionState: SessionState | null
|
|
9
|
-
isWidgetModalVisible: boolean
|
|
10
|
-
isOnline: boolean
|
|
11
|
-
error: string | null
|
|
12
|
-
}
|
|
6
|
+
isInitialized: boolean;
|
|
7
|
+
sessionType: SessionType | null;
|
|
8
|
+
sessionState: SessionState | null;
|
|
9
|
+
isWidgetModalVisible: boolean;
|
|
10
|
+
isOnline: boolean;
|
|
11
|
+
error: string | null;
|
|
12
|
+
};
|
|
13
13
|
|
|
14
|
-
export const sessionRecorderStore: Store<SessionRecorderState> =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
export const sessionRecorderStore: Store<SessionRecorderState> =
|
|
15
|
+
createStore<SessionRecorderState>({
|
|
16
|
+
isInitialized: false,
|
|
17
|
+
sessionType: null,
|
|
18
|
+
sessionState: null,
|
|
19
|
+
isWidgetModalVisible: false,
|
|
20
|
+
isOnline: true,
|
|
21
|
+
error: null,
|
|
22
|
+
});
|
|
@@ -1,27 +1,34 @@
|
|
|
1
|
-
import { SessionType } from
|
|
2
|
-
import { SessionState } from
|
|
3
|
-
import { useStoreSelector } from
|
|
4
|
-
import {
|
|
1
|
+
import { SessionType } from '@multiplayer-app/session-recorder-common';
|
|
2
|
+
import { SessionState } from '../types';
|
|
3
|
+
import { useStoreSelector } from './useStoreSelector';
|
|
4
|
+
import {
|
|
5
|
+
type SessionRecorderState,
|
|
6
|
+
sessionRecorderStore,
|
|
7
|
+
} from './SessionRecorderStore';
|
|
5
8
|
|
|
6
9
|
export function useSessionRecorderStore<TSlice>(
|
|
7
10
|
selector: (s: SessionRecorderState) => TSlice,
|
|
8
11
|
equalityFn?: (a: TSlice, b: TSlice) => boolean
|
|
9
12
|
): TSlice {
|
|
10
|
-
return useStoreSelector<SessionRecorderState, TSlice>(
|
|
13
|
+
return useStoreSelector<SessionRecorderState, TSlice>(
|
|
14
|
+
sessionRecorderStore,
|
|
15
|
+
selector,
|
|
16
|
+
equalityFn
|
|
17
|
+
);
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
export function useSessionRecordingState() {
|
|
14
|
-
return useSessionRecorderStore<SessionState | null>((s) => s.sessionState)
|
|
21
|
+
return useSessionRecorderStore<SessionState | null>((s) => s.sessionState);
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
export function useSessionType() {
|
|
18
|
-
return useSessionRecorderStore<SessionType | null>((s) => s.sessionType)
|
|
25
|
+
return useSessionRecorderStore<SessionType | null>((s) => s.sessionType);
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
export function useIsInitialized() {
|
|
22
|
-
return useSessionRecorderStore<boolean>((s) => s.isInitialized)
|
|
29
|
+
return useSessionRecorderStore<boolean>((s) => s.isInitialized);
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
export function useWidgetModalVisible() {
|
|
26
|
-
return useSessionRecorderStore<boolean>((s) => s.isWidgetModalVisible)
|
|
27
|
-
}
|
|
33
|
+
return useSessionRecorderStore<boolean>((s) => s.isWidgetModalVisible);
|
|
34
|
+
}
|
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react'
|
|
2
|
-
import { type Store } from '../utils/createStore'
|
|
3
|
-
import { shallowEqual } from '../utils/shallowEqual'
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { type Store } from '../utils/createStore';
|
|
3
|
+
import { shallowEqual } from '../utils/shallowEqual';
|
|
4
4
|
|
|
5
5
|
export function useStoreSelector<TState extends object, TSlice>(
|
|
6
6
|
store: Store<TState>,
|
|
7
7
|
selector: (state: TState) => TSlice,
|
|
8
|
-
equalityFn: (a: TSlice, b: TSlice) => boolean = Object.is
|
|
8
|
+
equalityFn: (a: TSlice, b: TSlice) => boolean = Object.is
|
|
9
9
|
): TSlice {
|
|
10
|
-
const latestSelectorRef = useRef(selector)
|
|
11
|
-
const latestEqualityRef = useRef(equalityFn)
|
|
12
|
-
latestSelectorRef.current = selector
|
|
13
|
-
latestEqualityRef.current = equalityFn
|
|
10
|
+
const latestSelectorRef = useRef(selector);
|
|
11
|
+
const latestEqualityRef = useRef(equalityFn);
|
|
12
|
+
latestSelectorRef.current = selector;
|
|
13
|
+
latestEqualityRef.current = equalityFn;
|
|
14
14
|
|
|
15
|
-
const [slice, setSlice] = useState<TSlice>(() =>
|
|
15
|
+
const [slice, setSlice] = useState<TSlice>(() =>
|
|
16
|
+
latestSelectorRef.current(store.getState())
|
|
17
|
+
);
|
|
16
18
|
|
|
17
19
|
useEffect(() => {
|
|
18
20
|
function handleChange(nextState: TState, prevState: TState) {
|
|
19
|
-
const nextSlice = latestSelectorRef.current(nextState)
|
|
20
|
-
const prevSlice = latestSelectorRef.current(prevState)
|
|
21
|
+
const nextSlice = latestSelectorRef.current(nextState);
|
|
22
|
+
const prevSlice = latestSelectorRef.current(prevState);
|
|
21
23
|
if (!latestEqualityRef.current(nextSlice, prevSlice)) {
|
|
22
|
-
setSlice(nextSlice)
|
|
24
|
+
setSlice(nextSlice);
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
|
-
const unsubscribe = store.subscribe(handleChange)
|
|
27
|
+
const unsubscribe = store.subscribe(handleChange);
|
|
26
28
|
// Sync once in case changed between render and effect
|
|
27
|
-
handleChange(store.getState(), store.getState())
|
|
28
|
-
return unsubscribe
|
|
29
|
-
}, [store])
|
|
29
|
+
handleChange(store.getState(), store.getState());
|
|
30
|
+
return unsubscribe;
|
|
31
|
+
}, [store]);
|
|
30
32
|
|
|
31
|
-
return slice
|
|
33
|
+
return slice;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
export const shallow = shallowEqual
|
|
36
|
+
export const shallow = shallowEqual;
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import './patch'
|
|
2
|
-
import SessionRecorder from './session-recorder'
|
|
3
|
-
export * from '@multiplayer-app/session-recorder-common'
|
|
4
|
-
export * from './context/SessionRecorderContext'
|
|
5
|
-
export * from './context/useSessionRecorderStore'
|
|
1
|
+
import './patch';
|
|
2
|
+
import SessionRecorder from './session-recorder';
|
|
3
|
+
export * from '@multiplayer-app/session-recorder-common';
|
|
4
|
+
export * from './context/SessionRecorderContext';
|
|
5
|
+
export * from './context/useSessionRecorderStore';
|
|
6
6
|
|
|
7
7
|
// Export the class for type checking
|
|
8
|
-
export { SessionRecorder }
|
|
8
|
+
export { SessionRecorder };
|
|
9
9
|
// Export the instance as default
|
|
10
|
-
export default SessionRecorder
|
|
10
|
+
export default SessionRecorder;
|
|
@@ -1,164 +1,120 @@
|
|
|
1
|
+
import { Platform, NativeEventEmitter } from 'react-native';
|
|
2
|
+
import SessionRecorderNative, { type MaskingOptions, type Spec } from '../SessionRecorderNativeSpec';
|
|
1
3
|
|
|
2
|
-
import { NativeEventEmitter, Platform, TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
3
|
-
|
|
4
|
-
export interface Spec extends TurboModule {
|
|
5
|
-
/**
|
|
6
|
-
* Capture the current screen and apply masking to sensitive elements
|
|
7
|
-
* @returns Promise that resolves to base64 encoded image
|
|
8
|
-
*/
|
|
9
|
-
captureAndMask(): Promise<string>
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Capture the current screen and apply masking with custom options
|
|
13
|
-
* @param options Custom masking options
|
|
14
|
-
* @returns Promise that resolves to base64 encoded image
|
|
15
|
-
*/
|
|
16
|
-
captureAndMaskWithOptions(options: MaskingOptions): Promise<string>
|
|
17
|
-
|
|
18
|
-
// Gesture recording APIs
|
|
19
|
-
startGestureRecording(): Promise<void>
|
|
20
|
-
stopGestureRecording(): Promise<void>
|
|
21
|
-
isGestureRecordingActive(): Promise<boolean>
|
|
22
|
-
setGestureCallback(callback: (event: any) => void): void
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface MaskingOptions {
|
|
26
|
-
/** Quality of the captured image (0.1 to 1.0, default: 0.3 for smaller file size) */
|
|
27
|
-
quality?: number
|
|
28
|
-
/** Scale of the captured image (0.1 to 1.0, default: 1.0) */
|
|
29
|
-
scale?: number
|
|
30
|
-
/** Whether to mask text inputs (UITextField, UITextView, React Native text components) */
|
|
31
|
-
maskTextInputs?: boolean
|
|
32
|
-
/** Whether to mask images (UIImageView, React Native Image components) */
|
|
33
|
-
maskImages?: boolean
|
|
34
|
-
/** Whether to mask buttons (UIButton) */
|
|
35
|
-
maskButtons?: boolean
|
|
36
|
-
/** Whether to mask labels (UILabel) */
|
|
37
|
-
maskLabels?: boolean
|
|
38
|
-
/** Whether to mask web views (WKWebView) */
|
|
39
|
-
maskWebViews?: boolean
|
|
40
|
-
/** Whether to mask sandboxed views (system views that don't belong to current process) */
|
|
41
|
-
maskSandboxedViews?: boolean
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
export interface SessionRecorderNativeModule {
|
|
46
|
-
/**
|
|
47
|
-
* Capture the current screen and apply masking to sensitive elements
|
|
48
|
-
* @returns Promise that resolves to base64 encoded image
|
|
49
|
-
*/
|
|
50
|
-
captureAndMask(): Promise<string>
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Capture the current screen and apply masking with custom options
|
|
54
|
-
* @param options Custom masking options
|
|
55
|
-
* @returns Promise that resolves to base64 encoded image
|
|
56
|
-
*/
|
|
57
|
-
captureAndMaskWithOptions(options: MaskingOptions): Promise<string>
|
|
58
|
-
|
|
59
|
-
// Gesture recording APIs
|
|
60
|
-
startGestureRecording(): Promise<void>
|
|
61
|
-
stopGestureRecording(): Promise<void>
|
|
62
|
-
isGestureRecordingActive(): Promise<boolean>
|
|
63
|
-
setGestureCallback(callback: (event: any) => void): void
|
|
64
|
-
}
|
|
65
4
|
|
|
66
5
|
// Check if we're on web platform
|
|
67
|
-
const isWeb = Platform.OS === 'web'
|
|
6
|
+
const isWeb = Platform.OS === 'web';
|
|
68
7
|
|
|
69
8
|
// Get the Turbo Module
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (!isWeb) {
|
|
74
|
-
try {
|
|
75
|
-
SessionRecorderNative = TurboModuleRegistry.getEnforcing<Spec>('SessionRecorderNative')
|
|
76
|
-
eventEmitter = new NativeEventEmitter(SessionRecorderNative as any)
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.warn('Failed to access SessionRecorderNative Turbo Module:', error)
|
|
79
|
-
}
|
|
80
|
-
}
|
|
9
|
+
let eventEmitter = new NativeEventEmitter(SessionRecorderNative as any);
|
|
10
|
+
|
|
81
11
|
|
|
82
12
|
// Validate that the native module is available
|
|
83
13
|
if (!SessionRecorderNative && !isWeb) {
|
|
84
|
-
console.warn(
|
|
14
|
+
console.warn(
|
|
15
|
+
'SessionRecorderNative Turbo Module is not available. Auto-linking may not have completed yet.'
|
|
16
|
+
);
|
|
85
17
|
} else if (isWeb) {
|
|
86
|
-
console.info(
|
|
18
|
+
console.info(
|
|
19
|
+
'SessionRecorderNative: Running on web platform, native module disabled'
|
|
20
|
+
);
|
|
87
21
|
}
|
|
88
22
|
|
|
89
23
|
// Create a safe wrapper that handles web platform
|
|
90
24
|
const SafeSessionRecorderNative: Spec = {
|
|
91
25
|
async captureAndMask(): Promise<string> {
|
|
92
26
|
if (isWeb || !SessionRecorderNative) {
|
|
93
|
-
throw new Error('SessionRecorderNative is not available on web platform')
|
|
27
|
+
throw new Error('SessionRecorderNative is not available on web platform');
|
|
94
28
|
}
|
|
95
|
-
return SessionRecorderNative.captureAndMask()
|
|
29
|
+
return SessionRecorderNative.captureAndMask();
|
|
96
30
|
},
|
|
97
31
|
|
|
98
32
|
async captureAndMaskWithOptions(options: MaskingOptions): Promise<string> {
|
|
99
33
|
if (isWeb || !SessionRecorderNative) {
|
|
100
|
-
throw new Error('SessionRecorderNative is not available on web platform')
|
|
34
|
+
throw new Error('SessionRecorderNative is not available on web platform');
|
|
101
35
|
}
|
|
102
|
-
return SessionRecorderNative.captureAndMaskWithOptions(options)
|
|
36
|
+
return SessionRecorderNative.captureAndMaskWithOptions(options);
|
|
103
37
|
},
|
|
104
38
|
|
|
105
39
|
async startGestureRecording(): Promise<void> {
|
|
106
40
|
if (isWeb || !SessionRecorderNative) {
|
|
107
|
-
throw new Error('SessionRecorderNative is not available on web platform')
|
|
41
|
+
throw new Error('SessionRecorderNative is not available on web platform');
|
|
108
42
|
}
|
|
109
|
-
return SessionRecorderNative.startGestureRecording()
|
|
43
|
+
return SessionRecorderNative.startGestureRecording();
|
|
110
44
|
},
|
|
111
45
|
|
|
112
46
|
async stopGestureRecording(): Promise<void> {
|
|
113
47
|
if (isWeb || !SessionRecorderNative) {
|
|
114
|
-
throw new Error('SessionRecorderNative is not available on web platform')
|
|
48
|
+
throw new Error('SessionRecorderNative is not available on web platform');
|
|
115
49
|
}
|
|
116
|
-
return SessionRecorderNative.stopGestureRecording()
|
|
50
|
+
return SessionRecorderNative.stopGestureRecording();
|
|
117
51
|
},
|
|
118
52
|
|
|
119
53
|
async isGestureRecordingActive(): Promise<boolean> {
|
|
120
54
|
if (isWeb || !SessionRecorderNative) {
|
|
121
|
-
throw new Error('SessionRecorderNative is not available on web platform')
|
|
55
|
+
throw new Error('SessionRecorderNative is not available on web platform');
|
|
122
56
|
}
|
|
123
|
-
return SessionRecorderNative.isGestureRecordingActive()
|
|
57
|
+
return SessionRecorderNative.isGestureRecordingActive();
|
|
124
58
|
},
|
|
125
59
|
|
|
126
60
|
setGestureCallback(callback: (event: any) => void): void {
|
|
127
61
|
if (isWeb || !SessionRecorderNative) {
|
|
128
|
-
throw new Error('SessionRecorderNative is not available on web platform')
|
|
62
|
+
throw new Error('SessionRecorderNative is not available on web platform');
|
|
129
63
|
}
|
|
130
64
|
// Native side will also invoke callback if provided; also subscribe to events here
|
|
131
65
|
try {
|
|
132
|
-
SessionRecorderNative.setGestureCallback(callback as any)
|
|
66
|
+
SessionRecorderNative.setGestureCallback(callback as any);
|
|
133
67
|
} catch { }
|
|
134
|
-
eventEmitter?.removeAllListeners('onGestureDetected')
|
|
135
|
-
eventEmitter?.addListener('onGestureDetected', callback)
|
|
136
|
-
}
|
|
137
|
-
|
|
68
|
+
eventEmitter?.removeAllListeners('onGestureDetected');
|
|
69
|
+
eventEmitter?.addListener('onGestureDetected', callback);
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
recordGesture(gestureType: string, x: number, y: number, target?: string, metadata?: any): void {
|
|
73
|
+
if (isWeb || !SessionRecorderNative) {
|
|
74
|
+
throw new Error('SessionRecorderNative is not available on web platform');
|
|
75
|
+
}
|
|
76
|
+
SessionRecorderNative.recordGesture(gestureType, x, y, target, metadata);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
addListener(_eventName: string): void {
|
|
80
|
+
// Required for RN event emitter contracts
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
removeListeners(_count: number): void {
|
|
84
|
+
// Required for RN event emitter contracts
|
|
85
|
+
},
|
|
86
|
+
};
|
|
138
87
|
|
|
139
88
|
export interface NativeGestureEvent {
|
|
140
|
-
type:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
89
|
+
type:
|
|
90
|
+
| 'tap'
|
|
91
|
+
| 'pan_start'
|
|
92
|
+
| 'pan_move'
|
|
93
|
+
| 'pan_end'
|
|
94
|
+
| 'long_press'
|
|
95
|
+
| 'pinch'
|
|
96
|
+
| 'swipe';
|
|
97
|
+
timestamp: number;
|
|
98
|
+
x: number;
|
|
99
|
+
y: number;
|
|
100
|
+
target?: string;
|
|
145
101
|
targetInfo?: {
|
|
146
|
-
identifier: string
|
|
147
|
-
label?: string
|
|
148
|
-
role?: string
|
|
149
|
-
testId?: string
|
|
150
|
-
text?: string
|
|
151
|
-
}
|
|
102
|
+
identifier: string;
|
|
103
|
+
label?: string;
|
|
104
|
+
role?: string;
|
|
105
|
+
testId?: string;
|
|
106
|
+
text?: string;
|
|
107
|
+
};
|
|
152
108
|
metadata?: {
|
|
153
|
-
pressure?: number
|
|
154
|
-
velocity?: number
|
|
155
|
-
scale?: number
|
|
156
|
-
direction?: string
|
|
157
|
-
distance?: number
|
|
158
|
-
}
|
|
109
|
+
pressure?: number;
|
|
110
|
+
velocity?: number;
|
|
111
|
+
scale?: number;
|
|
112
|
+
direction?: string;
|
|
113
|
+
distance?: number;
|
|
114
|
+
};
|
|
159
115
|
}
|
|
160
116
|
|
|
161
|
-
export default SafeSessionRecorderNative
|
|
117
|
+
export default SafeSessionRecorderNative;
|
|
162
118
|
|
|
163
119
|
// Export event emitter for gesture events to maintain previous API
|
|
164
|
-
export const gestureEventEmitter = eventEmitter
|
|
120
|
+
export const gestureEventEmitter = eventEmitter;
|
package/src/native/index.ts
CHANGED