Skip to content
Merged
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
2 changes: 1 addition & 1 deletion mobile-app/lib/features/components/card_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class CardInfo extends StatelessWidget {
width: context.isTablet ? 660 : 251,
child: Text(text, style: context.themeText.tag?.copyWith(color: textColor)),
),
if (icon != null) icon!,
?icon,
],
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,33 @@ class KingOfTheShillScreen extends ConsumerStatefulWidget {
}

class _KingOfTheShillScreenState extends ConsumerState<KingOfTheShillScreen> {
final TaskmasterService _taskmasterService = TaskmasterService();
int? _rank;
int? _totalImpressions;

@override
void initState() {
super.initState();
final asyncValue = ref.read(raiderSubmissionsProvider);
if (asyncValue.value is RaiderSubmissionsOk) {
_loadRaidStats((asyncValue.value as RaiderSubmissionsOk).activeRaid.id);
}
}

Future<void> _loadRaidStats(int raidId) async {
try {
final stats = await _taskmasterService.getRaidStats(raidId);
if (mounted) {
setState(() {
_rank = stats.rank;
_totalImpressions = stats.totalImpressions;
});
}
} catch (e) {
debugPrint('Error loading raid stats: $e');
}
}

void _showHowItWorksDialog() {
showDialog(
context: context,
Expand Down Expand Up @@ -114,6 +141,13 @@ class _KingOfTheShillScreenState extends ConsumerState<KingOfTheShillScreen> {
Widget build(BuildContext context) {
final raiderSubmissionsAsync = ref.watch(raiderSubmissionsProvider);

ref.listen(raiderSubmissionsProvider, (prev, next) {
final state = next.value;
if (state is RaiderSubmissionsOk && _rank == null) {
_loadRaidStats(state.activeRaid.id);
}
});

return ScaffoldBase(
appBar: WalletAppBar(
title: 'King of The Shill',
Expand Down Expand Up @@ -189,12 +223,12 @@ class _KingOfTheShillScreenState extends ConsumerState<KingOfTheShillScreen> {
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2),
),
),
error: (_, _) => _buildStatsBox(0, 0),
error: (_, _) => _buildStatsBox(0, _totalImpressions, _rank),
data: (state) {
if (state is RaiderSubmissionsOk) {
return _buildStatsBox(state.submissions.length, 0);
return _buildStatsBox(state.submissions.length, _totalImpressions, _rank);
}
return _buildStatsBox(0, 0);
return _buildStatsBox(0, _totalImpressions, _rank);
},
),
const SizedBox(height: 24),
Expand Down Expand Up @@ -237,7 +271,7 @@ class _KingOfTheShillScreenState extends ConsumerState<KingOfTheShillScreen> {
);
}

Widget _buildStatsBox(int submissions, int verifiedPosts) {
Widget _buildStatsBox(int submissions, int? totalImpressions, int? rank) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
Expand All @@ -251,17 +285,39 @@ class _KingOfTheShillScreenState extends ConsumerState<KingOfTheShillScreen> {
),
child: Column(
children: [
_buildStatRow('Submissions', '$submissions', Colors.white),
_buildStatRow(
'Submissions',
Text(
'$submissions',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontFamily: 'Fira Code',
fontWeight: FontWeight.w400,
),
),
),
const SizedBox(height: 16),
_buildStatRow('Verified posts', verifiedPosts.toString().padLeft(2, '0'), Colors.white),
_buildStatRow('Total impressions', _buildLoadingOrValue(totalImpressions, Colors.white)),
const SizedBox(height: 16),
_buildStatRow('Rank', '#-', context.themeColors.pink),
_buildStatRow('Rank', _buildLoadingOrValue(rank, context.themeColors.pink, isRank: true)),
],
),
);
}

Widget _buildStatRow(String label, String value, Color valueColor) {
Widget _buildLoadingOrValue(int? value, Color color, {bool isRank = false}) {
if (value == null) {
return SizedBox(width: 12, height: 12, child: CircularProgressIndicator(strokeWidth: 2, color: color));
}
final text = isRank ? (value > 0 ? '#$value' : '#-') : '$value';
return Text(
text,
style: TextStyle(color: color, fontSize: 14, fontFamily: 'Fira Code', fontWeight: FontWeight.w400),
);
}

Widget _buildStatRow(String label, Widget valueWidget) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expand All @@ -274,10 +330,7 @@ class _KingOfTheShillScreenState extends ConsumerState<KingOfTheShillScreen> {
fontWeight: FontWeight.w400,
),
),
Text(
value,
style: TextStyle(color: valueColor, fontSize: 14, fontFamily: 'Fira Code', fontWeight: FontWeight.w400),
),
valueWidget,
],
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,32 @@ class ReferralsQuestScreen extends ConsumerStatefulWidget {

class _ReferralsQuestScreenState extends ConsumerState<ReferralsQuestScreen> {
final ReferralService _referralService = ReferralService();
final TaskmasterService _taskmasterService = TaskmasterService();
String? _referralCode;
int? _rank;

Future<void> _loadReferralCode() async {
try {
final myReferralCode = await _referralService.getMyInviteCode();
setState(() {
_referralCode = myReferralCode;
});
_loadRank(myReferralCode);
} catch (e) {
debugPrint('Error loading account data: $e');
debugPrint('Error loading referral code: $e');
}
}

Future<void> _loadRank(String referralCode) async {
try {
final rankData = await _taskmasterService.getReferralRank(referralCode);
if (mounted) {
setState(() {});
setState(() {
_rank = rankData.rank;
});
}
} catch (e) {
debugPrint('Error loading rank: $e');
}
}

Expand Down Expand Up @@ -145,7 +158,7 @@ class _ReferralsQuestScreenState extends ConsumerState<ReferralsQuestScreen> {
@override
Widget build(BuildContext context) {
final statsAsync = ref.watch(accountsStatsProvider);
final referralsCount = statsAsync.value?.referralCount ?? 0;
final referralsCount = statsAsync.value?.referralCount;

return ScaffoldBase(
appBar: WalletAppBar(
Expand Down Expand Up @@ -248,9 +261,12 @@ class _ReferralsQuestScreenState extends ConsumerState<ReferralsQuestScreen> {
),
child: Column(
children: [
_buildStatRow('Referrals', '$referralsCount', Colors.white),
_buildStatRow('Referrals', _buildLoadingOrValue(referralsCount, Colors.white)),
const SizedBox(height: 16),
_buildStatRow('Rank', '#-', context.themeColors.pink),
_buildStatRow(
'Rank',
_buildLoadingOrValue(_rank, context.themeColors.pink, isRank: true),
),
],
),
),
Expand Down Expand Up @@ -349,7 +365,7 @@ class _ReferralsQuestScreenState extends ConsumerState<ReferralsQuestScreen> {
);
}

Widget _buildStatRow(String label, String value, Color valueColor) {
Widget _buildStatRow(String label, Widget valueWidget) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expand All @@ -362,12 +378,20 @@ class _ReferralsQuestScreenState extends ConsumerState<ReferralsQuestScreen> {
fontWeight: FontWeight.w400,
),
),
Text(
value,
textAlign: TextAlign.center,
style: TextStyle(color: valueColor, fontSize: 14, fontFamily: 'Fira Code', fontWeight: FontWeight.w400),
),
valueWidget,
],
);
}

Widget _buildLoadingOrValue(int? value, Color color, {bool isRank = false}) {
if (value == null) {
return SizedBox(width: 12, height: 12, child: CircularProgressIndicator(strokeWidth: 2, color: color));
}
final text = isRank ? (value > 0 ? '#$value' : '#-') : '$value';
return Text(
text,
textAlign: TextAlign.center,
style: TextStyle(color: color, fontSize: 14, fontFamily: 'Fira Code', fontWeight: FontWeight.w400),
);
}
}
2 changes: 1 addition & 1 deletion mobile-app/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: resonance_network_wallet
description: A Flutter wallet for the Quantus blockchain.
publish_to: "none"

