Skip to content

Commit 3e3c510

Browse files
authored
Windows (#415)
* Added manual pair device * permissoin fix
1 parent a8d0f56 commit 3e3c510

10 files changed

Lines changed: 406 additions & 25 deletions

File tree

apps/box/android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ android {
8686
applicationId "land.fx.blox"
8787
minSdkVersion rootProject.ext.minSdkVersion
8888
targetSdkVersion rootProject.ext.targetSdkVersion
89-
versionCode 256
90-
versionName "2.5.0"
89+
versionCode 257
90+
versionName "2.5.1"
9191

9292
testBuildType System.getProperty('testBuildType', 'debug')
9393
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

apps/box/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
22
xmlns:tools="http://schemas.android.com/tools">
33

4+
<uses-permission android:name="android.permission.CAMERA"/>
45
<uses-permission android:name="android.permission.INTERNET" />
56
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
67
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

apps/box/ios/Box.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@
589589
"$(inherited)",
590590
"@executable_path/Frameworks",
591591
);
592-
MARKETING_VERSION = 2.5.0;
592+
MARKETING_VERSION = 2.5.1;
593593
OTHER_LDFLAGS = (
594594
"$(inherited)",
595595
"-ObjC",
@@ -627,7 +627,7 @@
627627
"$(inherited)",
628628
"@executable_path/Frameworks",
629629
);
630-
MARKETING_VERSION = 2.5.0;
630+
MARKETING_VERSION = 2.5.1;
631631
OTHER_LDFLAGS = (
632632
"$(inherited)",
633633
"-ObjC",

apps/box/ios/Box/Info.plist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
</dict>
7474
</dict>
7575
</dict>
76+
<key>NSCameraUsageDescription</key>
77+
<string>This app uses the camera to scan QR codes for auto-pin pairing setup.</string>
7678
<key>NSBluetoothAlwaysUsageDescription</key>
7779
<string>This app uses Bluetooth to connect to nearby devices for setting up the hardware iOT device locally without wifi.</string>
7880
<key>NSBluetoothPeripheralUsageDescription</key>

apps/box/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "box",
3-
"version": "2.5.0",
3+
"version": "2.5.1",
44
"private": true,
55
"dependencies": {
66
"@babel/core": "*",
@@ -78,6 +78,7 @@
7878
"react-native-syntax-highlighter": "*",
7979
"react-native-tab-view": "*",
8080
"react-native-url-polyfill": "*",
81+
"react-native-vision-camera": "*",
8182
"react-native-webview": "*",
8283
"react-native-wheel-color-picker": "*",
8384
"react-native-wifi-reborn": "*",

apps/box/src/components/SettingsList/SettingsMenu.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ export const SettingsMenu = () => {
8080
// detail: null,
8181
// onPress: () => navigation.navigate(Routes.BloxLogs),
8282
// },
83+
{
84+
name: 'Auto-Pin Pairing',
85+
detail: null,
86+
onPress: () => navigation.navigate(Routes.AutoPinPairing),
87+
},
8388
{
8489
name: 'About',
8590
detail: null,

apps/box/src/screens/Settings/AutoPinPairing/AutoPinPairing.screen.tsx

Lines changed: 222 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import React, { useState } from 'react';
2-
import { Alert, Linking } from 'react-native';
2+
import {
3+
Alert,
4+
Linking,
5+
TextInput,
6+
StyleSheet,
7+
TouchableOpacity,
8+
} from 'react-native';
39
import {
410
FxBox,
511
FxButton,
612
FxSafeAreaBox,
713
FxText,
14+
useFxTheme,
815
} from '@functionland/component-library';
916
import { NativeStackScreenProps } from '@react-navigation/native-stack';
1017
import { blockchain, fula } from '@functionland/react-native-fula';
@@ -14,6 +21,8 @@ import {
1421
} from '../../../navigation/navigationConfig';
1522
import { useBloxsStore } from '../../../stores/useBloxsStore';
1623
import { useDAppsStore } from '../../../stores/dAppsSettingsStore';
24+
import { copyToClipboard } from '../../../utils/clipboard';
25+
import { QRScannerModal } from './QRScannerModal';
1726

1827
type Props = NativeStackScreenProps<
1928
SettingsStackParamList,
@@ -24,19 +33,32 @@ export const AutoPinPairingScreen = ({ route, navigation }: Props) => {
2433
const [loading, setLoading] = useState(false);
2534
const [error, setError] = useState<string | null>(null);
2635
const [success, setSuccess] = useState(false);
36+
const [scannerVisible, setScannerVisible] = useState(false);
37+
const [copied, setCopied] = useState(false);
38+
39+
// Manual mode state
40+
const [tokenInput, setTokenInput] = useState('');
41+
const [endpointInput, setEndpointInput] = useState('');
42+
const [pairingSecret, setPairingSecret] = useState<string | null>(null);
2743

2844
const bloxs = useBloxsStore((state) => state.bloxs);
2945
const currentBloxPeerId = useBloxsStore((state) => state.currentBloxPeerId);
3046
const addOrUpdateDApp = useDAppsStore((state) => state.addOrUpdateDApp);
3147

48+
const theme = useFxTheme();
49+
50+
// Deep link params
3251
const token = route?.params?.token;
3352
const endpoint = route?.params?.endpoint;
3453
const returnUrl = route?.params?.returnUrl;
3554

55+
const isDeepLinkMode = !!token;
56+
3657
const currentBlox = bloxs[currentBloxPeerId];
3758
const bloxName = currentBlox?.name || 'My Blox';
3859

39-
const handlePair = async () => {
60+
// Deep link mode handler (existing behavior)
61+
const handleDeepLinkPair = async () => {
4062
if (!token || !endpoint) {
4163
setError('Missing pairing parameters');
4264
return;
@@ -106,9 +128,102 @@ export const AutoPinPairingScreen = ({ route, navigation }: Props) => {
106128
}
107129
};
108130

109-
// Note: We intentionally don't auto-trigger pairing.
110-
// User must read the confirmation and press the button.
131+
// Manual mode handler
132+
const handleManualPair = async () => {
133+
if (!tokenInput || !endpointInput) {
134+
setError('Please fill in both API Key and Endpoint');
135+
return;
136+
}
137+
138+
setLoading(true);
139+
setError(null);
140+
setPairingSecret(null);
141+
142+
try {
143+
await fula.isReady(false);
111144

145+
const result = await blockchain.autoPinPair(tokenInput, endpointInput);
146+
147+
if (result?.pairing_secret) {
148+
setPairingSecret(result.pairing_secret);
149+
} else {
150+
setError('Unexpected response from blox');
151+
}
152+
} catch (e: any) {
153+
const msg = e?.message || e?.toString() || 'Unknown error';
154+
setError(msg);
155+
} finally {
156+
setLoading(false);
157+
}
158+
};
159+
160+
const handleCopy = () => {
161+
if (pairingSecret) {
162+
copyToClipboard(pairingSecret);
163+
setCopied(true);
164+
setTimeout(() => setCopied(false), 2000);
165+
}
166+
};
167+
168+
const handleQRScanned = (api: string, qrEndpoint: string) => {
169+
setTokenInput(api);
170+
setEndpointInput(qrEndpoint);
171+
setScannerVisible(false);
172+
};
173+
174+
// Deep link mode UI (unchanged behavior)
175+
if (isDeepLinkMode) {
176+
return (
177+
<FxSafeAreaBox flex={1} edges={['top']} paddingHorizontal="20">
178+
<FxBox marginTop="32">
179+
<FxText variant="h300" marginBottom="16">
180+
Auto-Pin Pairing
181+
</FxText>
182+
183+
<FxText variant="bodyMediumRegular" marginBottom="24" color="content2">
184+
FxFiles wants to enable auto-pinning on your blox. This will
185+
automatically pin your uploaded files to{' '}
186+
<FxText variant="bodySmallSemibold">{bloxName}</FxText>, allowing you to
187+
download them directly from your local network.
188+
</FxText>
189+
190+
{error && (
191+
<FxBox
192+
backgroundColor="errorBase"
193+
padding="16"
194+
borderRadius="s"
195+
marginBottom="16"
196+
>
197+
<FxText color="content1">{error}</FxText>
198+
</FxBox>
199+
)}
200+
201+
{success ? (
202+
<FxBox
203+
backgroundColor="greenBackground"
204+
padding="16"
205+
borderRadius="s"
206+
>
207+
<FxText color="content1">
208+
Auto-pinning is enabled! Your files will be automatically pinned
209+
to this blox.
210+
</FxText>
211+
</FxBox>
212+
) : (
213+
<FxButton
214+
size="large"
215+
onPress={handleDeepLinkPair}
216+
disabled={loading || !token || !endpoint}
217+
>
218+
{loading ? 'Pairing...' : 'Enable Auto-Pin'}
219+
</FxButton>
220+
)}
221+
</FxBox>
222+
</FxSafeAreaBox>
223+
);
224+
}
225+
226+
// Manual mode UI
112227
return (
113228
<FxSafeAreaBox flex={1} edges={['top']} paddingHorizontal="20">
114229
<FxBox marginTop="32">
@@ -117,12 +232,59 @@ export const AutoPinPairingScreen = ({ route, navigation }: Props) => {
117232
</FxText>
118233

119234
<FxText variant="bodyMediumRegular" marginBottom="24" color="content2">
120-
FxFiles wants to enable auto-pinning on your blox. This will
121-
automatically pin your uploaded files to{' '}
122-
<FxText variant="bodySmallSemibold">{bloxName}</FxText>, allowing you to
123-
download them directly from your local network.
235+
Scan a QR code or enter the API key and endpoint manually to pair an
236+
app for auto-pinning.
124237
</FxText>
125238

239+
<FxButton
240+
size="large"
241+
marginBottom="24"
242+
onPress={() => setScannerVisible(true)}
243+
>
244+
Scan QR Code
245+
</FxButton>
246+
247+
<FxText variant="bodySmallSemibold" marginBottom="8" color="content2">
248+
API Key
249+
</FxText>
250+
<TextInput
251+
style={[
252+
styles.input,
253+
{
254+
color: theme.colors.content1,
255+
borderColor: theme.colors.border,
256+
backgroundColor: theme.colors.backgroundSecondary,
257+
},
258+
]}
259+
value={tokenInput}
260+
onChangeText={setTokenInput}
261+
placeholder="Enter API key"
262+
placeholderTextColor={theme.colors.content3}
263+
autoCapitalize="none"
264+
autoCorrect={false}
265+
/>
266+
267+
<FxText variant="bodySmallSemibold" marginBottom="8" color="content2">
268+
Endpoint
269+
</FxText>
270+
<TextInput
271+
style={[
272+
styles.input,
273+
{
274+
color: theme.colors.content1,
275+
borderColor: theme.colors.border,
276+
backgroundColor: theme.colors.backgroundSecondary,
277+
},
278+
]}
279+
value={endpointInput}
280+
onChangeText={setEndpointInput}
281+
placeholder="Enter endpoint URL"
282+
placeholderTextColor={theme.colors.content3}
283+
autoCapitalize="none"
284+
autoCorrect={false}
285+
keyboardType="url"
286+
/>
287+
126288
{error && (
127289
<FxBox
128290
backgroundColor="errorBase"
@@ -134,27 +296,67 @@ export const AutoPinPairingScreen = ({ route, navigation }: Props) => {
134296
</FxBox>
135297
)}
136298

137-
{success ? (
138-
<FxBox
139-
backgroundColor="greenBackground"
140-
padding="16"
141-
borderRadius="s"
142-
>
143-
<FxText color="content1">
144-
Auto-pinning is enabled! Your files will be automatically pinned
145-
to this blox.
299+
{pairingSecret ? (
300+
<FxBox marginBottom="16">
301+
<FxText variant="bodySmallSemibold" marginBottom="8" color="content2">
302+
Pairing Secret
146303
</FxText>
304+
<TouchableOpacity onPress={handleCopy} activeOpacity={0.7}>
305+
<FxBox
306+
backgroundColor="backgroundSecondary"
307+
padding="16"
308+
borderRadius="s"
309+
flexDirection="row"
310+
alignItems="center"
311+
justifyContent="space-between"
312+
>
313+
<FxText
314+
variant="bodyMediumRegular"
315+
color="content1"
316+
style={styles.secretText}
317+
>
318+
{pairingSecret}
319+
</FxText>
320+
<FxText
321+
variant="bodySmallSemibold"
322+
color={copied ? 'greenBase' : 'primary'}
323+
>
324+
{copied ? 'Copied!' : 'Copy'}
325+
</FxText>
326+
</FxBox>
327+
</TouchableOpacity>
147328
</FxBox>
148329
) : (
149330
<FxButton
150331
size="large"
151-
onPress={handlePair}
152-
disabled={loading || !token || !endpoint}
332+
onPress={handleManualPair}
333+
disabled={loading || !tokenInput || !endpointInput}
153334
>
154-
{loading ? 'Pairing...' : 'Enable Auto-Pin'}
335+
{loading ? 'Getting Secret...' : 'Get Secret'}
155336
</FxButton>
156337
)}
157338
</FxBox>
339+
340+
<QRScannerModal
341+
visible={scannerVisible}
342+
onScanned={handleQRScanned}
343+
onClose={() => setScannerVisible(false)}
344+
/>
158345
</FxSafeAreaBox>
159346
);
160347
};
348+
349+
const styles = StyleSheet.create({
350+
input: {
351+
borderWidth: 1,
352+
borderRadius: 8,
353+
paddingHorizontal: 12,
354+
paddingVertical: 10,
355+
fontSize: 14,
356+
marginBottom: 16,
357+
},
358+
secretText: {
359+
flex: 1,
360+
marginRight: 12,
361+
},
362+
});

0 commit comments

Comments
 (0)