Skip to content

Commit

Permalink
minor UI and UX improvements, upgraded dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
aldrinzigmundv committed Jul 8, 2024
1 parent 4298aa2 commit 1675e1d
Show file tree
Hide file tree
Showing 20 changed files with 358 additions and 271 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,20 @@ or download the latest APK from the [Releases Section](https://github.com/aldrin

## What is Third Bank?

A free and open-source experimental non-custodial Bitcoin wallet with a strong emphasis on simplicity and ease of use.
A free and open-source non-custodial Bitcoin wallet with a strong emphasis on simplicity and ease of use.

## Features
* Free, open-source and non-custodial
* Intuitive and easy-to-navigate interface design
* Easily lock the app with your phone's PIN or biometrics during setup
* Effortlessly scan QR codes to send Bitcoin to another address
* Enhanced memory safety by adopting a Rust-based backend (Bitcoin Development Project)

## Inspiration

I developed this app after working for a smart TV manufacturer and receiving a lot of feedback from elderly customers who were having difficulty operating their smart TVs, which in my opinion are really far from being user-friendly. I believe we have transitioned from making technology user-friendly for everyone to focusing on aesthetics, often resulting in designs that appear impressive but can be overwhelming for others. This app is my attempt to create a Bitcoin wallet for those people.

## Warning

Please be aware that this application is still in its early stages of development and there is a possibility of losing all your funds. Therefore, it's important to exercise caution, use the app at your own discretion, and refrain from holding significant amounts of funds within the app due to the said risk.
## Contribute

You can contribute in various ways: by identifying and reporting issues, submitting pull requests on GitHub, or by showing support through a donation using the Bitcoin wallet address provided below.

Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ android {
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 23
targetSdkVersion 33
versionCode 9
versionName "0.8.5"
versionCode 10
versionName "1.0.0"
}

signingConfigs {
Expand Down
27 changes: 17 additions & 10 deletions lib/pages/homepage.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:flutter_mobx/flutter_mobx.dart';

import 'package:thirdbank/services/routing.dart';
import 'package:thirdbank/services/wallet.dart';
import 'package:thirdbank/services/storage.dart';
import 'package:thirdbank/services/formatting.dart';
import 'package:thirdbank/services/routing.dart';

import 'package:flutter_mobx/flutter_mobx.dart';

//The homepage of the app if setup is already done
//Shows user balance and transactions
//Shows user options to refresh wallet balance, send, receive, and view transactions in detail when needed
class HomePage extends StatefulWidget {
const HomePage({super.key, required this.wallet, required this.storage});

Expand All @@ -26,8 +29,10 @@ class _HomePageState extends State<HomePage> {

FormattingProvider format = FormattingProvider();

//Placeholder message if wallet openned don't have any transactions yet
String _transactionAreaMessage = "Your transactions will appear here.";

//Refreshes wallet balance and transactions data
_refreshWallet() async {
try {
await wallet.syncWallet();
Expand All @@ -46,6 +51,7 @@ class _HomePageState extends State<HomePage> {
}
}

//Shows additional information about a transaction in a dialog box with an option to copy transaction ID if user needs to
_showTransactionInfo(int index, int blockHeight, int finalFee) {
showDialog(
context: context,
Expand All @@ -60,7 +66,7 @@ class _HomePageState extends State<HomePage> {
Clipboard.setData(
ClipboardData(text: wallet.transactions[index].txid));
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("Wallet address copied to clipboard."),
content: Text("Transaction ID copied to clipboard."),
duration: Duration(seconds: 2),
));
},
Expand All @@ -78,6 +84,7 @@ class _HomePageState extends State<HomePage> {
);
}

//Shows an error message if refreshing wallet fails
_showFailedRefreshingWalletError() {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content:
Expand Down Expand Up @@ -130,8 +137,8 @@ class _HomePageState extends State<HomePage> {
onPressed: () => _refreshWallet(),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(12.0),
child: Text(
Expand All @@ -147,8 +154,8 @@ class _HomePageState extends State<HomePage> {
goToSendBitcoinPage(context: context, wallet: wallet),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(12.0),
child: Text(
Expand All @@ -164,8 +171,8 @@ class _HomePageState extends State<HomePage> {
goToReceivePage(context: context, wallet: wallet),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(12.0),
child: Text(
Expand Down
14 changes: 8 additions & 6 deletions lib/pages/loadingpage.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:flutter/material.dart';

import 'package:thirdbank/services/routing.dart';
import 'package:thirdbank/services/wallet.dart';
import 'package:thirdbank/services/storage.dart';
import 'package:thirdbank/services/authentication.dart';
import 'package:thirdbank/services/routing.dart';

//Page the user first sees
//Checks how the app should proceed if setup is done and if authentication is required before opening wallet
class LoadingPage extends StatefulWidget {
const LoadingPage({super.key});

Expand All @@ -17,8 +19,10 @@ class _LoadingPageState extends State<LoadingPage> {
final StorageProvider storage = StorageProvider();
final AuthenticationProvider auth = AuthenticationProvider();

//Placeholder for error message if an error occurs while setting up
String _loadingpagetext = "Powered by aldrinzigmund.com";

//Opens storage then checks if setup is done and if authentication is required before opening wallet
void _startup() async {
Future.delayed(const Duration(seconds: 1), () async {
await Future.wait([
Expand All @@ -44,6 +48,7 @@ class _LoadingPageState extends State<LoadingPage> {
});
}

//Starts wallet service if authentication was successful or is not required before sending user to home page if wallet is already setup
void _startWallet() async {
try {
await Future.wait([
Expand All @@ -53,21 +58,18 @@ class _LoadingPageState extends State<LoadingPage> {
wallet.mnemonic = values[0],
wallet.walletAddress = values[1],
});

await wallet.createOrRestoreWallet(mnemonic: wallet.mnemonic);
wallet.mnemonic = "";
await wallet.getBlockchainHeight();
if (mounted) {
goToHomePage(context: context, wallet: wallet, storage: storage);
}
} catch (_) {
setState(() {
_loadingpagetext =
"Something went wrong. Either phone authentication failed or bad internet connection...";
});
_displayError();
}
}

//Displays an error if anything fails
_displayError() {
setState(() {
_loadingpagetext =
Expand Down
13 changes: 8 additions & 5 deletions lib/pages/setup pages/authenticationquestionpage.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'package:flutter/material.dart';

import 'package:thirdbank/services/routing.dart';
import 'package:thirdbank/services/wallet.dart';
import 'package:thirdbank/services/storage.dart';
import 'package:thirdbank/services/authentication.dart';
import 'package:thirdbank/services/routing.dart';

//Page where the user is asked if they want to use their phone's authentication method like PIN, fingerprint, etc.
class AuthenticationQuestionPage extends StatefulWidget {
const AuthenticationQuestionPage(
{super.key, required this.wallet, required this.storage});
Expand All @@ -26,6 +27,7 @@ class _AuthenticationQuestionPageState

final AuthenticationProvider authentication = AuthenticationProvider();

//Sets variables for the app to skip setup and authenticate user first through phone's authentication method next time app is opened
_addLocalAuth() async {
await Future.wait([
Future(() => storage.write(key: "setupdone", value: "true")),
Expand All @@ -36,6 +38,7 @@ class _AuthenticationQuestionPageState
}
}

//Sets variables for the app to skip setup and not authenticate the user through phone's authentication method next time app is opened
_noLocalAuth() async {
await Future.wait([
Future(() => storage.write(key: "setupdone", value: "true")),
Expand Down Expand Up @@ -84,8 +87,8 @@ class _AuthenticationQuestionPageState
onPressed: () => _addLocalAuth(),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(9.0),
child: Text(
Expand All @@ -100,8 +103,8 @@ class _AuthenticationQuestionPageState
onPressed: () => _noLocalAuth(),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(9.0),
child: Text(
Expand Down
17 changes: 12 additions & 5 deletions lib/pages/setup pages/generatemnemonicpage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import 'package:flutter/services.dart';

import 'package:flutter_mobx/flutter_mobx.dart';

import 'package:thirdbank/services/routing.dart';
import 'package:thirdbank/services/storage.dart';
import 'package:thirdbank/services/wallet.dart';
import 'package:thirdbank/services/authentication.dart';
import 'package:thirdbank/services/routing.dart';

//Page where the user is given a mnemonic phrase with a message in red asking the user to save it
//Also a new mnemonic can be generated if user didn't like the last one given
class GenerateMnemonicPage extends StatefulWidget {
const GenerateMnemonicPage(
{super.key, required this.wallet, required this.storage});
Expand All @@ -27,10 +29,12 @@ class _GenerateMnemonicPageState extends State<GenerateMnemonicPage> {

final AuthenticationProvider authentication = AuthenticationProvider();

//Gets the wallet to generate a new mnemonic replacing the last one
_regenarateMnemonic() {
wallet.generateMnemonic();
}

//Writes mnemonic and wallet address to two variables inside secure storage
_proceedWithCreatedMnemonic() async {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text("Creating Wallet..."),
Expand All @@ -50,6 +54,8 @@ class _GenerateMnemonicPageState extends State<GenerateMnemonicPage> {
}
}

//Checks first if phone has an authentication method available for the app to give user an option to use it when openning the app
//Skips asking customer about authentication if nothing was found then sets variable to let the app know to skip setup next time and sends customer to home page
_evaluateNextPage() async {
final localAuthAvailable =
await authentication.checkAuthenticationAvailability();
Expand All @@ -69,6 +75,7 @@ class _GenerateMnemonicPageState extends State<GenerateMnemonicPage> {
}
}

//Shows an error message if any of the functions above fails
_showFailedCreatingWalletError() {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content:
Expand Down Expand Up @@ -154,8 +161,8 @@ class _GenerateMnemonicPageState extends State<GenerateMnemonicPage> {
onPressed: () => _proceedWithCreatedMnemonic(),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(9.0),
child: Text(
Expand All @@ -170,8 +177,8 @@ class _GenerateMnemonicPageState extends State<GenerateMnemonicPage> {
onPressed: () => _regenarateMnemonic(),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(9.0),
child: Text(
Expand Down
13 changes: 8 additions & 5 deletions lib/pages/setup pages/mnemonicquestionpage.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import 'package:flutter/material.dart';

import 'package:thirdbank/services/routing.dart';
import 'package:thirdbank/services/wallet.dart';
import 'package:thirdbank/services/storage.dart';
import 'package:thirdbank/services/routing.dart';

//Page where the customer is asked if customer wants to create a new wallet or recover an existing wallet
class MnemonicQuestionPage extends StatelessWidget {
const MnemonicQuestionPage(
{super.key, required this.wallet, required this.storage});

final WalletProvider wallet;
final StorageProvider storage;

//Sends customer to page where the customer can generate a mnemonic for a new wallet
_goToGenerateMnemonicPage(context) {
goToGenerateMnemonicPage(
context: context, wallet: wallet, storage: storage);
}

//Sends customer to page where the customer can recover an existing wallet through mnemonic that the customer already has
_goToRestoreMnemonicPage(context) {
goToRestoreMnemonicPage(context: context, wallet: wallet, storage: storage);
}
Expand Down Expand Up @@ -49,8 +52,8 @@ class MnemonicQuestionPage extends StatelessWidget {
onPressed: () => _goToGenerateMnemonicPage(context),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(12.0),
child: Text(
Expand All @@ -66,8 +69,8 @@ class MnemonicQuestionPage extends StatelessWidget {
onPressed: () => _goToRestoreMnemonicPage(context),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.yellowAccent),
foregroundColor: MaterialStateProperty.all(Colors.black)),
WidgetStateProperty.all(Colors.yellowAccent),
foregroundColor: WidgetStateProperty.all(Colors.black)),
child: const Padding(
padding: EdgeInsets.all(12.0),
child: Text(
Expand Down
Loading

0 comments on commit 1675e1d

Please sign in to comment.