version: 1.1.5+66
version: 1.1.5+67

environment:
sdk: ">=3.8.0 <4.0.0"
Expand Down
2 changes: 2 additions & 0 deletions quantus_sdk/lib/quantus_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export 'src/models/transaction_event.dart';
export 'src/models/transaction_state.dart';
export 'src/models/raider_submissions.dart';
export 'src/models/raid_quest.dart';
export 'src/models/referral_rank.dart';
export 'src/models/raid_stats.dart';
// note we have to hide some things here because they're exported by substrate service
// should probably expise all of crypto.dart through substrateservice instead
export 'src/rust/api/crypto.dart' hide crystalAlice, crystalCharlie, crystalBob;
Expand Down
35 changes: 35 additions & 0 deletions quantus_sdk/lib/src/models/raid_stats.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:flutter/foundation.dart';

@immutable
class RaidStats {
final int raidId;
final int rank;
final int totalSubmissions;
final int totalImpressions;
final int totalReplies;
final int totalRetweets;
final int totalLikes;

const RaidStats({
required this.raidId,
required this.rank,
required this.totalSubmissions,
required this.totalImpressions,
required this.totalReplies,
required this.totalRetweets,
required this.totalLikes,
});

factory RaidStats.fromJson(Map<String, dynamic> json) {
final data = json['data'] as Map<String, dynamic>;
return RaidStats(
raidId: data['raid_id'] as int,
rank: data['rank'] as int,
totalSubmissions: data['total_submissions'] as int,
totalImpressions: data['total_impressions'] as int,
totalReplies: data['total_replies'] as int,
totalRetweets: data['total_retweets'] as int,
totalLikes: data['total_likes'] as int,
);
}
}
18 changes: 18 additions & 0 deletions quantus_sdk/lib/src/models/referral_rank.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:flutter/foundation.dart';

