Skip to content

Commit b6dcad0

Browse files
Add Market Screens (#931)
* WIP: implementing market screen * WIP: handling fetching price * WIP: Implement pages && connect with BE * WIP: update new style of selecting wallet * WIP: handle market stats * WIP: fix bug in overview screen * WIP: implement order screens * WIP: handld orders screen * WIP: working on order cards * WIP: fix cards * WIP: fix bug * remove wallets provides && add notifier * add market in layout_drawer * fix bug of updating order * WIP: updating with changes in dart client * WIP: update changes from the dart client * fix navigation * WIP: fixing bugs && testing the whole flow * add more validations * fix orders history * fix workflow * remove duplicated import * improve code && remove unnecessary logic * apply pr comments * Update app/lib/apps/market/market.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * Update app/lib/widgets/market/buy_tft.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * Update app/lib/widgets/market/overview.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * Update app/lib/widgets/market/overview.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * Update app/lib/widgets/market/order_card.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * Update app/lib/widgets/market/order_details.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * Update app/lib/widgets/market/order_details.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * WIP: apply pr comments * WIP: apply pr comments * move order notifier to providers directory * fix conflict * Update app/lib/widgets/market/buy_tft.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * Update app/lib/widgets/market/buy_tft.dart Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com> * WIP: apply pr comments * WIP: apply pr comments * handle available balance with usdc * add check on low xlm balance when catching error * fix error message * update pushReplacement in creating order && add cancel button * WIP: apply pr comments * fix failed workflow * apply pr comments * run format && analyze * add intl as direct dependency to fix workflow * add reflectable in pubspec yaml * fix pr comments * remove unused var * update version && remove unused var * remove missed e * update text styling of buttons from titleLarge to medium (#1022) * update font tp be consistent with app * undo changes from pubspec.yaml --------- Co-authored-by: AhmedHanafy725 <41957921+AhmedHanafy725@users.noreply.github.com>
1 parent 66a3f08 commit b6dcad0

34 files changed

Lines changed: 3360 additions & 83 deletions

.github/workflows/format_analyze.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jobs:
2121
- name: Set up Flutter
2222
uses: subosito/flutter-action@v2
2323
with:
24+
flutter-version: "3.27.2"
2425
channel: 'stable'
2526

2627
- name: Get dependencies

app/assets/usdc-icon.png

4.27 KB
Loading

app/lib/apps/market/market.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:flutter/widgets.dart';
2+
import 'package:threebotlogin/app.dart';
3+
import 'package:threebotlogin/apps/farmers/farmers_user_data.dart';
4+
import 'package:threebotlogin/events/events.dart';
5+
import 'package:threebotlogin/events/go_home_event.dart';
6+
import 'package:threebotlogin/screens/market/market_screen.dart';
7+
8+
class Market implements App {
9+
static final Market _singleton = Market._internal();
10+
static const Widget _daoWidget = MarketPage();
11+
12+
factory Market() {
13+
return _singleton;
14+
}
15+
16+
Market._internal();
17+
18+
@override
19+
Future<Widget> widget() async {
20+
return _daoWidget;
21+
}
22+
23+
@override
24+
void clearData() {
25+
clearAllData();
26+
}
27+
28+
@override
29+
bool emailVerificationRequired() {
30+
return false;
31+
}
32+
33+
@override
34+
bool pinRequired() {
35+
return true;
36+
}
37+
38+
@override
39+
void back() {
40+
Events().emit(GoHomeEvent());
41+
}
42+
}

app/lib/jrouter.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:threebotlogin/app.dart';
33
import 'package:threebotlogin/apps/council/council.dart';
44
import 'package:threebotlogin/apps/dao/dao.dart';
5+
import 'package:threebotlogin/apps/market/market.dart';
56
import 'package:threebotlogin/apps/wallet/wallet.dart';
67
import 'package:threebotlogin/screens/identity_verification_screen.dart';
78
import 'package:threebotlogin/screens/preference_screen.dart';
@@ -71,6 +72,15 @@ class JRouter {
7172
view: const IdentityVerificationScreen(),
7273
),
7374
app: null),
75+
AppInfo(
76+
route: Route(
77+
path: '/market',
78+
name: 'Market',
79+
icon: Icons.show_chart_sharp,
80+
view: await Market().widget(),
81+
),
82+
app: Market(),
83+
),
7484
AppInfo(
7585
route: Route(
7686
path: '/settings',

app/lib/models/market_data.dart

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
class TftMarketData {
2+
final double lastPrice;
3+
final double lastUsdPrice;
4+
final double change24h;
5+
final double high24h;
6+
final double low24h;
7+
final double volume24h;
8+
9+
TftMarketData({
10+
required this.lastPrice,
11+
required this.lastUsdPrice,
12+
required this.change24h,
13+
required this.high24h,
14+
required this.low24h,
15+
required this.volume24h,
16+
});
17+
18+
factory TftMarketData.fromTrades(List<dynamic> trades) {
19+
if (trades.isEmpty) return TftMarketData.empty();
20+
21+
final latestTrade = trades.first;
22+
final double lastUsdcPrice = double.parse(latestTrade['base_amount']) /
23+
double.parse(latestTrade['counter_amount']);
24+
final double lastPrice = 1 / lastUsdcPrice;
25+
26+
double high24h = lastUsdcPrice;
27+
double low24h = lastUsdcPrice;
28+
double volume24h = 0;
29+
30+
final oldestTrade = trades.last;
31+
final double oldestUsdcPrice = double.parse(oldestTrade['base_amount']) /
32+
double.parse(oldestTrade['counter_amount']);
33+
34+
for (var trade in trades) {
35+
double usdcPrice = double.parse(trade['base_amount']) /
36+
double.parse(trade['counter_amount']);
37+
high24h = usdcPrice > high24h ? usdcPrice : high24h;
38+
low24h = usdcPrice < low24h ? usdcPrice : low24h;
39+
volume24h += double.parse(trade['counter_amount']);
40+
}
41+
42+
final double change24h =
43+
((lastUsdcPrice - oldestUsdcPrice) / oldestUsdcPrice) * 100;
44+
45+
return TftMarketData(
46+
lastPrice: 1 / lastPrice,
47+
lastUsdPrice: lastUsdcPrice,
48+
change24h: change24h,
49+
high24h: high24h,
50+
low24h: low24h,
51+
volume24h: volume24h / 1000,
52+
);
53+
}
54+
55+
factory TftMarketData.empty() {
56+
return TftMarketData(
57+
lastPrice: 0,
58+
lastUsdPrice: 0,
59+
change24h: 0,
60+
high24h: 0,
61+
low24h: 0,
62+
volume24h: 0,
63+
);
64+
}
65+
}

app/lib/models/offer.dart

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import 'package:stellar_flutter_sdk/stellar_flutter_sdk.dart';
2+
import 'package:threebotlogin/services/stellar_service.dart';
3+
4+
class Offer {
5+
final String id;
6+
final String seller;
7+
final String sellingAsset;
8+
final String buyingAsset;
9+
final String amount;
10+
final String price;
11+
final String lastModifiedTime;
12+
13+
Offer({
14+
required this.id,
15+
required this.seller,
16+
required this.sellingAsset,
17+
required this.buyingAsset,
18+
required this.amount,
19+
required this.price,
20+
required this.lastModifiedTime,
21+
});
22+
23+
factory Offer.fromOfferResponse(OfferResponse response) {
24+
return Offer(
25+
id: response.id,
26+
seller: response.seller,
27+
sellingAsset: getAssetName(response.selling),
28+
buyingAsset: getAssetName(response.buying),
29+
amount: response.amount,
30+
price: response.price,
31+
lastModifiedTime: response.lastModifiedTime,
32+
);
33+
}
34+
35+
factory Offer.fromTradeResponse(TradeResponse response) {
36+
final bool userIsSeller =
37+
response.baseAccount == response.links.base.href.split('/').last;
38+
final String sellingAsset = userIsSeller
39+
? _getAssetNameFromTrade(response.baseAssetType, response.baseAssetCode,
40+
response.baseAssetIssuer)
41+
: _getAssetNameFromTrade(response.counterAssetType,
42+
response.counterAssetCode, response.counterAssetIssuer);
43+
44+
final String buyingAsset = userIsSeller
45+
? _getAssetNameFromTrade(response.counterAssetType,
46+
response.counterAssetCode, response.counterAssetIssuer)
47+
: _getAssetNameFromTrade(response.baseAssetType, response.baseAssetCode,
48+
response.baseAssetIssuer);
49+
final String amount =
50+
userIsSeller ? response.baseAmount : response.counterAmount;
51+
52+
String priceStr;
53+
try {
54+
Price priceObj = response.price;
55+
priceStr = (priceObj.numerator! / priceObj.denominator!).toString();
56+
} catch (e) {
57+
priceStr = '0';
58+
}
59+
60+
return Offer(
61+
id: response.id,
62+
seller: userIsSeller ? response.baseAccount! : response.counterAccount!,
63+
sellingAsset: sellingAsset,
64+
buyingAsset: buyingAsset,
65+
amount: amount,
66+
price: priceStr,
67+
lastModifiedTime: response.ledgerCloseTime,
68+
);
69+
}
70+
71+
static String _getAssetNameFromTrade(
72+
String type, String? code, String? issuer) {
73+
if (type == 'native') {
74+
return 'XLM';
75+
} else if (code != null && issuer != null) {
76+
return code;
77+
}
78+
return 'Unknown Asset';
79+
}
80+
}

app/lib/models/order_book.dart

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
class OrderBook {
2+
final String base;
3+
final String counter;
4+
final List<OrderOffer> bids;
5+
final List<OrderOffer> asks;
6+
7+
OrderBook({
8+
required this.base,
9+
required this.counter,
10+
required this.bids,
11+
required this.asks,
12+
});
13+
14+
factory OrderBook.fromJson(Map<String, dynamic> json) {
15+
return OrderBook(
16+
base: json['base'],
17+
counter: json['counter'],
18+
bids: (json['bids'] as List<dynamic>)
19+
.map((e) => OrderOffer.fromJson(e))
20+
.toList(),
21+
asks: (json['asks'] as List<dynamic>)
22+
.map((e) => OrderOffer.fromJson(e))
23+
.toList(),
24+
);
25+
}
26+
27+
Map<String, dynamic> toJson() {
28+
return {
29+
'base': base,
30+
'counter': counter,
31+
'bids': bids.map((e) => e.toJson()).toList(),
32+
'asks': asks.map((e) => e.toJson()).toList(),
33+
};
34+
}
35+
}
36+
37+
class OrderOffer {
38+
final String amount;
39+
final String price;
40+
final PriceR priceR;
41+
42+
OrderOffer({
43+
required this.amount,
44+
required this.price,
45+
required this.priceR,
46+
});
47+
48+
factory OrderOffer.fromJson(Map<String, dynamic> json) {
49+
return OrderOffer(
50+
amount: json['amount'],
51+
price: json['price'],
52+
priceR: PriceR.fromJson(json['price_r']),
53+
);
54+
}
55+
56+
Map<String, dynamic> toJson() {
57+
return {
58+
'amount': amount,
59+
'price': price,
60+
'price_r': priceR.toJson(),
61+
};
62+
}
63+
}
64+
65+
class PriceR {
66+
final int numerator;
67+
final int denominator;
68+
69+
PriceR({
70+
required this.numerator,
71+
required this.denominator,
72+
});
73+
74+
factory PriceR.fromJson(Map<String, dynamic> json) {
75+
return PriceR(
76+
numerator: json['n'],
77+
denominator: json['d'],
78+
);
79+
}
80+
81+
Map<String, dynamic> toJson() {
82+
return {
83+
'n': numerator,
84+
'd': denominator,
85+
};
86+
}
87+
}

app/lib/models/wallet.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class Wallet {
1111
required this.name,
1212
required this.stellarSecret,
1313
required this.stellarAddress,
14-
required this.stellarBalance,
14+
required this.stellarBalances,
1515
required this.tfchainSecret,
1616
required this.tfchainAddress,
1717
required this.tfchainBalance,
@@ -23,7 +23,7 @@ class Wallet {
2323
final String stellarAddress;
2424
final String tfchainSecret;
2525
final String tfchainAddress;
26-
String stellarBalance;
26+
final Map<String, String> stellarBalances;
2727
String tfchainBalance;
2828
final WalletType type;
2929
VerificationState verificationStatus;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import 'package:flutter/material.dart';
2+
3+
class OrderNotifier {
4+
static final ValueNotifier<bool> orderUpdated = ValueNotifier(false);
5+
6+
static void emitUpdate() {
7+
orderUpdated.value = !orderUpdated.value;
8+
}
9+
}

app/lib/providers/wallets_provider.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,11 @@ class WalletsNotifier extends StateNotifier<List<Wallet>> {
9292
final tfchainBalance =
9393
balance.toString() == '0.0' ? '0' : balance.toString();
9494
final stellarBalance =
95-
await StellarService.getBalance(wallet.stellarSecret);
95+
await StellarService.getTFTBalance(wallet.stellarSecret);
9696

9797
if (tfchainBalance != wallet.tfchainBalance ||
98-
stellarBalance != wallet.stellarBalance) {
99-
wallet.stellarBalance = stellarBalance;
98+
stellarBalance != wallet.stellarBalances['TFT']) {
99+
wallet.stellarBalances['TFT'] = stellarBalance;
100100
wallet.tfchainBalance = tfchainBalance;
101101
}
102102
}

0 commit comments

Comments
 (0)