flowboard-react 0.6.1 → 0.6.3
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 +168 -52
- package/app.plugin.js +341 -0
- package/bin/setup.js +427 -0
- package/lib/module/Flowboard.js +1 -2
- package/lib/module/Flowboard.js.map +1 -1
- package/lib/module/FlowboardProvider.js +1 -1
- package/lib/module/FlowboardProvider.js.map +1 -1
- package/lib/module/components/FlowboardFlow.js +31 -9
- package/lib/module/components/FlowboardFlow.js.map +1 -1
- package/lib/module/components/FlowboardRenderer.js +419 -85
- package/lib/module/components/FlowboardRenderer.js.map +1 -1
- package/lib/module/core/clientContext.js +10 -29
- package/lib/module/core/clientContext.js.map +1 -1
- package/lib/module/core/fontAwesome.js +2 -9
- package/lib/module/core/fontAwesome.js.map +1 -1
- package/lib/module/core/onboardingRepository.js +13 -13
- package/lib/module/core/onboardingRepository.js.map +1 -1
- package/lib/module/native/asyncStorage.js +55 -0
- package/lib/module/native/asyncStorage.js.map +1 -0
- package/lib/module/native/deviceInfo.js +46 -0
- package/lib/module/native/deviceInfo.js.map +1 -0
- package/lib/module/native/inAppReview.js +17 -0
- package/lib/module/native/inAppReview.js.map +1 -0
- package/lib/module/native/linearGradient.js +29 -0
- package/lib/module/native/linearGradient.js.map +1 -0
- package/lib/module/native/lottie.js +20 -0
- package/lib/module/native/lottie.js.map +1 -0
- package/lib/module/native/maskedView.js +26 -0
- package/lib/module/native/maskedView.js.map +1 -0
- package/lib/module/native/pagerView.js +79 -0
- package/lib/module/native/pagerView.js.map +1 -0
- package/lib/module/native/permissions.js +30 -0
- package/lib/module/native/permissions.js.map +1 -0
- package/lib/module/native/runtime.js +81 -0
- package/lib/module/native/runtime.js.map +1 -0
- package/lib/module/native/safeAreaContext.js +43 -0
- package/lib/module/native/safeAreaContext.js.map +1 -0
- package/lib/module/native/svg.js +83 -0
- package/lib/module/native/svg.js.map +1 -0
- package/lib/module/native/vectorIcons.js +41 -0
- package/lib/module/native/vectorIcons.js.map +1 -0
- package/lib/module/utils/flowboardUtils.js +4 -1
- package/lib/module/utils/flowboardUtils.js.map +1 -1
- package/lib/typescript/src/Flowboard.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardFlow.d.ts.map +1 -1
- package/lib/typescript/src/components/FlowboardRenderer.d.ts +33 -0
- package/lib/typescript/src/components/FlowboardRenderer.d.ts.map +1 -1
- package/lib/typescript/src/core/clientContext.d.ts.map +1 -1
- package/lib/typescript/src/core/fontAwesome.d.ts +1 -1
- package/lib/typescript/src/core/fontAwesome.d.ts.map +1 -1
- package/lib/typescript/src/core/onboardingRepository.d.ts.map +1 -1
- package/lib/typescript/src/native/asyncStorage.d.ts +16 -0
- package/lib/typescript/src/native/asyncStorage.d.ts.map +1 -0
- package/lib/typescript/src/native/deviceInfo.d.ts +11 -0
- package/lib/typescript/src/native/deviceInfo.d.ts.map +1 -0
- package/lib/typescript/src/native/inAppReview.d.ts +4 -0
- package/lib/typescript/src/native/inAppReview.d.ts.map +1 -0
- package/lib/typescript/src/native/linearGradient.d.ts +3 -0
- package/lib/typescript/src/native/linearGradient.d.ts.map +1 -0
- package/lib/typescript/src/native/lottie.d.ts +3 -0
- package/lib/typescript/src/native/lottie.d.ts.map +1 -0
- package/lib/typescript/src/native/maskedView.d.ts +3 -0
- package/lib/typescript/src/native/maskedView.d.ts.map +1 -0
- package/lib/typescript/src/native/pagerView.d.ts +14 -0
- package/lib/typescript/src/native/pagerView.d.ts.map +1 -0
- package/lib/typescript/src/native/permissions.d.ts +9 -0
- package/lib/typescript/src/native/permissions.d.ts.map +1 -0
- package/lib/typescript/src/native/runtime.d.ts +13 -0
- package/lib/typescript/src/native/runtime.d.ts.map +1 -0
- package/lib/typescript/src/native/safeAreaContext.d.ts +15 -0
- package/lib/typescript/src/native/safeAreaContext.d.ts.map +1 -0
- package/lib/typescript/src/native/svg.d.ts +8 -0
- package/lib/typescript/src/native/svg.d.ts.map +1 -0
- package/lib/typescript/src/native/vectorIcons.d.ts +4 -0
- package/lib/typescript/src/native/vectorIcons.d.ts.map +1 -0
- package/lib/typescript/src/utils/flowboardUtils.d.ts.map +1 -1
- package/package.json +24 -19
- package/src/Flowboard.ts +1 -2
- package/src/FlowboardProvider.tsx +1 -1
- package/src/components/FlowboardFlow.tsx +47 -9
- package/src/components/FlowboardRenderer.tsx +689 -113
- package/src/core/clientContext.ts +10 -32
- package/src/core/fontAwesome.ts +2 -9
- package/src/core/onboardingRepository.ts +18 -13
- package/src/native/asyncStorage.ts +99 -0
- package/src/native/deviceInfo.ts +88 -0
- package/src/native/inAppReview.ts +34 -0
- package/src/native/linearGradient.tsx +24 -0
- package/src/native/lottie.tsx +17 -0
- package/src/native/maskedView.tsx +19 -0
- package/src/native/pagerView.tsx +95 -0
- package/src/native/permissions.ts +59 -0
- package/src/native/runtime.ts +110 -0
- package/src/native/safeAreaContext.tsx +44 -0
- package/src/native/svg.tsx +82 -0
- package/src/native/vectorIcons.tsx +50 -0
- package/src/utils/flowboardUtils.ts +9 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
|
-
import DeviceInfo from 'react-native-device-info';
|
|
3
1
|
import { Platform } from 'react-native';
|
|
4
2
|
import { v4 as uuidv4 } from 'uuid';
|
|
5
3
|
import 'react-native-get-random-values';
|
|
4
|
+
import { FlowboardAsyncStorage } from '../native/asyncStorage';
|
|
5
|
+
import { FlowboardDeviceInfo } from '../native/deviceInfo';
|
|
6
6
|
|
|
7
7
|
export type ClientContextData = {
|
|
8
8
|
appVersion: string;
|
|
@@ -44,16 +44,16 @@ export class ClientContext {
|
|
|
44
44
|
static async create(): Promise<ClientContext> {
|
|
45
45
|
const installId = await getOrCreateInstallId();
|
|
46
46
|
|
|
47
|
-
const appVersion =
|
|
48
|
-
const buildNumber =
|
|
49
|
-
const bundleId =
|
|
47
|
+
const appVersion = FlowboardDeviceInfo.getVersion();
|
|
48
|
+
const buildNumber = FlowboardDeviceInfo.getBuildNumber();
|
|
49
|
+
const bundleId = FlowboardDeviceInfo.getBundleId();
|
|
50
50
|
|
|
51
|
-
const locale = await getDeviceLocale();
|
|
51
|
+
const locale = await FlowboardDeviceInfo.getDeviceLocale();
|
|
52
52
|
const country = locale.includes('_') ? locale.split('_').pop() ?? '' : '';
|
|
53
53
|
|
|
54
54
|
const os = Platform.OS;
|
|
55
|
-
const osVersion =
|
|
56
|
-
const deviceType = mapDeviceType(await
|
|
55
|
+
const osVersion = FlowboardDeviceInfo.getSystemVersion();
|
|
56
|
+
const deviceType = mapDeviceType(await FlowboardDeviceInfo.getDeviceType());
|
|
57
57
|
|
|
58
58
|
return new ClientContext({
|
|
59
59
|
appVersion,
|
|
@@ -87,35 +87,13 @@ export class ClientContext {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
async function getOrCreateInstallId(): Promise<string> {
|
|
90
|
-
const stored = await
|
|
90
|
+
const stored = await FlowboardAsyncStorage.getItem(INSTALL_ID_KEY);
|
|
91
91
|
if (stored) return stored;
|
|
92
92
|
const installId = uuidv4();
|
|
93
|
-
await
|
|
93
|
+
await FlowboardAsyncStorage.setItem(INSTALL_ID_KEY, installId);
|
|
94
94
|
return installId;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
async function getDeviceLocale(): Promise<string> {
|
|
98
|
-
const deviceInfoAny = DeviceInfo as any;
|
|
99
|
-
if (typeof deviceInfoAny.getDeviceLocale === 'function') {
|
|
100
|
-
const locale = await deviceInfoAny.getDeviceLocale();
|
|
101
|
-
if (locale) return locale;
|
|
102
|
-
}
|
|
103
|
-
if (typeof deviceInfoAny.getDeviceLocales === 'function') {
|
|
104
|
-
const locales = await deviceInfoAny.getDeviceLocales();
|
|
105
|
-
if (Array.isArray(locales) && locales.length > 0) {
|
|
106
|
-
const first = locales[0];
|
|
107
|
-
if (typeof first === 'string') return first;
|
|
108
|
-
if (first?.languageTag) return first.languageTag;
|
|
109
|
-
if (first?.languageCode) {
|
|
110
|
-
return first.countryCode
|
|
111
|
-
? `${first.languageCode}_${first.countryCode}`
|
|
112
|
-
: first.languageCode;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return 'en_US';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
97
|
function mapDeviceType(deviceType: string): string {
|
|
120
98
|
switch (deviceType?.toLowerCase()) {
|
|
121
99
|
case 'handset':
|
package/src/core/fontAwesome.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import FontAwesome6 from '
|
|
1
|
+
import { FontAwesome6, getFontAwesomeGlyphMap } from '../native/vectorIcons';
|
|
2
2
|
|
|
3
3
|
const hexRegex = /^(0x)?[a-fA-F0-9]{4,5}$/;
|
|
4
4
|
|
|
@@ -50,14 +50,7 @@ export function resolveFontAwesomeIcon(
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function getGlyphMap(): Record<string, number> | null {
|
|
53
|
-
|
|
54
|
-
if (typeof anyIcon.getRawGlyphMap === 'function') {
|
|
55
|
-
return anyIcon.getRawGlyphMap();
|
|
56
|
-
}
|
|
57
|
-
if (anyIcon.glyphMap) {
|
|
58
|
-
return anyIcon.glyphMap as Record<string, number>;
|
|
59
|
-
}
|
|
60
|
-
return null;
|
|
53
|
+
return getFontAwesomeGlyphMap();
|
|
61
54
|
}
|
|
62
55
|
|
|
63
56
|
function styleProps(style: string): Record<string, any> {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
1
|
import type { FlowboardData } from '../types/flowboard';
|
|
2
|
+
import { FlowboardAsyncStorage } from '../native/asyncStorage';
|
|
3
3
|
|
|
4
4
|
const STORAGE_KEY = 'flowboard_onboarding_json';
|
|
5
5
|
const FETCH_TIME_KEY = 'flowboard_fetch_time';
|
|
@@ -9,12 +9,12 @@ const PROGRESS_FORM_DATA_KEY = 'flowboard_progress_form_data';
|
|
|
9
9
|
|
|
10
10
|
export class OnboardingRepository {
|
|
11
11
|
async saveOnboardingJson(json: FlowboardData): Promise<void> {
|
|
12
|
-
await
|
|
13
|
-
await
|
|
12
|
+
await FlowboardAsyncStorage.setItem(STORAGE_KEY, JSON.stringify(json));
|
|
13
|
+
await FlowboardAsyncStorage.setItem(FETCH_TIME_KEY, Date.now().toString());
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
async getOnboardingJson(): Promise<FlowboardData | null> {
|
|
17
|
-
const jsonString = await
|
|
17
|
+
const jsonString = await FlowboardAsyncStorage.getItem(STORAGE_KEY);
|
|
18
18
|
if (!jsonString) return null;
|
|
19
19
|
try {
|
|
20
20
|
return JSON.parse(jsonString) as FlowboardData;
|
|
@@ -25,7 +25,7 @@ export class OnboardingRepository {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
async clearOnboardingJson(): Promise<void> {
|
|
28
|
-
await
|
|
28
|
+
await FlowboardAsyncStorage.multiRemove([STORAGE_KEY, FETCH_TIME_KEY]);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
async saveProgress(params: {
|
|
@@ -33,18 +33,21 @@ export class OnboardingRepository {
|
|
|
33
33
|
stepIndex: number;
|
|
34
34
|
formData: Record<string, any>;
|
|
35
35
|
}): Promise<void> {
|
|
36
|
-
await
|
|
37
|
-
await
|
|
38
|
-
|
|
36
|
+
await FlowboardAsyncStorage.setItem(PROGRESS_FLOW_KEY, params.flowId);
|
|
37
|
+
await FlowboardAsyncStorage.setItem(
|
|
38
|
+
PROGRESS_STEP_KEY,
|
|
39
|
+
params.stepIndex.toString()
|
|
40
|
+
);
|
|
41
|
+
await FlowboardAsyncStorage.setItem(
|
|
39
42
|
PROGRESS_FORM_DATA_KEY,
|
|
40
43
|
JSON.stringify(params.formData)
|
|
41
44
|
);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
async getProgressStepForFlow(flowId: string): Promise<number | null> {
|
|
45
|
-
const storedFlowId = await
|
|
48
|
+
const storedFlowId = await FlowboardAsyncStorage.getItem(PROGRESS_FLOW_KEY);
|
|
46
49
|
if (storedFlowId !== flowId) return null;
|
|
47
|
-
const value = await
|
|
50
|
+
const value = await FlowboardAsyncStorage.getItem(PROGRESS_STEP_KEY);
|
|
48
51
|
if (!value) return null;
|
|
49
52
|
const parsed = Number(value);
|
|
50
53
|
return Number.isNaN(parsed) ? null : parsed;
|
|
@@ -53,9 +56,11 @@ export class OnboardingRepository {
|
|
|
53
56
|
async getProgressFormDataForFlow(
|
|
54
57
|
flowId: string
|
|
55
58
|
): Promise<Record<string, any> | null> {
|
|
56
|
-
const storedFlowId = await
|
|
59
|
+
const storedFlowId = await FlowboardAsyncStorage.getItem(PROGRESS_FLOW_KEY);
|
|
57
60
|
if (storedFlowId !== flowId) return null;
|
|
58
|
-
const jsonString = await
|
|
61
|
+
const jsonString = await FlowboardAsyncStorage.getItem(
|
|
62
|
+
PROGRESS_FORM_DATA_KEY
|
|
63
|
+
);
|
|
59
64
|
if (!jsonString) return null;
|
|
60
65
|
try {
|
|
61
66
|
const decoded = JSON.parse(jsonString);
|
|
@@ -70,7 +75,7 @@ export class OnboardingRepository {
|
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
async clearProgress(): Promise<void> {
|
|
73
|
-
await
|
|
78
|
+
await FlowboardAsyncStorage.multiRemove([
|
|
74
79
|
PROGRESS_FLOW_KEY,
|
|
75
80
|
PROGRESS_STEP_KEY,
|
|
76
81
|
PROGRESS_FORM_DATA_KEY,
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
|
+
import { hasNativeModule, safeAsync } from './runtime';
|
|
3
|
+
|
|
4
|
+
const memoryStore = new Map<string, string>();
|
|
5
|
+
|
|
6
|
+
const warning =
|
|
7
|
+
'AsyncStorage is unavailable. Flowboard will fall back to in-memory storage until native setup is fixed.';
|
|
8
|
+
|
|
9
|
+
export const asyncStorageAvailable = hasNativeModule(
|
|
10
|
+
'RNCAsyncStorage',
|
|
11
|
+
'RNC_AsyncSQLiteDBStorage',
|
|
12
|
+
'AsyncSQLiteDBStorage',
|
|
13
|
+
'AsyncLocalStorage',
|
|
14
|
+
'PlatformLocalStorage'
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
function getMemoryValue(key: string): string | null {
|
|
18
|
+
return memoryStore.has(key) ? memoryStore.get(key) ?? null : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function getItem(key: string): Promise<string | null> {
|
|
22
|
+
const fallback = getMemoryValue(key);
|
|
23
|
+
return safeAsync(
|
|
24
|
+
'native-async-storage-get',
|
|
25
|
+
asyncStorageAvailable,
|
|
26
|
+
async () => {
|
|
27
|
+
const value = await AsyncStorage.getItem(key);
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
memoryStore.set(key, value);
|
|
30
|
+
}
|
|
31
|
+
return value ?? fallback;
|
|
32
|
+
},
|
|
33
|
+
fallback,
|
|
34
|
+
warning
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function setItem(key: string, value: string): Promise<void> {
|
|
39
|
+
memoryStore.set(key, value);
|
|
40
|
+
await safeAsync(
|
|
41
|
+
'native-async-storage-set',
|
|
42
|
+
asyncStorageAvailable,
|
|
43
|
+
async () => {
|
|
44
|
+
await AsyncStorage.setItem(key, value);
|
|
45
|
+
},
|
|
46
|
+
undefined,
|
|
47
|
+
warning
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function removeItem(key: string): Promise<void> {
|
|
52
|
+
memoryStore.delete(key);
|
|
53
|
+
await safeAsync(
|
|
54
|
+
'native-async-storage-remove',
|
|
55
|
+
asyncStorageAvailable,
|
|
56
|
+
async () => {
|
|
57
|
+
await AsyncStorage.removeItem(key);
|
|
58
|
+
},
|
|
59
|
+
undefined,
|
|
60
|
+
warning
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function multiRemove(keys: string[]): Promise<void> {
|
|
65
|
+
keys.forEach((key) => memoryStore.delete(key));
|
|
66
|
+
await safeAsync(
|
|
67
|
+
'native-async-storage-multi-remove',
|
|
68
|
+
asyncStorageAvailable,
|
|
69
|
+
async () => {
|
|
70
|
+
await AsyncStorage.multiRemove(keys);
|
|
71
|
+
},
|
|
72
|
+
undefined,
|
|
73
|
+
warning
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function clear(): Promise<void> {
|
|
78
|
+
memoryStore.clear();
|
|
79
|
+
await safeAsync(
|
|
80
|
+
'native-async-storage-clear',
|
|
81
|
+
asyncStorageAvailable,
|
|
82
|
+
async () => {
|
|
83
|
+
if (typeof (AsyncStorage as any).clear === 'function') {
|
|
84
|
+
await (AsyncStorage as any).clear();
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
undefined,
|
|
88
|
+
warning
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const FlowboardAsyncStorage = {
|
|
93
|
+
available: asyncStorageAvailable,
|
|
94
|
+
clear,
|
|
95
|
+
getItem,
|
|
96
|
+
multiRemove,
|
|
97
|
+
removeItem,
|
|
98
|
+
setItem,
|
|
99
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import DeviceInfo from 'react-native-device-info';
|
|
2
|
+
import { hasNativeModule, safeAsync, safeSync } from './runtime';
|
|
3
|
+
|
|
4
|
+
const warning =
|
|
5
|
+
'react-native-device-info is unavailable. Flowboard will use generic device metadata until native setup is fixed.';
|
|
6
|
+
const nativeDeviceInfo = DeviceInfo as any;
|
|
7
|
+
|
|
8
|
+
export const deviceInfoAvailable = hasNativeModule('RNDeviceInfo');
|
|
9
|
+
|
|
10
|
+
export const FlowboardDeviceInfo = {
|
|
11
|
+
available: deviceInfoAvailable,
|
|
12
|
+
getBuildNumber(): string {
|
|
13
|
+
return safeSync(
|
|
14
|
+
'native-device-info-build-number',
|
|
15
|
+
deviceInfoAvailable,
|
|
16
|
+
() => String(nativeDeviceInfo.getBuildNumber?.() ?? '1'),
|
|
17
|
+
'1',
|
|
18
|
+
warning
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
async getDeviceLocale(): Promise<string> {
|
|
22
|
+
return safeAsync(
|
|
23
|
+
'native-device-info-locale',
|
|
24
|
+
deviceInfoAvailable,
|
|
25
|
+
async () => {
|
|
26
|
+
if (typeof nativeDeviceInfo.getDeviceLocale === 'function') {
|
|
27
|
+
const locale = await nativeDeviceInfo.getDeviceLocale();
|
|
28
|
+
if (locale) return String(locale);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof nativeDeviceInfo.getDeviceLocales === 'function') {
|
|
32
|
+
const locales = await nativeDeviceInfo.getDeviceLocales();
|
|
33
|
+
if (Array.isArray(locales) && locales.length > 0) {
|
|
34
|
+
const first = locales[0];
|
|
35
|
+
if (typeof first === 'string') return first;
|
|
36
|
+
if (first?.languageTag) return String(first.languageTag);
|
|
37
|
+
if (first?.languageCode) {
|
|
38
|
+
return first.countryCode
|
|
39
|
+
? `${first.languageCode}_${first.countryCode}`
|
|
40
|
+
: String(first.languageCode);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return 'en_US';
|
|
46
|
+
},
|
|
47
|
+
'en_US',
|
|
48
|
+
warning
|
|
49
|
+
);
|
|
50
|
+
},
|
|
51
|
+
async getDeviceType(): Promise<string> {
|
|
52
|
+
return safeAsync(
|
|
53
|
+
'native-device-info-device-type',
|
|
54
|
+
deviceInfoAvailable,
|
|
55
|
+
async () =>
|
|
56
|
+
String((await nativeDeviceInfo.getDeviceType?.()) ?? 'Handset'),
|
|
57
|
+
'Handset',
|
|
58
|
+
warning
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
getBundleId(): string {
|
|
62
|
+
return safeSync(
|
|
63
|
+
'native-device-info-bundle-id',
|
|
64
|
+
deviceInfoAvailable,
|
|
65
|
+
() => String(nativeDeviceInfo.getBundleId?.() ?? 'unknown.bundle'),
|
|
66
|
+
'unknown.bundle',
|
|
67
|
+
warning
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
getSystemVersion(): string {
|
|
71
|
+
return safeSync(
|
|
72
|
+
'native-device-info-system-version',
|
|
73
|
+
deviceInfoAvailable,
|
|
74
|
+
() => String(nativeDeviceInfo.getSystemVersion?.() ?? '0'),
|
|
75
|
+
'0',
|
|
76
|
+
warning
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
getVersion(): string {
|
|
80
|
+
return safeSync(
|
|
81
|
+
'native-device-info-version',
|
|
82
|
+
deviceInfoAvailable,
|
|
83
|
+
() => String(nativeDeviceInfo.getVersion?.() ?? '0.0.0'),
|
|
84
|
+
'0.0.0',
|
|
85
|
+
warning
|
|
86
|
+
);
|
|
87
|
+
},
|
|
88
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import InAppReview from 'react-native-in-app-review';
|
|
2
|
+
import { hasNativeModule, safeAsync, safeSync } from './runtime';
|
|
3
|
+
|
|
4
|
+
const warning =
|
|
5
|
+
'react-native-in-app-review is unavailable. Flowboard will skip in-app review prompts until native setup is fixed.';
|
|
6
|
+
const nativeInAppReview = InAppReview as any;
|
|
7
|
+
|
|
8
|
+
export const inAppReviewAvailable = hasNativeModule(
|
|
9
|
+
'InAppReviewModule',
|
|
10
|
+
'RNInAppReviewIOS'
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export function isInAppReviewAvailable(): boolean {
|
|
14
|
+
return safeSync(
|
|
15
|
+
'native-in-app-review-available',
|
|
16
|
+
inAppReviewAvailable,
|
|
17
|
+
() => Boolean(nativeInAppReview.isAvailable?.()),
|
|
18
|
+
false,
|
|
19
|
+
warning
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function requestInAppReview(): Promise<boolean> {
|
|
24
|
+
return safeAsync(
|
|
25
|
+
'native-in-app-review-request',
|
|
26
|
+
inAppReviewAvailable,
|
|
27
|
+
async () => {
|
|
28
|
+
const result = nativeInAppReview.RequestInAppReview?.();
|
|
29
|
+
return typeof result === 'boolean' ? result : Boolean(await result);
|
|
30
|
+
},
|
|
31
|
+
false,
|
|
32
|
+
warning
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import NativeLinearGradient from 'react-native-linear-gradient';
|
|
3
|
+
import { hasViewManager, warnOnce } from './runtime';
|
|
4
|
+
|
|
5
|
+
const warning =
|
|
6
|
+
'react-native-linear-gradient is unavailable. Flowboard will fall back to solid backgrounds until native setup is fixed.';
|
|
7
|
+
|
|
8
|
+
export const linearGradientAvailable = hasViewManager('BVLinearGradient');
|
|
9
|
+
|
|
10
|
+
export default function LinearGradient(props: any) {
|
|
11
|
+
if (!linearGradientAvailable) {
|
|
12
|
+
warnOnce('native-linear-gradient', warning);
|
|
13
|
+
const { children, colors, style } = props;
|
|
14
|
+
const backgroundColor =
|
|
15
|
+
Array.isArray(colors) && colors.length > 0 ? colors[0] : undefined;
|
|
16
|
+
return (
|
|
17
|
+
<View style={[style, backgroundColor ? { backgroundColor } : null]}>
|
|
18
|
+
{children}
|
|
19
|
+
</View>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return <NativeLinearGradient {...props} />;
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import NativeLottieView from 'lottie-react-native';
|
|
3
|
+
import { hasViewManager, warnOnce } from './runtime';
|
|
4
|
+
|
|
5
|
+
const warning =
|
|
6
|
+
'lottie-react-native is unavailable. Flowboard will reserve space for animations but skip playback until native setup is fixed.';
|
|
7
|
+
|
|
8
|
+
export const lottieAvailable = hasViewManager('LottieAnimationView');
|
|
9
|
+
|
|
10
|
+
export default function LottieView(props: any) {
|
|
11
|
+
if (!lottieAvailable) {
|
|
12
|
+
warnOnce('native-lottie', warning);
|
|
13
|
+
return <View style={props.style} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <NativeLottieView {...props} />;
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { View } from 'react-native';
|
|
2
|
+
import NativeMaskedView from '@react-native-masked-view/masked-view';
|
|
3
|
+
import { hasViewManager, warnOnce } from './runtime';
|
|
4
|
+
|
|
5
|
+
const warning =
|
|
6
|
+
'@react-native-masked-view/masked-view is unavailable. Flowboard will render masked content without the mask until native setup is fixed.';
|
|
7
|
+
|
|
8
|
+
export const maskedViewAvailable = hasViewManager('RNCMaskedView');
|
|
9
|
+
|
|
10
|
+
export default function MaskedView(props: any) {
|
|
11
|
+
if (!maskedViewAvailable) {
|
|
12
|
+
warnOnce('native-masked-view', warning);
|
|
13
|
+
const { children, ...viewProps } = props;
|
|
14
|
+
delete viewProps.maskElement;
|
|
15
|
+
return <View {...viewProps}>{children}</View>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <NativeMaskedView {...props} />;
|
|
19
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import NativePagerView from 'react-native-pager-view';
|
|
4
|
+
import { hasViewManager, warnOnce } from './runtime';
|
|
5
|
+
|
|
6
|
+
export type PagerViewHandle = {
|
|
7
|
+
setPage: (index: number) => void;
|
|
8
|
+
setPageWithoutAnimation: (index: number) => void;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type PagerViewOnPageSelectedEvent = {
|
|
12
|
+
nativeEvent: {
|
|
13
|
+
position: number;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const warning =
|
|
18
|
+
'react-native-pager-view is unavailable. Flowboard will render one page at a time without native paging until setup is fixed.';
|
|
19
|
+
|
|
20
|
+
export const pagerViewAvailable = hasViewManager('RNCViewPager');
|
|
21
|
+
|
|
22
|
+
function clampPage(index: number, totalPages: number): number {
|
|
23
|
+
if (totalPages <= 0) return 0;
|
|
24
|
+
if (index < 0) return 0;
|
|
25
|
+
if (index >= totalPages) return totalPages - 1;
|
|
26
|
+
return index;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class PagerViewFallback extends React.Component<any, { page: number }> {
|
|
30
|
+
constructor(props: any) {
|
|
31
|
+
super(props);
|
|
32
|
+
this.state = {
|
|
33
|
+
page: clampPage(
|
|
34
|
+
Number(props.initialPage ?? 0),
|
|
35
|
+
React.Children.count(props.children)
|
|
36
|
+
),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
componentDidUpdate(prevProps: any): void {
|
|
41
|
+
if (prevProps.initialPage !== this.props.initialPage) {
|
|
42
|
+
this.setPageWithoutAnimation(Number(this.props.initialPage ?? 0));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const totalPages = React.Children.count(this.props.children);
|
|
47
|
+
const nextPage = clampPage(this.state.page, totalPages);
|
|
48
|
+
if (nextPage !== this.state.page) {
|
|
49
|
+
this.setState({ page: nextPage });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setPage = (index: number) => {
|
|
54
|
+
this.updatePage(index);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
setPageWithoutAnimation = (index: number) => {
|
|
58
|
+
this.updatePage(index);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
private updatePage(index: number): void {
|
|
62
|
+
warnOnce('native-pager-view', warning);
|
|
63
|
+
const totalPages = React.Children.count(this.props.children);
|
|
64
|
+
const nextPage = clampPage(index, totalPages);
|
|
65
|
+
if (nextPage === this.state.page) return;
|
|
66
|
+
|
|
67
|
+
this.setState({ page: nextPage });
|
|
68
|
+
this.props.onPageSelected?.({
|
|
69
|
+
nativeEvent: { position: nextPage },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
render() {
|
|
74
|
+
const viewProps = { ...this.props } as Record<string, any>;
|
|
75
|
+
const { children, style } = viewProps;
|
|
76
|
+
delete viewProps.initialPage;
|
|
77
|
+
delete viewProps.onPageSelected;
|
|
78
|
+
delete viewProps.orientation;
|
|
79
|
+
delete viewProps.overdrag;
|
|
80
|
+
delete viewProps.pageMargin;
|
|
81
|
+
|
|
82
|
+
const pages = React.Children.toArray(children);
|
|
83
|
+
return (
|
|
84
|
+
<View {...viewProps} style={style}>
|
|
85
|
+
{pages[this.state.page] ?? null}
|
|
86
|
+
</View>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const PagerViewComponent = pagerViewAvailable
|
|
92
|
+
? NativePagerView
|
|
93
|
+
: PagerViewFallback;
|
|
94
|
+
|
|
95
|
+
export default PagerViewComponent as React.ComponentType<any>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
openSettings as nativeOpenSettings,
|
|
3
|
+
PERMISSIONS as nativePermissions,
|
|
4
|
+
request as nativeRequest,
|
|
5
|
+
requestNotifications as nativeRequestNotifications,
|
|
6
|
+
RESULTS as nativeResults,
|
|
7
|
+
} from 'react-native-permissions';
|
|
8
|
+
import { hasNativeModule, safeAsync } from './runtime';
|
|
9
|
+
|
|
10
|
+
const warning =
|
|
11
|
+
'react-native-permissions is unavailable. Permission actions will return unavailable until native setup is fixed.';
|
|
12
|
+
|
|
13
|
+
export const permissionsAvailable = hasNativeModule('RNPermissions');
|
|
14
|
+
|
|
15
|
+
export const RESULTS = nativeResults ?? {
|
|
16
|
+
BLOCKED: 'blocked',
|
|
17
|
+
GRANTED: 'granted',
|
|
18
|
+
LIMITED: 'limited',
|
|
19
|
+
UNAVAILABLE: 'unavailable',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const PERMISSIONS = nativePermissions ?? {
|
|
23
|
+
ANDROID: {},
|
|
24
|
+
IOS: {},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export async function openSettings(): Promise<void> {
|
|
28
|
+
await safeAsync(
|
|
29
|
+
'native-permissions-open-settings',
|
|
30
|
+
permissionsAvailable,
|
|
31
|
+
async () => {
|
|
32
|
+
await nativeOpenSettings();
|
|
33
|
+
},
|
|
34
|
+
undefined,
|
|
35
|
+
warning
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function request(permission: string): Promise<string> {
|
|
40
|
+
return safeAsync(
|
|
41
|
+
'native-permissions-request',
|
|
42
|
+
permissionsAvailable && permission.length > 0,
|
|
43
|
+
async () => nativeRequest(permission),
|
|
44
|
+
RESULTS.UNAVAILABLE ?? 'unavailable',
|
|
45
|
+
warning
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function requestNotifications(
|
|
50
|
+
options?: string[]
|
|
51
|
+
): Promise<{ status: string }> {
|
|
52
|
+
return safeAsync(
|
|
53
|
+
'native-permissions-request-notifications',
|
|
54
|
+
permissionsAvailable,
|
|
55
|
+
async () => nativeRequestNotifications(options),
|
|
56
|
+
{ status: RESULTS.UNAVAILABLE ?? 'unavailable' },
|
|
57
|
+
warning
|
|
58
|
+
);
|
|
59
|
+
}
|