@immutable
class ReferralRank {
final int rank;
final int referralsCount;

const ReferralRank({required this.rank, required this.referralsCount});

factory ReferralRank.fromJson(Map<String, dynamic> json) {
final data = json['data'] as List<dynamic>;
if (data.isEmpty) {
return const ReferralRank(rank: 0, referralsCount: 0);
}
final first = data[0] as Map<String, dynamic>;
return ReferralRank(rank: first['rank'] as int, referralsCount: first['address']['referrals_count'] as int);
}
}
52 changes: 26 additions & 26 deletions quantus_sdk/lib/src/services/taskmaster_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -464,29 +464,30 @@ class TaskmasterService {
}
}

Future<AccountAssociations> getAccountAssociations() async {
final activeAccount = await getMainAccount();
print('getAccountAssociations ${activeAccount.accountId}');
final accountAssociationsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/associations');

Future<T> _authenticatedGet<T>(Uri uri, T Function(Map<String, dynamic>) fromJson) async {
try {
final http.Response response = await _authenticatedHttpClient.get(accountAssociationsEndpoint);
final response = await _authenticatedHttpClient.get(uri);

if (response.statusCode != 200) {
throw Exception(
'Account Associations http request failed with status: ${response.statusCode}. Body: ${response.body}',
);
throw Exception('HTTP request failed with status: ${response.statusCode}. Body: ${response.body}');
}

final json = jsonDecode(response.body) as Map<String, dynamic>;
return AccountAssociations.fromJson(json);
return fromJson(json);
} catch (e, stackTrace) {
print('Error fetching miner stats: $e');
print('Error fetching data from $uri: $e');
print(stackTrace);
rethrow;
}
}

Future<AccountAssociations> getAccountAssociations() async {
final activeAccount = await getMainAccount();
print('getAccountAssociations ${activeAccount.accountId}');
final accountAssociationsEndpoint = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/associations');
return _authenticatedGet(accountAssociationsEndpoint, AccountAssociations.fromJson);
}

Future<void> submitAddress() async {
await ensureIsLoggedIn();
}
Expand Down Expand Up @@ -561,24 +562,23 @@ class TaskmasterService {

Future<OptedInPosition> getOptInPosition() async {
final Uri uri = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/my-position');

try {
final http.Response response = await _authenticatedHttpClient.get(uri);

if (response.statusCode != 200) {
throw Exception('HTTP request failed with status: ${response.statusCode}. Body: ${response.body}');
}

final json = jsonDecode(response.body) as Map<String, dynamic>;
return OptedInPosition.fromJson(json);
} catch (e, stackTrace) {
print('Error fetching address stats: $e');
print(stackTrace);
rethrow;
}
return _authenticatedGet(uri, OptedInPosition.fromJson);
}

Future<void> logout() async {
_clearToken();
}

Future<ReferralRank> getReferralRank(String referralCode) async {
final Uri uri = Uri.parse('${AppConstants.taskMasterEndpoint}/addresses/leaderboard?referral_code=$referralCode');
return _authenticatedGet(uri, ReferralRank.fromJson);
}

Future<RaidStats> getRaidStats(int raidId) async {
final activeAccount = await getMainAccount();
final Uri uri = Uri.parse(
'${AppConstants.taskMasterEndpoint}/raid-quests/raiders/${activeAccount.accountId}/leaderboards/$raidId',
);
return _authenticatedGet(uri, RaidStats.fromJson);
}
}