Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files

# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/
expo-env.d.ts

# Native
.kotlin/
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env*.local

# typescript
*.tsbuildinfo

# generated native folders
/ios
/android


# node
package-lock.json

# test
test/
59 changes: 59 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# React Native Project Setup

Arise Wallet!
A BitDevsUyo community project

## Overview and Setup Guide

This is a mobile application built with React Native
The purpose of the project is to build a user friendly Bitcoin wallet using react native for our community project.
During the development of this app members of the community who can contribute in one way or another to the building of the project are encouraged to do so.

## Setup Instructions

### 1. Prerequisites

Make sure you have the following installed:

- **Node.js**(https://nodejs.org/) (v18+ recommended)
- **Package Manager:** **npm**
- **Development Environment:**
- **Android:** Android Studio, Android SDK, & Java (JDK 17).
- **iOS:** Xcode (macOS only) & CocoaPods.
- **Git**

> _Tip: If you haven't set up your machine for React Native before, follow the [React Native Environment Setup Guide](https://reactnative.dev/docs/environment-setup) and select "React Native CLI Quickstart" -> "Expo" tab._

### 2. Clone the Repository

To start contributing clone the repo to your local
git clone -b reactnative/dev (https://github.com/BitDevsUyo/AriseWallet.git)
cd AriseWallet

### 3. Install Dependencies

npm install

### 4. Generate Native Projects (Required)
You must run this command to generate the android and ios folders. These folders are git-ignored, so you must generate them locally before compiling.

npx expo prebuild

### 5. Compile & Run the App
# For Android
npx expo run:android

# For iOS (Mac only)
npx expo run:ios

## Once the app is compiled and installed on your phone/emulator (Step 4), you generally do not need to recompile unless you install a new native library.

npx expo start
## Guildelines on how to contribute

-Issues will be created so do check the issues tabb for tasks
-Pick a task to work on or create new issue.
-Create a new branch from main
-Submit PR with clear descriptions

Keep code clean, documented and reusable
45 changes: 45 additions & 0 deletions app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"expo": {
"scheme": "AriseWallet",
"name": "AriseWallet",
"slug": "arisewallet",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/bitcoin.png",
"userInterfaceStyle": "dark",
"newArchEnabled": true,
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#FF6B00"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.ohotuowomorgan.bitdevwallet",
"infoPlist": {
"NSFaceIDUsageDescription": "Arise Wallet uses Face ID to securely authenticate and unlock your Bitcoin wallet."
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/bitcoin.png",
"backgroundColor": "#FF6B00"
},
"edgeToEdgeEnabled": true,
"package": "com.ohotuowomorgan.bitdevwallet"
},
"web": {
"favicon": "./assets/favicon.png"
},
"plugins": [
"expo-router"
],
"extra": {
"router": {},
"eas": {
"projectId": "a68d1422-b84d-484d-a55d-2bff5452e22a"
}
},
"owner": "bitdevs-uyo"
}
}
47 changes: 47 additions & 0 deletions app/(create)/_layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { StyleSheet, Text, useColorScheme, View, TouchableOpacity } from 'react-native'
import { Stack, useRouter } from 'expo-router'
import React from 'react'
import { StatusBar } from 'expo-status-bar'
import { colors } from "../../constants/colors"


const _layout = () => {
const colorScheme = useColorScheme()
const theme = colors[colorScheme] ?? colors.light
const router = useRouter();

return (
<Stack screenOptions={{ headerStyle: { backgroundColor: '#0D0D0D' }, headerTintColor: '#666666', headerShadowVisible: false, headerBackTitleVisible: false, }}>

<Stack.Screen name="recovery"
options={{
title: '',
}} />

<Stack.Screen name="confirm"
options={{
title: '',
}}
/>
<Stack.Screen name="nameWallet" options={{ title: '', }} />
<Stack.Screen name="protectWallet" options={{ title: '', }} />
<Stack.Screen name="success" options={{ title: '', }} />
<Stack.Screen name="setPin" options={{ headerShown: false, }} />


</Stack>

)
}

export default _layout

const styles = StyleSheet.create({
next: {
backgroundColor: "#1C1C1C",
paddingHorizontal: 9,
paddingVertical: 4,
borderRadius: 999,
}

})
185 changes: 185 additions & 0 deletions app/(create)/confirm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { StyleSheet, Text, View, TouchableOpacity, } from 'react-native'
import { PrimaryButton } from '../../src/components/PrimaryButton'
import { colors, spacing, radii } from '../../src/theme'
import { Link, useRouter } from 'expo-router';
import React, { useState, useEffect } from 'react'

