Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to change backend URL on the setting page #54

Merged
merged 25 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from 24 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
12 changes: 7 additions & 5 deletions lib/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import 'dart:convert';
import 'dart:io';

class Client {
static final protocol = 'https';
static final domain = 'api.anypayx.com';
static final host = "$protocol://$domain";
static Uri apiUri = Uri(scheme: 'https', path: 'api.anypayx.com');

static String humanize(String str) {
return StringUtils.capitalize(str);
Expand Down Expand Up @@ -132,7 +130,7 @@ class Client {

var response = await makeRequest('get',
unauthorized: (() => Authentication.logout()),
uri: Uri.https(domain, '/invoices', {
uri: Uri.https(apiUri.path, '/invoices', {
'limit': perPage.toString(),
'offset': offset.toString(),
'complete': 'true',
Expand Down Expand Up @@ -192,7 +190,7 @@ class Client {

static Future<Map<dynamic, dynamic>> makeRequest(method, {path, uri, headers, body, requireAuth, basicAuth, unauthorized, genericErrorCodes}) async {
try {
http.Request request = http.Request(method, uri ?? Uri.parse('$host$path'));
http.Request request = http.Request(method, uri ?? Uri.parse('${apiUri.toString()}$path'));
if (requireAuth ?? false) request.headers['authorization'] = buildAuthHeader();
if (basicAuth != null) request.headers['authorization'] = basicAuth;
if (genericErrorCodes == null) genericErrorCodes = [500];
Expand Down Expand Up @@ -238,4 +236,8 @@ class Client {
};
}
}

static updateUri({required Uri uri}) {
apiUri = uri;
}
}
14 changes: 13 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:app/app_controller.dart';
import 'package:app/router.dart';
import 'client.dart';
import 'native_storage.dart';

void main() {
WidgetsFlutterBinding.ensureInitialized();
setDefaultUrl();
Authentication.checkForAuth().then((isAuthenticated) {
AnyFluroRouter.setupRouter();
runApp(Anypay(isAuthenticated));
Expand Down Expand Up @@ -39,14 +42,16 @@ class Anypay extends StatelessWidget {
),
fontFamily: 'Ubuntu',
);

var darkTheme = ThemeData(
primaryColorDark: Color(0xffCCCCCC),
primaryColorLight: Color(0xFFFFFFFF),
textTheme: TextTheme(
bodyMedium: TextStyle(color: Color(0xFFFFFFFF)),
bodyLarge: TextStyle(color: Color(0xFFFFFFFF)),
),
dialogTheme: DialogTheme(
titleTextStyle: TextStyle(color: Color(0xFFFFFFFF),fontSize: 24),
),
inputDecorationTheme: const InputDecorationTheme(
labelStyle: TextStyle(color: Color(0xFFFFFFFF)),
enabledBorder:
Expand Down Expand Up @@ -87,3 +92,10 @@ class Anypay extends StatelessWidget {
});
}
}

void setDefaultUrl() async {
final storedUrl = await Storage.read("backend_url");
if (storedUrl != null) {
Client.updateUri(uri: Uri.parse(storedUrl));
}
}
4 changes: 2 additions & 2 deletions lib/models/invoice.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class Invoice {

String urlStyleUri([useCurrency]) {
useCurrency = useCurrency ?? currency;
String host = Client.host;
String host = Client.apiUri.toString();
String protocol = {
'BTC': 'bitcoin',
'BCH': 'bitcoincash',
Expand All @@ -143,7 +143,7 @@ class Invoice {
}

String uriFor(currency, {format}) {
if (format == 'pay') return "pay:?r=${Client.host}/r/$uid";
if (format == 'pay') return "pay:?r=${Client.apiUri.toString()}/r/$uid";
if (format == 'url') return urlStyleUri(currency);

return paymentOptionFor(currency)['uri'];
Expand Down
6 changes: 6 additions & 0 deletions lib/router.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:app/routes/edit_backend_url.dart';
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';

Expand Down Expand Up @@ -84,6 +85,11 @@ class AnyFluroRouter {
handler: newHandler(() => SetCurrency(), []),
transitionType: TransitionType.inFromBottom,
);
router.define(
'settings/backend_url',
handler: newHandler(() => EditBackEndUrl(), []),
transitionType: TransitionType.inFromBottom,
);
router.define(
'settings/addresses',
handler: newHandler(() => Addresses(), []),
Expand Down
145 changes: 145 additions & 0 deletions lib/routes/edit_backend_url.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import 'package:app/authentication.dart';
import 'package:flutter/material.dart';
import 'package:app/back_button.dart';
import 'package:app/app_controller.dart';
import 'package:app/currencies.dart';
import '../client.dart';
import '../native_storage.dart';

class EditBackEndUrl extends StatelessWidget {
@override
Widget build(BuildContext context) {
return EditBackEndUrlPage(title: "Edit Backend Url");
}
}

class EditBackEndUrlPage extends StatefulWidget {
EditBackEndUrlPage({Key? key, required this.title}) : super(key: key);

final String title;

@override
_EditBackEndUrlState createState() => _EditBackEndUrlState();
}

class _EditBackEndUrlState extends State<EditBackEndUrlPage> {
var urlController = TextEditingController();

GlobalKey<FormState> _formKey = GlobalKey();


@override
void initState() {
super.initState();
setBackendUrl();
}

void setBackendUrl() {
urlController.text = Client.apiUri.toString();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 300,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_EditUrlLink(),
CircleBackButton(
margin: EdgeInsets.only(top: 20.0),
backPath: 'navigation',
),
],
)),
],
),
),
),
);
}

Widget _EditUrlLink() {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: urlController,
decoration: InputDecoration(
labelText: 'Backend Url',
hintText: "http:// or https://"),
validator: (value) {
if (value != null && Uri.parse(value).isAbsolute) {
return null;
} else {
return "Please provide valid url";
}
}),
Container(
margin: EdgeInsets.only(top: 40.0),
child: GestureDetector(
child: Text('SAVE', style: TextStyle(
fontWeight: FontWeight.bold,
color: AppController.blue,
fontSize: 18,
)),
onTap: () async {
if (_formKey.currentState!.validate()) {
showAlertDialog(
context: context,
title: "Confirmation",
desc: "Are you sure you want to change the backend API url?",
onOkPressed: () async {
await Storage.write(
"backend_url", urlController.text);
Client.updateUri(
uri: Uri.parse(urlController.text));
Authentication.logout();
});
}
},
),
),
],
),
);
}
showAlertDialog(
{required BuildContext context,
required String title,
required String desc,
required onOkPressed}) {
Widget okButton = TextButton(
child: Text("OK"),
onPressed: onOkPressed,
);
Widget cancelButton = TextButton(
child: Text("Cancel"),
onPressed: () {
Navigator.pop(context);
},
);
AlertDialog alert = AlertDialog(
title: Text(title),
content: Text(desc),
actions: [
cancelButton,
okButton,
],
);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
}
29 changes: 27 additions & 2 deletions lib/routes/login.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:convert';

import 'package:email_validator/email_validator.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:app/app_controller.dart';
import 'package:flutter/material.dart';
Expand Down Expand Up @@ -68,6 +69,13 @@ class _LoginPageState extends State<LoginPage> {
});
Client.authenticate(email.text, password.text).then((response) {
_submitting = false;
if (response['body'] == null || response['body'].isEmpty) {
setState(() {
_errorMessage =
"An unknown error occured, try changing the backend url.";
});
return;
}
if (response['success']) {
AppController.closeUntilPath('/new-invoice');
}
Expand Down Expand Up @@ -141,7 +149,7 @@ class _LoginPageState extends State<LoginPage> {
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: _submitting ? 20.0 : 40.0),
margin: EdgeInsets.only(bottom: 20.0),
child: _submitting ?
SpinKitCircle(color: AppController.blue) :
GestureDetector(
Expand Down Expand Up @@ -183,9 +191,26 @@ class _LoginPageState extends State<LoginPage> {
]
),
),
Container(
margin: EdgeInsets.only(top: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: Text('Settings', style: TextStyle(
fontWeight: FontWeight.bold,
color: AppController.blue,
fontSize: 18,
)),
onTap: () {
Navigator.pushNamed(context, 'settings');
}
),
]
),
),
],
),
);
}

}
43 changes: 36 additions & 7 deletions lib/routes/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
import 'package:app/back_button.dart';
import 'package:app/app_controller.dart';
import 'package:app/currencies.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../client.dart';

