diff --git a/App.tsx b/App.tsx
index 0329d0c..478205d 100644
--- a/App.tsx
+++ b/App.tsx
@@ -1,20 +1,339 @@
-import { StatusBar } from 'expo-status-bar';
-import { StyleSheet, Text, View } from 'react-native';
+import { useState, useEffect } from 'react';
+import {
+ StyleSheet,
+ Text,
+ View,
+ TextInput,
+ TouchableOpacity,
+ Alert,
+ ActivityIndicator,
+ SafeAreaView,
+ StatusBar
+} from 'react-native';
+import { WebView } from 'react-native-webview';
+import AsyncStorage from '@react-native-async-storage/async-storage';
+
+const STORAGE_KEY = '@condomob_url';
+const DEFAULT_URL = 'https://condopay.fcarra.mywire.org';
export default function App() {
+ const [url, setUrl] = useState('');
+ const [isLoading, setIsLoading] = useState(true);
+ const [isValidating, setIsValidating] = useState(false);
+ const [showWebView, setShowWebView] = useState(false);
+
+ useEffect(() => {
+ loadSavedUrl();
+ }, []);
+
+ const loadSavedUrl = async () => {
+ try {
+ const savedUrl = await AsyncStorage.getItem(STORAGE_KEY);
+ if (savedUrl) {
+ setUrl(savedUrl);
+ setShowWebView(true);
+ }
+ } catch (error) {
+ console.error('Error loading saved URL:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const validateUrl = (urlString: string): boolean => {
+ try {
+ const parsedUrl = new URL(urlString);
+ return parsedUrl.protocol === 'http:' || parsedUrl.protocol === 'https:';
+ } catch {
+ return false;
+ }
+ };
+
+ const isCondoPayUrl = (urlString: string): boolean => {
+ // Check if URL contains 'condopay' in the hostname
+ const lowerUrl = urlString.toLowerCase();
+ return lowerUrl.includes('condopay');
+ };
+
+ const handleConnect = async () => {
+ if (!url.trim()) {
+ Alert.alert('Errore', 'Inserisci un URL valido');
+ return;
+ }
+
+ // Add https:// if no protocol is provided
+ let finalUrl = url.trim();
+ if (!finalUrl.startsWith('http://') && !finalUrl.startsWith('https://')) {
+ finalUrl = 'https://' + finalUrl;
+ }
+
+ // Validate URL format
+ if (!validateUrl(finalUrl)) {
+ Alert.alert('Errore', 'L\'URL inserito non รจ valido');
+ return;
+ }
+
+ // Check if it's a CondoPay URL
+ if (!isCondoPayUrl(finalUrl)) {
+ Alert.alert(
+ 'URL non valido',
+ 'L\'URL deve puntare a un\'istanza di CondoPay (deve contenere "condopay" nel dominio)'
+ );
+ return;
+ }
+
+ setIsValidating(true);
+
+ // Try to validate by checking if the URL is reachable
+ try {
+ const response = await fetch(finalUrl, {
+ method: 'HEAD',
+ signal: AbortSignal.timeout(10000)
+ });
+
+ if (response.ok || response.status === 200 || response.status === 301 || response.status === 302) {
+ // Save URL and show WebView
+ await AsyncStorage.setItem(STORAGE_KEY, finalUrl);
+ setUrl(finalUrl);
+ setShowWebView(true);
+ } else {
+ Alert.alert('Errore', 'Impossibile connettersi all\'istanza di CondoPay');
+ }
+ } catch (error) {
+ // If fetch fails, still try to show the WebView - it might work
+ console.log('Validation fetch failed, trying WebView anyway:', error);
+ try {
+ await AsyncStorage.setItem(STORAGE_KEY, finalUrl);
+ setUrl(finalUrl);
+ setShowWebView(true);
+ } catch (storageError) {
+ Alert.alert('Errore', 'Impossibile salvare l\'URL');
+ }
+ } finally {
+ setIsValidating(false);
+ }
+ };
+
+ const handleBack = () => {
+ setShowWebView(false);
+ };
+
+ // Loading screen while checking for saved URL
+ if (isLoading) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ // WebView screen
+ if (showWebView) {
+ return (
+
+
+
+
+ โ Indietro
+
+
+ CondoPay
+
+
+
+ (
+
+
+ Caricamento...
+
+ )}
+ />
+
+ );
+ }
+
+ // Configuration screen
return (
-
- Open up App.tsx to start working on your app!
-
-
+
+
+
+
+ ๐
+ CondoMob
+ Connetti al tuo CondoPay
+
+
+
+ URL Istanza CondoPay
+
+
+ Inserisci l'URL della tua istanza CondoPay
+
+
+
+
+ {isValidating ? (
+
+ ) : (
+ Connetti
+ )}
+
+
+
+ Esempio:
+ {DEFAULT_URL}
+
+
+
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
- backgroundColor: '#fff',
- alignItems: 'center',
+ backgroundColor: '#f5f5f5',
+ },
+ content: {
+ flex: 1,
+ padding: 24,
justifyContent: 'center',
},
+ logoContainer: {
+ alignItems: 'center',
+ marginBottom: 40,
+ },
+ logoText: {
+ fontSize: 64,
+ marginBottom: 16,
+ },
+ title: {
+ fontSize: 32,
+ fontWeight: 'bold',
+ color: '#333',
+ marginBottom: 8,
+ },
+ subtitle: {
+ fontSize: 16,
+ color: '#666',
+ },
+ inputContainer: {
+ marginBottom: 24,
+ },
+ label: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#333',
+ marginBottom: 8,
+ },
+ input: {
+ backgroundColor: '#fff',
+ borderRadius: 12,
+ padding: 16,
+ fontSize: 16,
+ borderWidth: 1,
+ borderColor: '#ddd',
+ },
+ hint: {
+ fontSize: 12,
+ color: '#999',
+ marginTop: 8,
+ },
+ button: {
+ backgroundColor: '#007AFF',
+ borderRadius: 12,
+ padding: 16,
+ alignItems: 'center',
+ marginBottom: 24,
+ },
+ buttonDisabled: {
+ backgroundColor: '#99c2ff',
+ },
+ buttonText: {
+ color: '#fff',
+ fontSize: 18,
+ fontWeight: '600',
+ },
+ exampleContainer: {
+ alignItems: 'center',
+ marginTop: 20,
+ },
+ exampleTitle: {
+ fontSize: 14,
+ color: '#999',
+ marginBottom: 8,
+ },
+ exampleUrl: {
+ fontSize: 14,
+ color: '#007AFF',
+ fontFamily: 'monospace',
+ },
+ // WebView styles
+ webViewContainer: {
+ flex: 1,
+ backgroundColor: '#fff',
+ },
+ webViewHeader: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ paddingHorizontal: 16,
+ paddingVertical: 12,
+ backgroundColor: '#fff',
+ borderBottomWidth: 1,
+ borderBottomColor: '#ddd',
+ },
+ backButton: {
+ padding: 8,
+ },
+ backButtonText: {
+ fontSize: 16,
+ color: '#007AFF',
+ },
+ headerTitle: {
+ fontSize: 18,
+ fontWeight: '600',
+ color: '#333',
+ flex: 1,
+ textAlign: 'center',
+ },
+ placeholder: {
+ width: 60,
+ },
+ webView: {
+ flex: 1,
+ },
+ loadingContainer: {
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: '#fff',
+ },
+ loadingText: {
+ marginTop: 12,
+ fontSize: 16,
+ color: '#666',
+ },
});
diff --git a/app.json b/app.json
index 33d76bb..e5df081 100644
--- a/app.json
+++ b/app.json
@@ -1,6 +1,6 @@
{
"expo": {
- "name": "condomob",
+ "name": "Condopay Mobile",
"slug": "condomob",
"version": "1.0.0",
"orientation": "portrait",
@@ -13,7 +13,8 @@
"backgroundColor": "#ffffff"
},
"ios": {
- "supportsTablet": true
+ "supportsTablet": true,
+ "bundleIdentifier": "com.okcomputer.condopay"
},
"android": {
"adaptiveIcon": {
@@ -21,10 +22,17 @@
"backgroundColor": "#ffffff"
},
"edgeToEdgeEnabled": true,
- "predictiveBackGestureEnabled": false
+ "predictiveBackGestureEnabled": false,
+ "permissions": ["INTERNET"],
+ "package": "com.okcomputer.condopay"
},
"web": {
"favicon": "./assets/favicon.png"
+ },
+ "extra": {
+ "eas": {
+ "projectId": "1af9def8-ac59-42a0-b40e-76cf8a58bd23"
+ }
}
}
}
diff --git a/eas.json b/eas.json
new file mode 100644
index 0000000..6e5c62e
--- /dev/null
+++ b/eas.json
@@ -0,0 +1,21 @@
+{
+ "cli": {
+ "version": ">= 18.0.3",
+ "appVersionSource": "remote"
+ },
+ "build": {
+ "development": {
+ "developmentClient": true,
+ "distribution": "internal"
+ },
+ "preview": {
+ "distribution": "internal"
+ },
+ "production": {
+ "autoIncrement": true
+ }
+ },
+ "submit": {
+ "production": {}
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index d686396..8fe0771 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,10 +8,13 @@
"name": "condomob",
"version": "1.0.0",
"dependencies": {
+ "@react-native-async-storage/async-storage": "^2.2.0",
"expo": "~54.0.33",
+ "expo-dev-client": "~6.0.20",
"expo-status-bar": "~3.0.9",
"react": "19.1.0",
- "react-native": "0.81.5"
+ "react-native": "0.81.5",
+ "react-native-webview": "^13.16.0"
},
"devDependencies": {
"@types/react": "~19.1.0",
@@ -2661,6 +2664,18 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@react-native-async-storage/async-storage": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz",
+ "integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==",
+ "license": "MIT",
+ "dependencies": {
+ "merge-options": "^3.0.4"
+ },
+ "peerDependencies": {
+ "react-native": "^0.0.0-0 || >=0.65 <1.0"
+ }
+ },
"node_modules/@react-native/assets-registry": {
"version": "0.81.5",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.5.tgz",
@@ -3137,6 +3152,22 @@
"node": ">= 14"
}
},
+ "node_modules/ajv": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
"node_modules/anser": {
"version": "1.4.10",
"resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz",
@@ -4292,6 +4323,76 @@
}
}
},
+ "node_modules/expo-dev-client": {
+ "version": "6.0.20",
+ "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-6.0.20.tgz",
+ "integrity": "sha512-5XjoVlj1OxakNxy55j/AUaGPrDOlQlB6XdHLLWAw61w5ffSpUDHDnuZzKzs9xY1eIaogOqTOQaAzZ2ddBkdXLA==",
+ "license": "MIT",
+ "dependencies": {
+ "expo-dev-launcher": "6.0.20",
+ "expo-dev-menu": "7.0.18",
+ "expo-dev-menu-interface": "2.0.0",
+ "expo-manifests": "~1.0.10",
+ "expo-updates-interface": "~2.0.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-dev-launcher": {
+ "version": "6.0.20",
+ "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-6.0.20.tgz",
+ "integrity": "sha512-a04zHEeT9sB0L5EB38fz7sNnUKJ2Ar1pXpcyl60Ki8bXPNCs9rjY7NuYrDkP/irM8+1DklMBqHpyHiLyJ/R+EA==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.11.0",
+ "expo-dev-menu": "7.0.18",
+ "expo-manifests": "~1.0.10"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-dev-menu": {
+ "version": "7.0.18",
+ "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-7.0.18.tgz",
+ "integrity": "sha512-4kTdlHrnZCAWCT6tZRQHSSjZ7vECFisL4T+nsG/GJDo/jcHNaOVGV5qPV9wzlTxyMk3YOPggRw4+g7Ownrg5eA==",
+ "license": "MIT",
+ "dependencies": {
+ "expo-dev-menu-interface": "2.0.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-dev-menu-interface": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/expo-dev-menu-interface/-/expo-dev-menu-interface-2.0.0.tgz",
+ "integrity": "sha512-BvAMPt6x+vyXpThsyjjOYyjwfjREV4OOpQkZ0tNl+nGpsPfcY9mc6DRACoWnH9KpLzyIt3BOgh3cuy/h/OxQjw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
+ "node_modules/expo-json-utils": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.15.0.tgz",
+ "integrity": "sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==",
+ "license": "MIT"
+ },
+ "node_modules/expo-manifests": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-1.0.10.tgz",
+ "integrity": "sha512-oxDUnURPcL4ZsOBY6X1DGWGuoZgVAFzp6PISWV7lPP2J0r8u1/ucuChBgpK7u1eLGFp6sDIPwXyEUCkI386XSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@expo/config": "~12.0.11",
+ "expo-json-utils": "~0.15.0"
+ },
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo-modules-autolinking": {
"version": "3.0.24",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.24.tgz",
@@ -4413,6 +4514,15 @@
"react-native": "*"
}
},
+ "node_modules/expo-updates-interface": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-2.0.0.tgz",
+ "integrity": "sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "expo": "*"
+ }
+ },
"node_modules/expo/node_modules/@babel/code-frame": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
@@ -4828,12 +4938,34 @@
"integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==",
"license": "Apache-2.0"
},
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"license": "MIT"
},
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
"node_modules/fb-watchman": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
@@ -5296,6 +5428,15 @@
"node": ">=0.12.0"
}
},
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@@ -5797,6 +5938,12 @@
"node": ">=6"
}
},
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@@ -6194,6 +6341,18 @@
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
"license": "MIT"
},
+ "node_modules/merge-options": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
+ "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -7311,6 +7470,32 @@
"react-native": "*"
}
},
+ "node_modules/react-native-webview": {
+ "version": "13.16.0",
+ "resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.16.0.tgz",
+ "integrity": "sha512-Nh13xKZWW35C0dbOskD7OX01nQQavOzHbCw9XoZmar4eXCo7AvrYJ0jlUfRVVIJzqINxHlpECYLdmAdFsl9xDA==",
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0",
+ "invariant": "2.2.4"
+ },
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
+ "node_modules/react-native-webview/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/react-native/node_modules/@react-native/virtualized-lists": {
"version": "0.81.5",
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz",
diff --git a/package.json b/package.json
index 59deb28..adf6a20 100644
--- a/package.json
+++ b/package.json
@@ -4,15 +4,18 @@
"main": "index.ts",
"scripts": {
"start": "expo start",
- "android": "expo start --android",
- "ios": "expo start --ios",
+ "android": "expo run:android",
+ "ios": "expo run:ios",
"web": "expo start --web"
},
"dependencies": {
+ "@react-native-async-storage/async-storage": "^2.2.0",
"expo": "~54.0.33",
+ "expo-dev-client": "~6.0.20",
"expo-status-bar": "~3.0.9",
"react": "19.1.0",
- "react-native": "0.81.5"
+ "react-native": "0.81.5",
+ "react-native-webview": "^13.16.0"
},
"devDependencies": {
"@types/react": "~19.1.0",