import { useWalletStore } from '../../src/store/walletStore';
import Toast from 'react-native-toast-message';

const confirm = () => {
const router = useRouter();

const mnemonic = useWalletStore((state) => state.onboarding.mnemonic);

const [step, setStep] = useState(1);
const [targetIndex, setTargetIndex] = useState(0);
const [options, setOptions] = useState([]);
const [selectedWord, setSelectedWord] = useState(null);

const setupQuiz = (indexToAvoid = -1) => {
if (!mnemonic) return;

const wordsArray = mnemonic.split(' ');

let randomTarget;
do {
randomTarget = Math.floor(Math.random() * wordsArray.length);
} while (randomTarget === indexToAvoid);

setTargetIndex(randomTarget);
const correctWord = wordsArray[randomTarget];

let uniqueDecoys = Array.from(new Set(wordsArray.filter(w => w !== correctWord)));

uniqueDecoys = uniqueDecoys.sort(() => 0.5 - Math.random()).slice(0, 4);

const allOptions = [correctWord, ...uniqueDecoys].sort(() => 0.5 - Math.random());
setOptions(allOptions);
setSelectedWord(null);
};

useEffect(() => {

setupQuiz();
}, [mnemonic]);

const getOrdinal = (n) => {
const s = ["th", "st", "nd", "rd"];
const v = n % 100;
return n + (s[(v - 20) % 10] || s[v] || s[0]);
};

const handleContinue = () => {
const wordsArray = mnemonic.split(' ');
const correctWord = wordsArray[targetIndex];

if (selectedWord === correctWord) {
if (step === 1) {
setStep(2);
setupQuiz(targetIndex);
} else {
router.push("/nameWallet");
}
} else {
Toast.show({
type: 'error',
text1: 'Incorrect',
text2: 'That is not the correct word. Please try again.',
})
setSelectedWord(null);
}
};

return (
<View style={styles.container}>

<View style={styles.headerText}>
<Text style={styles.mainText}>Confirm Recovery Phrase</Text>
<Text style={styles.subText}>What was the <Text style={styles.subText}>{getOrdinal(targetIndex + 1)}</Text> word in your recovery phrase?</Text>
</View>

<View style={styles.confirmBox}>
{options.map((word, index) => {
const isSelected = selectedWord === word;
return (
<TouchableOpacity
key={index}
style={[
styles.optionRow,
isSelected && styles.optionRowSelected
]}
onPress={() => setSelectedWord(word)}
activeOpacity={0.7}
>
<Text style={styles.optionText}>{word}</Text>
<View style={[
styles.radioCircle,
isSelected && styles.radioCircleSelected
]}>
{isSelected && <View style={styles.radioDot} />}
</View>
</TouchableOpacity>
)
})}
</View>

<PrimaryButton
title={"Continue"}
disabled={!selectedWord}
onPress={handleContinue}
/>

</View>
)
}

export default confirm

const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: spacing.lg,
backgroundColor: colors.background.default,
// justifyContent: 'center',
// alignItems: 'center',
},
headerText: {
paddingVertical: spacing.xl,
marginBottom: spacing.xl * 2,
gap: spacing.xs
},
mainText: {
color: colors.text.primary,
fontSize: 24,
lineHeight: 24,
fontWeight: "500"
},
subText: {
color: colors.text.midgrey,
fontSize: 16,
lineHeight: 24,
},
confirmBox: {
marginVertical: spacing.md,
marginBottom: spacing.xl * 7,
gap: spacing.sm
},
optionRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: spacing.md,
paddingHorizontal: spacing.lg,
borderRadius: radii.md || 12,
backgroundColor: colors.background.surface,
},
optionRowSelected: {
backgroundColor: colors.background.darkgrey,
},
optionText: {
color: colors.text.primary,
fontSize: 16,
fontWeight: '500',
},
radioCircle: {
height: 22,
width: 22,
borderRadius: 11,
borderWidth: 1,
borderColor: colors.border.default,
alignItems: 'center',
justifyContent: 'center',
},
radioCircleSelected: {
borderColor: colors.accent.primary,
},
radioDot: {
height: 10,
width: 10,
borderRadius: 5,
backgroundColor: colors.accent.primary,
justifyContent: 'center',
alignItems: 'center'
},
})
Loading