class Settings extends StatelessWidget {
@override
Expand All @@ -24,14 +26,17 @@ class _SettingsPageState extends State<SettingsPage> {
var _successMessage = '';
var denomination;
var symbol;

bool? isUserLoggedIn;
@override
void initState() {
_rebuild();
super.initState();
Authentication.getAccount().then((account) {
_rebuild();
});
isUserLoggedIn = Authentication.currentAccount.email != null;
if (isUserLoggedIn == true) {
Authentication.getAccount().then((account) {
_rebuild();
});
}
}

@override
Expand All @@ -51,9 +56,10 @@ class _SettingsPageState extends State<SettingsPage> {
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_SelectCurrencyLink(context),
_BusinessInfoLink(context),
_AddressesLink(context),
_EditUrlLink(context),
if (isUserLoggedIn == true) _SelectCurrencyLink(context),
if (isUserLoggedIn == true) _BusinessInfoLink(context),
if (isUserLoggedIn == true) _AddressesLink(context),
CircleBackButton(
margin: EdgeInsets.only(top: 20.0),
backPath: 'navigation',
Expand All @@ -68,6 +74,29 @@ class _SettingsPageState extends State<SettingsPage> {
);
}

Widget _EditUrlLink(context) {
return Container(
margin: EdgeInsets.all(10.0),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.all(AppController.scale(20.0)),
child: Text("Backend URL",
style: TextStyle(
fontSize: 22,
))),
Icon(Icons.edit),
],
),
onTap: () {
Navigator.pushNamed(context, 'settings/backend_url');
}),
);
}

void _rebuild() {
setState(() {
if (Authentication.currentAccount.denomination != null) {
Expand Down
Loading