sync
This commit is contained in:
parent
3cfc3c5792
commit
b25e13160e
|
|
@ -3,7 +3,7 @@ import { Image, StyleSheet, Pressable, Platform, View, Alert, ActivityIndicator,
|
||||||
import { useCameraPermissions } from 'expo-camera';
|
import { useCameraPermissions } from 'expo-camera';
|
||||||
import Link, { } from 'expo-router/link';
|
import Link, { } from 'expo-router/link';
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
|
import { Stack, useNavigation, useRouter } from "expo-router";
|
||||||
import ParallaxScrollView from '@/components/ParallaxScrollView';
|
import ParallaxScrollView from '@/components/ParallaxScrollView';
|
||||||
import { ThemedText } from '@/components/ThemedText';
|
import { ThemedText } from '@/components/ThemedText';
|
||||||
import { ThemedView } from '@/components/ThemedView';
|
import { ThemedView } from '@/components/ThemedView';
|
||||||
|
|
@ -29,7 +29,8 @@ export default function HomeScreen() {
|
||||||
const activeAddress = isConnected ? address : embeddedWallet;
|
const activeAddress = isConnected ? address : embeddedWallet;
|
||||||
const [ethBalance, setEthBalance] = useState(null);
|
const [ethBalance, setEthBalance] = useState(null);
|
||||||
const [usdtBalance, setUsdtBalance] = useState(null);
|
const [usdtBalance, setUsdtBalance] = useState(null);
|
||||||
|
const navigation = useNavigation();
|
||||||
|
const router = useRouter();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchBalances = async () => {
|
const fetchBalances = async () => {
|
||||||
if (activeAddress) {
|
if (activeAddress) {
|
||||||
|
|
@ -215,6 +216,14 @@ export default function HomeScreen() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openScanner = ()=>{
|
||||||
|
router.push({pathname:"/scanner"});
|
||||||
|
}
|
||||||
|
|
||||||
|
const openHistory= ()=>{
|
||||||
|
router.push({pathname:"/transactions"});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ParallaxScrollView
|
<ParallaxScrollView
|
||||||
|
|
@ -274,11 +283,14 @@ export default function HomeScreen() {
|
||||||
|
|
||||||
{/* Link to Scanner */}
|
{/* Link to Scanner */}
|
||||||
{isPermissionGranted ? (isConnected || firebaseUser != null) && (
|
{isPermissionGranted ? (isConnected || firebaseUser != null) && (
|
||||||
<Link href="./scanner" asChild>
|
|
||||||
<Pressable
|
<ThemedView style={{flexDirection:"row"}}>
|
||||||
|
<Pressable
|
||||||
|
onPress={openScanner}
|
||||||
style={({ pressed }) => [
|
style={({ pressed }) => [
|
||||||
styles.scanButton,
|
styles.scanButton,
|
||||||
{ opacity: pressed ? 0.5 : 1 },
|
{ opacity: pressed ? 0.5 : 1 },
|
||||||
|
{backgroundColor:"transparent"}
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
|
|
@ -287,7 +299,21 @@ export default function HomeScreen() {
|
||||||
/>
|
/>
|
||||||
<ThemedText style={styles.scanButtonText}>Scan</ThemedText>
|
<ThemedText style={styles.scanButtonText}>Scan</ThemedText>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</Link>
|
<Pressable
|
||||||
|
onPress={openHistory}
|
||||||
|
style={({ pressed }) => [
|
||||||
|
styles.scanButton,
|
||||||
|
{ opacity: pressed ? 0.5 : 1 },
|
||||||
|
{backgroundColor:"transparent"}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
source={require('@/assets/images/history_icon.png')}
|
||||||
|
style={styles.qrIcon}
|
||||||
|
/>
|
||||||
|
<ThemedText style={styles.scanButtonText}>Transaction History</ThemedText>
|
||||||
|
</Pressable>
|
||||||
|
</ThemedView>
|
||||||
) : (
|
) : (
|
||||||
<Pressable onPress={requestPermission} style={styles.button}>
|
<Pressable onPress={requestPermission} style={styles.button}>
|
||||||
<ThemedText style={styles.buttonText}>Grant Permission</ThemedText>
|
<ThemedText style={styles.buttonText}>Grant Permission</ThemedText>
|
||||||
|
|
@ -460,4 +486,4 @@ const styles = StyleSheet.create({
|
||||||
fontSize: 14, // Smaller font for ETH/USDT labels
|
fontSize: 14, // Smaller font for ETH/USDT labels
|
||||||
color: 'gray',
|
color: 'gray',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -4,22 +4,23 @@ import { CameraView } from "expo-camera";
|
||||||
import { Stack, useNavigation } from "expo-router"; // Updated import for navigation
|
import { Stack, useNavigation } from "expo-router"; // Updated import for navigation
|
||||||
import { Overlay } from "./Overlay";
|
import { Overlay } from "./Overlay";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { CHAIN_ID, CONTRACT_ADDRESS, TOKEN_CONTRACT_ADDRESS } from "./shared";
|
import { CHAIN_ID, CONTRACT_ADDRESS, MerchantData, TOKEN_CONTRACT_ADDRESS } from "./shared";
|
||||||
import { useWalletConnectModal } from "@walletconnect/modal-react-native";
|
import { useWalletConnectModal } from "@walletconnect/modal-react-native";
|
||||||
import { contractABI } from "./ABI/transfer_contract";
|
import { contractABI } from "./ABI/transfer_contract";
|
||||||
import { erc20ABI } from "./ABI/erc20_contract";
|
import { erc20ABI } from "./ABI/erc20_contract";
|
||||||
import { GoogleSignin, User } from "@react-native-google-signin/google-signin";
|
import { GoogleSignin, User } from "@react-native-google-signin/google-signin";
|
||||||
|
import { ThemedText } from "@/components/ThemedText";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const qrLock = useRef(false);
|
const qrLock = useRef(false);
|
||||||
const appState = useRef(AppState.currentState);
|
const appState = useRef(AppState.currentState);
|
||||||
const navigation = useNavigation(); // Hook for navigation
|
const navigation = useNavigation(); // Hook for navigation
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
|
||||||
const [scannedAddress, setScannedAddress] = useState("0xE09865aaCd816729C19E4BeA4caFf247704De9fE");
|
const [scannedAddress, setScannedAddress] = useState("0xE09865aaCd816729C19E4BeA4caFf247704De9fE");
|
||||||
|
const [merchantData, setMerchantData] = useState<MerchantData | null>(null);
|
||||||
const [dollarValue, setDollarValue] = useState("400");
|
const [dollarValue, setDollarValue] = useState("400");
|
||||||
const [firebaseUser, setFirebaseUser] = useState<User | null>();
|
const [firebaseUser, setFirebaseUser] = useState<User | null>();
|
||||||
const [isLoading, setIsLoading]= useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = AppState.addEventListener("change", (nextAppState) => {
|
const subscription = AppState.addEventListener("change", (nextAppState) => {
|
||||||
|
|
@ -44,8 +45,6 @@ export default function Home() {
|
||||||
};
|
};
|
||||||
checkSignInStatus();
|
checkSignInStatus();
|
||||||
|
|
||||||
transferEth();
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
subscription.remove();
|
subscription.remove();
|
||||||
};
|
};
|
||||||
|
|
@ -53,11 +52,11 @@ export default function Home() {
|
||||||
|
|
||||||
const { open, isConnected, address, provider } = useWalletConnectModal();
|
const { open, isConnected, address, provider } = useWalletConnectModal();
|
||||||
|
|
||||||
const isValidEthAddress = (address:string) => /^0x[a-fA-F0-9]{40}$/.test(address);
|
const isValidEthAddress = (address: string) => /^0x[a-fA-F0-9]{40}$/.test(address);
|
||||||
|
|
||||||
const transferEthRemtoe = async () => {
|
const transferEthRemtoe = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const response =await fetch(`http://vps.playpoolstudios.com:30117/sendTransaction?tokenId=${firebaseUser?.idToken}&receiver=${scannedAddress}&amount=${dollarValue}`);
|
const response = await fetch(`http://vps.playpoolstudios.com:30117/sendTransaction?tokenId=${firebaseUser?.idToken}&receiver=${scannedAddress}&amount=${dollarValue}`);
|
||||||
console.log(await response.text());
|
console.log(await response.text());
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
navigation.goBack();
|
navigation.goBack();
|
||||||
|
|
@ -67,7 +66,7 @@ export default function Home() {
|
||||||
if (!provider || !isConnected) {
|
if (!provider || !isConnected) {
|
||||||
console.error("Wallet not connected.");
|
console.error("Wallet not connected.");
|
||||||
|
|
||||||
if(firebaseUser!=null){
|
if (firebaseUser != null) {
|
||||||
transferEthRemtoe();
|
transferEthRemtoe();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
@ -80,7 +79,7 @@ export default function Home() {
|
||||||
console.log(`Switching from ${network.chainId} to ${CHAIN_ID} (Arbitrum Sepolia)...`);
|
console.log(`Switching from ${network.chainId} to ${CHAIN_ID} (Arbitrum Sepolia)...`);
|
||||||
await provider.request({
|
await provider.request({
|
||||||
method: "wallet_switchEthereumChain",
|
method: "wallet_switchEthereumChain",
|
||||||
params: [{ chainId: `0x${CHAIN_ID.toString(16)}`, name:"Arbitrum Sepolia" }],
|
params: [{ chainId: `0x${CHAIN_ID.toString(16)}`, name: "Arbitrum Sepolia" }],
|
||||||
});
|
});
|
||||||
console.log(`Switched to chain ID: ${CHAIN_ID}`);
|
console.log(`Switched to chain ID: ${CHAIN_ID}`);
|
||||||
}
|
}
|
||||||
|
|
@ -90,7 +89,7 @@ export default function Home() {
|
||||||
const price = priceJson['price'];
|
const price = priceJson['price'];
|
||||||
|
|
||||||
// const ethValue = parseFloat(dollarValue) / price; // Convert dollars to ETH assuming 1 ETH = $3600
|
// const ethValue = parseFloat(dollarValue) / price; // Convert dollars to ETH assuming 1 ETH = $3600
|
||||||
const usdtValue = parseFloat(dollarValue)/price; // ex: 10 USDT
|
const usdtValue = parseFloat(dollarValue) / price; // ex: 10 USDT
|
||||||
console.log(`Converted ${dollarValue} THB to ${usdtValue} USDT`);
|
console.log(`Converted ${dollarValue} THB to ${usdtValue} USDT`);
|
||||||
|
|
||||||
console.log("prepping the contract");
|
console.log("prepping the contract");
|
||||||
|
|
@ -125,7 +124,36 @@ export default function Home() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async function validateScannedQR(data: string) {
|
||||||
|
if (data === scannedAddress) {
|
||||||
|
// console.log(`${data} == ${scannedAddress}`);
|
||||||
|
qrLock.current = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setScannedAddress(data);
|
||||||
|
|
||||||
|
const response = await fetch(`https://vps.playpoolstudios.com/ecpay/api/get_qr_by_id.php?id=${data}`);
|
||||||
|
const resText = await response.text();
|
||||||
|
|
||||||
|
qrLock.current = false;
|
||||||
|
|
||||||
|
if (resText == "Invalid ID") {
|
||||||
|
console.log("Invalid QR, DO NOTHING");
|
||||||
|
} else {
|
||||||
|
console.log(resText);
|
||||||
|
|
||||||
|
const resJson = JSON.parse(resText);
|
||||||
|
console.log(resJson);
|
||||||
|
setMerchantData(resJson);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log("Merchant Data updated:", merchantData);
|
||||||
|
}, [merchantData]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={StyleSheet.absoluteFillObject}>
|
<SafeAreaView style={StyleSheet.absoluteFillObject}>
|
||||||
|
|
@ -142,50 +170,62 @@ export default function Home() {
|
||||||
onBarcodeScanned={({ data }) => {
|
onBarcodeScanned={({ data }) => {
|
||||||
if (data && !qrLock.current) {
|
if (data && !qrLock.current) {
|
||||||
qrLock.current = true;
|
qrLock.current = true;
|
||||||
setScannedAddress(data); // Save the scanned address
|
validateScannedQR(data); // Save the scanned address
|
||||||
setModalVisible(true); // Open the popup/modal
|
// setModalVisible(true); // Open the popup/modal
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Overlay />
|
<Overlay />
|
||||||
|
|
||||||
{/* Loading Modal */}
|
{/* Loading Modal */}
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<Modal animationType="fade" transparent={true} visible={isLoading}>
|
<Modal animationType="fade" transparent={true} visible={isLoading}>
|
||||||
<View style={styles.loadingModalContainer}>
|
<View style={styles.loadingModalContainer}>
|
||||||
<View style={styles.loadingContent}>
|
<View style={styles.loadingContent}>
|
||||||
<Text style={styles.loadingText}>Loading...</Text>
|
<Text style={styles.loadingText}>Loading...</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
animationType="slide"
|
animationType="slide"
|
||||||
transparent={true}
|
transparent={true}
|
||||||
visible={modalVisible}
|
visible={merchantData !== null}//make this come up when merchant data is set
|
||||||
onRequestClose={() => setModalVisible(false)} // Close modal on back action
|
onRequestClose={() => { setMerchantData(null); setScannedAddress(""); }} // Close modal on back action
|
||||||
>
|
>
|
||||||
<View style={styles.modalContainer}>
|
<View style={styles.modalContainer}>
|
||||||
<View style={styles.modalContent}>
|
<View style={styles.modalContent}>
|
||||||
|
<Text style={styles.title}>Confirm Payment for {merchantData?.username}</Text>
|
||||||
|
{
|
||||||
|
merchantData?.price == "0" ?
|
||||||
|
<>
|
||||||
<Text style={styles.title}>Enter Amount</Text>
|
<Text style={styles.title}>Enter Amount</Text>
|
||||||
|
|
||||||
<View style={styles.inputContainer}>
|
<View style={styles.inputContainer}>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={styles.input}
|
style={styles.input}
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
keyboardType="numeric"
|
keyboardType="numeric"
|
||||||
value={dollarValue}
|
value={dollarValue}
|
||||||
onChangeText={setDollarValue}
|
onChangeText={setDollarValue}
|
||||||
/>
|
/>
|
||||||
<Text style={styles.dollarSign}> THB</Text>
|
<Text style={styles.dollarSign}> THB</Text>
|
||||||
|
|
||||||
</View>
|
</View>
|
||||||
|
</>
|
||||||
|
:
|
||||||
|
<>
|
||||||
|
<Text style={styles.title}>Amount : {merchantData?.price} THB</Text>
|
||||||
|
|
||||||
<Button title="Transfer ETH" onPress={transferEth} color="#4CAF50" />
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
<Button title="Confirm payment" onPress={transferEth} color="#4CAF50" />
|
||||||
|
|
||||||
<View style={styles.spacer} />
|
<View style={styles.spacer} />
|
||||||
<Button title="Close" onPress={() => setModalVisible(false)} color="#F44336" />
|
<Button title="Close" onPress={() => { setMerchantData(null); setScannedAddress(""); }} color="#F44336" />
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
|
|
@ -3,27 +3,6 @@ export const CONTRACT_ADDRESS = '0x5C38736f3E8d2A4F4Ca742fE0B6c4e22D3dB8917';
|
||||||
export const TOKEN_CONTRACT_ADDRESS = '0x976E7ED682f781F39Fd79BF2966bB54A9CBbDF5F';
|
export const TOKEN_CONTRACT_ADDRESS = '0x976E7ED682f781F39Fd79BF2966bB54A9CBbDF5F';
|
||||||
export const RECEIPENT_ADDRESS = "0xE09865aaCd816729C19E4BeA4caFf247704De9fE";
|
export const RECEIPENT_ADDRESS = "0xE09865aaCd816729C19E4BeA4caFf247704De9fE";
|
||||||
|
|
||||||
//Old contract: 0xa9B7B85cFB7eD8C57b92086C52c63E3c01eac0C0
|
|
||||||
/*
|
|
||||||
inputs: [
|
|
||||||
{
|
|
||||||
internalType: "address",
|
|
||||||
name: "receiver",
|
|
||||||
type: "address",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
*/
|
|
||||||
//New contract: 0xad36caE443aBa3130a5a01F76F19B89b5C8118FA
|
|
||||||
// export const contractABI = [
|
|
||||||
// {
|
|
||||||
// inputs: [],
|
|
||||||
// name: "transferETH",
|
|
||||||
// outputs: [],
|
|
||||||
// stateMutability: "payable",
|
|
||||||
// type: "function"
|
|
||||||
// }
|
|
||||||
// ];
|
|
||||||
export const projectId = 'e3a443e9ea8dbf483ef28f1d4f8393de';
|
export const projectId = 'e3a443e9ea8dbf483ef28f1d4f8393de';
|
||||||
export const providerMetadata = {
|
export const providerMetadata = {
|
||||||
name: 'ECPAY',
|
name: 'ECPAY',
|
||||||
|
|
@ -36,3 +15,11 @@ export const providerMetadata = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export interface MerchantData {
|
||||||
|
id: string;
|
||||||
|
merchant_id: string;
|
||||||
|
price: string;
|
||||||
|
description: string;
|
||||||
|
username:string;
|
||||||
|
}
|
||||||
165
app/(tabs)/transactions.tsx
Normal file
165
app/(tabs)/transactions.tsx
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
FlatList,
|
||||||
|
ActivityIndicator,
|
||||||
|
SafeAreaView,
|
||||||
|
TouchableOpacity,
|
||||||
|
ListRenderItem,
|
||||||
|
Platform,
|
||||||
|
StatusBar,
|
||||||
|
} from 'react-native';
|
||||||
|
import { useNavigation } from 'expo-router';
|
||||||
|
|
||||||
|
interface Transaction {
|
||||||
|
txHash: string;
|
||||||
|
buyer: string;
|
||||||
|
amount: string;
|
||||||
|
receiver: string;
|
||||||
|
mark_price: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransactionsList: React.FC = () => {
|
||||||
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchTransactions = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
'http://vps.playpoolstudios.com:28329/getTransactions/0x4586F1Dd2DeD0045923c9519BBf3b87B599480C2'
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
setTransactions(data.transactions || []);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching transactions:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTransactions();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderTransaction: ListRenderItem<Transaction> = ({ item }) => {
|
||||||
|
// Calculate the amount in million WEI and convert it to Thai Baht
|
||||||
|
const amountInMillion = parseFloat(item.amount) / 1_000_000_000_000_000_000;
|
||||||
|
// Display mark_price as conversion rate in USD
|
||||||
|
const conversionRate = (item.mark_price/ 1_000_000_00).toFixed(2) ;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.card}>
|
||||||
|
<Text style={styles.label}>Transaction Hash:</Text>
|
||||||
|
<Text style={styles.value}>{item.txHash}</Text>
|
||||||
|
<Text style={styles.label}>Amount:</Text>
|
||||||
|
<Text style={styles.value}>
|
||||||
|
{ (parseFloat(conversionRate)*amountInMillion).toFixed(2)} THB ~{amountInMillion.toLocaleString()} USD
|
||||||
|
</Text>
|
||||||
|
<Text style={styles.label}>Receiver:</Text>
|
||||||
|
<Text style={styles.value}>{item.receiver}</Text>
|
||||||
|
<Text style={styles.label}>Conversion Rate:</Text>
|
||||||
|
<Text style={styles.value}>${conversionRate}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBackPress = () => {
|
||||||
|
navigation.goBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<StatusBar
|
||||||
|
barStyle={Platform.OS === 'android' ? 'light-content' : 'dark-content'}
|
||||||
|
backgroundColor="#1e1e1e"
|
||||||
|
/>
|
||||||
|
<View style={styles.header}>
|
||||||
|
<TouchableOpacity style={styles.backButton} onPress={handleBackPress}>
|
||||||
|
<Text style={styles.backButtonText}>←</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Text style={styles.title}>Transaction History</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<ActivityIndicator size="large" color="#bb86fc" style={styles.loader} />
|
||||||
|
) : transactions.length > 0 ? (
|
||||||
|
<FlatList
|
||||||
|
data={transactions}
|
||||||
|
renderItem={renderTransaction}
|
||||||
|
keyExtractor={(item) => item.txHash}
|
||||||
|
contentContainerStyle={styles.listContent}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Text style={styles.noDataText}>No transactions found.</Text>
|
||||||
|
)}
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
padding: 15,
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: '#121212', // Dark background
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: 16,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
backgroundColor: '#1e1e1e',
|
||||||
|
elevation: 4,
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
backButton: {
|
||||||
|
marginRight: 16,
|
||||||
|
},
|
||||||
|
backButtonText: {
|
||||||
|
fontSize: 24,
|
||||||
|
color: '#bb86fc', // Highlight color for back button
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 20,
|
||||||
|
color: '#ffffff', // White text for title
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
listContent: {
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
backgroundColor: '#1e1e1e', // Darker card background
|
||||||
|
borderRadius: 8,
|
||||||
|
padding: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 2 },
|
||||||
|
shadowOpacity: 0.2,
|
||||||
|
shadowRadius: 4,
|
||||||
|
elevation: 4,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: '#bb86fc', // Highlight color for labels
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: '#e0e0e0', // Light text for values
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
|
loader: {
|
||||||
|
marginTop: 50,
|
||||||
|
},
|
||||||
|
noDataText: {
|
||||||
|
fontSize: 18,
|
||||||
|
color: '#888888', // Slightly muted text color for "No data" message
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default TransactionsList;
|
||||||
BIN
assets/images/history_icon.png
Normal file
BIN
assets/images/history_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
41
components/BalancesSection.tsx
Normal file
41
components/BalancesSection.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { StyleSheet, ActivityIndicator } from 'react-native';
|
||||||
|
import { ThemedText } from '@/components/ThemedText';
|
||||||
|
import { ThemedView } from './ThemedView';
|
||||||
|
|
||||||
|
export default function BalancesSection() {
|
||||||
|
const [ethBalance, setEthBalance] = useState(null);
|
||||||
|
const [usdtBalance, setUsdtBalance] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchBalances = async () => {
|
||||||
|
try {
|
||||||
|
const [eth, usdt] = await Promise.all([
|
||||||
|
fetch('/api/ethBalance').then(res => res.json()),
|
||||||
|
fetch('/api/usdtBalance').then(res => res.json()),
|
||||||
|
]);
|
||||||
|
setEthBalance(eth.balance);
|
||||||
|
setUsdtBalance(usdt.balance);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch balances:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchBalances();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemedView>
|
||||||
|
{ethBalance && usdtBalance ? (
|
||||||
|
<>
|
||||||
|
<ThemedText>ETH: {ethBalance}</ThemedText>
|
||||||
|
<ThemedText>USDT: {usdtBalance}</ThemedText>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<ActivityIndicator />
|
||||||
|
)}
|
||||||
|
</ThemedView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({});
|
||||||
58
components/GoogleSignInSection.tsx
Normal file
58
components/GoogleSignInSection.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Pressable, StyleSheet, ActivityIndicator, Alert } from 'react-native';
|
||||||
|
import { ThemedText } from '@/components/ThemedText';
|
||||||
|
import { GoogleSignin, GoogleSigninButton, statusCodes, User } from '@react-native-google-signin/google-signin';
|
||||||
|
import auth from '@react-native-firebase/auth';
|
||||||
|
|
||||||
|
export default function GoogleSignInSection() {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [firebaseUser, setFirebaseUser] = useState<User | null>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
GoogleSignin.configure({ webClientId: 'YOUR_WEB_CLIENT_ID' });
|
||||||
|
|
||||||
|
const checkSignInStatus = async () => {
|
||||||
|
try {
|
||||||
|
const userInfo = await GoogleSignin.signInSilently();
|
||||||
|
if (userInfo) setFirebaseUser(userInfo.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Google Sign-In check failed', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
checkSignInStatus();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const signIn = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const userInfo = await GoogleSignin.signIn();
|
||||||
|
setFirebaseUser(userInfo.data);
|
||||||
|
} catch (error:any) {
|
||||||
|
if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
|
||||||
|
Alert.alert('Error', 'Google Play Services unavailable.');
|
||||||
|
} else {
|
||||||
|
Alert.alert('Error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{firebaseUser ? (
|
||||||
|
<Pressable onPress={() => GoogleSignin.signOut()} style={styles.button}>
|
||||||
|
<ThemedText style={styles.text}>Logout</ThemedText>
|
||||||
|
</Pressable>
|
||||||
|
) : (
|
||||||
|
<GoogleSigninButton onPress={signIn} style={styles.googleButton} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: { backgroundColor: '#007bff', padding: 12, borderRadius: 8 },
|
||||||
|
text: { color: '#fff', textAlign: 'center' },
|
||||||
|
googleButton: { width: '100%', height: 48 },
|
||||||
|
});
|
||||||
36
components/ScannerSection.tsx
Normal file
36
components/ScannerSection.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Pressable, StyleSheet, Image } from 'react-native';
|
||||||
|
import { useCameraPermissions } from 'expo-camera';
|
||||||
|
import { ThemedText} from '@/components/ThemedText';
|
||||||
|
import Link from 'expo-router/link';
|
||||||
|
|
||||||
|
export default function ScannerSection (){
|
||||||
|
const [permission, requestPermission] = useCameraPermissions();
|
||||||
|
const isPermissionGranted = Boolean(permission?.granted);
|
||||||
|
|
||||||
|
return (
|
||||||
|
isPermissionGranted ? (
|
||||||
|
<Link href="./scanner" asChild>
|
||||||
|
<Pressable style={styles.scanButton}>
|
||||||
|
<Image
|
||||||
|
source={require('@/assets/images/qr-code-icon.png')}
|
||||||
|
style={styles.qrIcon}
|
||||||
|
/>
|
||||||
|
<ThemedText style={styles.scanButtonText}>Scan</ThemedText>
|
||||||
|
</Pressable>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Pressable onPress={requestPermission} style={styles.grantButton}>
|
||||||
|
<ThemedText style={styles.grantText}>Grant Permission</ThemedText>
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
scanButton: { padding: 12, borderRadius: 8, backgroundColor: '#007bff', alignItems: 'center' },
|
||||||
|
qrIcon: { width: 40, height: 40 },
|
||||||
|
scanButtonText: { color: '#fff', fontWeight: 'bold' },
|
||||||
|
grantButton: { padding: 12, borderRadius: 8, backgroundColor: '#007bff', alignItems: 'center' },
|
||||||
|
grantText: { color: '#fff', fontWeight: 'bold' },
|
||||||
|
});
|
||||||
41
components/WalletConnectSection.tsx
Normal file
41
components/WalletConnectSection.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Pressable, StyleSheet, ActivityIndicator } from 'react-native';
|
||||||
|
import { ThemedText } from '@/components/ThemedText';
|
||||||
|
import { useWalletConnectModal } from '@walletconnect/modal-react-native';
|
||||||
|
import { CHAIN_ID, CONTRACT_ADDRESS, providerMetadata } from '@/constants/Web3';
|
||||||
|
|
||||||
|
export default function WalletConnectSection() {
|
||||||
|
const { open, isConnected, provider } = useWalletConnectModal();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const connectWallet = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
if (isConnected) await provider?.disconnect();
|
||||||
|
else await open();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Wallet connection failed:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable onPress={connectWallet} style={styles.button}>
|
||||||
|
<ThemedText style={styles.text}>
|
||||||
|
{loading ? <ActivityIndicator color="#fff" /> : isConnected ? 'Disconnect' : 'Connect Wallet'}
|
||||||
|
</ThemedText>
|
||||||
|
</Pressable>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
backgroundColor: '#007bff',
|
||||||
|
padding: 12,
|
||||||
|
borderRadius: 8,
|
||||||
|
alignItems: 'center',
|
||||||
|
marginVertical: 8,
|
||||||
|
},
|
||||||
|
text: { color: '#fff' },
|
||||||
|
});
|
||||||
3
constants/Web3.ts
Normal file
3
constants/Web3.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const CHAIN_ID = "";
|
||||||
|
export const CONTRACT_ADDRESS= "";
|
||||||
|
export const providerMetadata= "";
|
||||||
Loading…
Reference in New Issue
Block a user