Skip to content

Commit 6466290

Browse files
authored
Merge pull request #344 from Quantus-Network/feat/raid-quests
Quantus raid quest
2 parents 025c335 + 34a3272 commit 6466290

21 files changed

Lines changed: 1491 additions & 576 deletions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
3+
import 'package:url_launcher/url_launcher.dart';
4+
5+
class LinkText extends StatelessWidget {
6+
final String label;
7+
final String url;
8+
final TextStyle? textStyle;
9+
10+
const LinkText({super.key, required this.label, required this.url, this.textStyle});
11+
12+
@override
13+
Widget build(BuildContext context) {
14+
final effectiveTextStyle = (textStyle ?? context.themeText.paragraph)?.copyWith(
15+
decoration: TextDecoration.underline,
16+
);
17+
18+
return GestureDetector(
19+
child: Text(label, style: effectiveTextStyle),
20+
onTap: () {
21+
final Uri uri = Uri.parse(url);
22+
launchUrl(uri);
23+
},
24+
);
25+
}
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart';
3+
import 'package:resonance_network_wallet/features/styles/app_size_theme.dart';
4+
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
5+
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';
6+
7+
class ListItem extends StatelessWidget {
8+
final String title;
9+
final VoidCallback onTap;
10+
final Widget? trailing;
11+
final bool showArrow;
12+
13+
const ListItem({super.key, required this.title, required this.onTap, this.trailing, this.showArrow = true});
14+
15+
@override
16+
Widget build(BuildContext context) {
17+
return GestureDetector(
18+
onTap: onTap,
19+
child: Container(
20+
width: double.infinity,
21+
padding: EdgeInsets.symmetric(vertical: context.isTablet ? 16 : 12, horizontal: 18),
22+
decoration: ShapeDecoration(
23+
color: context.themeColors.buttonGlass,
24+
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
25+
),
26+
child: Row(
27+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
28+
crossAxisAlignment: CrossAxisAlignment.center,
29+
children: [
30+
Text(title, style: context.themeText.smallParagraph),
31+
trailing ??
32+
(showArrow
33+
? Icon(Icons.arrow_forward_ios, size: context.themeSize.settingMenuIconSize)
34+
: const SizedBox()),
35+
],
36+
),
37+
),
38+
);
39+
}
40+
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import 'dart:ui';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter/services.dart';
5+
import 'package:flutter_riverpod/flutter_riverpod.dart';
6+
import 'package:flutter_svg/flutter_svg.dart';
7+
import 'package:quantus_sdk/quantus_sdk.dart';
8+
import 'package:resonance_network_wallet/features/components/button.dart';
9+
import 'package:resonance_network_wallet/features/components/custom_text_field.dart';
10+
import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart';
11+
import 'package:resonance_network_wallet/features/styles/app_size_theme.dart';
12+
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
13+
import 'package:resonance_network_wallet/providers/raider_quest_providers.dart';
14+
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';
15+
import 'package:resonance_network_wallet/shared/extensions/snackbar_extensions.dart';
16+
import 'package:resonance_network_wallet/utils/validators.dart';
17+
18+
class RaidSubmissionActionSheet extends ConsumerStatefulWidget {
19+
const RaidSubmissionActionSheet({super.key});
20+
21+
@override
22+
ConsumerState<RaidSubmissionActionSheet> createState() => _RaidSubmissionActionSheetState();
23+
}
24+
25+
class _RaidSubmissionActionSheetState extends ConsumerState<RaidSubmissionActionSheet> {
26+
final _taskmasterService = TaskmasterService();
27+
28+
final _targetTweetController = TextEditingController();
29+
final _replyTweetController = TextEditingController();
30+
31+
bool _isSubmitting = false;
32+
bool _isDisabled = true;
33+
34+
String? _targetErrorMsg;
35+
String? _replyErrorMsg;
36+
String? _errorMsg;
37+
38+
@override
39+
void initState() {
40+
super.initState();
41+
42+
_targetTweetController.addListener(_checkFormValidity);
43+
_replyTweetController.addListener(_checkFormValidity);
44+
}
45+
46+
void _checkFormValidity() {
47+
String targetInput = _targetTweetController.text.trim();
48+
String replyInput = _replyTweetController.text.trim();
49+
50+
bool targetTweetIsValid = Validators.isValidXStatusUrl(targetInput);
51+
bool replyTweetIsValid = Validators.isValidXStatusUrl(replyInput);
52+
53+
String errMsg = 'Invalid X status link.';
54+
55+
setState(() {
56+
_isDisabled = !targetTweetIsValid || !replyTweetIsValid;
57+
_errorMsg = null;
58+
_targetErrorMsg = targetTweetIsValid ? null : errMsg;
59+
_replyErrorMsg = replyTweetIsValid ? null : errMsg;
60+
});
61+
}
62+
63+
void _closeSheet() {
64+
Navigator.of(context).pop();
65+
}
66+
67+
Future<void> _handleSubmit(String targetLink, String replyLink) async {
68+
setState(() {
69+
_isSubmitting = true;
70+
});
71+
72+
try {
73+
await _taskmasterService.addRaidSubmission(targetLink, replyLink);
74+
if (mounted) {
75+
context.showSuccessSnackbar(title: 'Success submitted!', message: 'Success adding raid submission!');
76+
}
77+
ref.invalidate(raiderSubmissionsProvider);
78+
_closeSheet();
79+
} catch (e) {
80+
print('Failed adding raid submission: $e');
81+
82+
setState(() {
83+
_isSubmitting = false;
84+
_errorMsg = e.toString();
85+
});
86+
}
87+
}
88+
89+
@override
90+
Widget build(BuildContext context) {
91+
final height = MediaQuery.of(context).size.height;
92+
93+
final effectiveHeight = height * 0.76;
94+
final effectiveRadius = 5.0;
95+
final effectivePadding = const EdgeInsets.symmetric(horizontal: 24, vertical: 16);
96+
97+
return SafeArea(
98+
child: Container(
99+
height: effectiveHeight,
100+
padding: effectivePadding,
101+
decoration: ShapeDecoration(
102+
color: Colors.black,
103+
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(effectiveRadius)),
104+
),
105+
child: _buildForm(context),
106+
),
107+
);
108+
}
109+
110+
Widget _buildForm(BuildContext context) {
111+
return Column(
112+
crossAxisAlignment: CrossAxisAlignment.start,
113+
children: [
114+
Container(
115+
width: double.infinity,
116+
padding: const EdgeInsets.all(7),
117+
decoration: ShapeDecoration(shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(100))),
118+
child: Row(
119+
mainAxisAlignment: MainAxisAlignment.end,
120+
children: [
121+
InkWell(
122+
onTap: _isSubmitting ? null : _closeSheet,
123+
child: Icon(Icons.close, size: context.isTablet ? 28 : 24),
124+
),
125+
],
126+
),
127+
),
128+
const SizedBox(height: 22),
129+
Text('Raid Submission', style: context.themeText.mediumTitle?.copyWith(fontWeight: FontWeight.w600)),
130+
SizedBox(height: context.isTablet ? 32 : 24),
131+
Text(
132+
'Have you conducted a raid on a target? Enter your raid detail here:',
133+
style: context.themeText.smallParagraph?.copyWith(color: context.themeColors.inputLabel),
134+
),
135+
const SizedBox(height: 12),
136+
CustomTextField(
137+
controller: _targetTweetController,
138+
labelText: 'Target Tweet Link',
139+
fillColor: context.themeColors.background,
140+
trailing: InkWell(
141+
onTap: () async {
142+
final data = await Clipboard.getData('text/plain');
143+
if (data != null && data.text != null) {
144+
_targetTweetController.text = data.text!;
145+
}
146+
},
147+
child: SvgPicture.asset('assets/paste_icon_1.svg', width: context.isTablet ? 24 : 18),
148+
),
149+
errorMsg: _targetErrorMsg,
150+
),
151+
const SizedBox(height: 8),
152+
CustomTextField(
153+
controller: _replyTweetController,
154+
labelText: 'Reply Tweet Link',
155+
fillColor: context.themeColors.background,
156+
trailing: InkWell(
157+
onTap: () async {
158+
final data = await Clipboard.getData('text/plain');
159+
if (data != null && data.text != null) {
160+
_replyTweetController.text = data.text!;
161+
}
162+
},
163+
child: SvgPicture.asset('assets/paste_icon_1.svg', width: context.isTablet ? 24 : 18),
164+
),
165+
errorMsg: _replyErrorMsg,
166+
),
167+
const SizedBox(height: 24),
168+
const Spacer(),
169+
if (_errorMsg != null)
170+
Center(
171+
child: Column(
172+
crossAxisAlignment: CrossAxisAlignment.center,
173+
mainAxisAlignment: MainAxisAlignment.center,
174+
children: [
175+
Text(_errorMsg!, style: context.themeText.detail?.copyWith(color: context.themeColors.textError)),
176+
const SizedBox(height: 4),
177+
],
178+
),
179+
),
180+
SizedBox(
181+
width: context.isTablet ? 465 : null,
182+
child: Button(
183+
label: 'Submit',
184+
isLoading: _isSubmitting,
185+
isDisabled: _isDisabled,
186+
variant: ButtonVariant.primary,
187+
onPressed: () {
188+
_handleSubmit(_targetTweetController.text, _replyTweetController.text);
189+
},
190+
),
191+
),
192+
193+
SizedBox(height: context.themeSize.bottomButtonSpacing),
194+
],
195+
);
196+
}
197+
}
198+
199+
void showRaidSubmissionActionSheet(
200+
BuildContext context, {
201+
String? referralCode,
202+
bool? directlyShowRewardProgram,
203+
bool showRewardProgram = true,
204+
}) {
205+
showModalBottomSheet(
206+
context: context,
207+
backgroundColor: Colors.transparent,
208+
isScrollControlled: true,
209+
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width),
210+
builder: (context) => Stack(
211+
children: [
212+
Positioned.fill(
213+
child: Container(
214+
decoration: BoxDecoration(
215+
gradient: LinearGradient(
216+
begin: Alignment.topCenter,
217+
end: Alignment.bottomCenter,
218+
colors: [Colors.black, const Color(0xFF312E6E).useOpacity(0.4), Colors.black],
219+
),
220+
),
221+
),
222+
),
223+
Positioned(
224+
bottom: 0,
225+
left: 0,
226+
right: 0,
227+
child: BackdropFilter(
228+
filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3),
229+
child: Container(color: Colors.black.useOpacity(0.3), child: const RaidSubmissionActionSheet()),
230+
),
231+
),
232+
],
233+
),
234+
);
235+
}

mobile-app/lib/features/main/screens/navbar.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
55
import 'package:flutter_svg/svg.dart';
66
import 'package:quantus_sdk/quantus_sdk.dart';
77
import 'package:resonance_network_wallet/features/components/referral_and_reward_action_sheet.dart';
8-
import 'package:resonance_network_wallet/features/main/screens/quests_screen.dart';
8+
import 'package:resonance_network_wallet/features/main/screens/quests/quests_screen.dart';
99
import 'package:resonance_network_wallet/features/main/screens/settings_screen.dart';
1010
import 'package:resonance_network_wallet/features/main/screens/transactions_screen.dart';
1111
import 'package:resonance_network_wallet/features/main/screens/wallet_main/wallet_main.dart';

mobile-app/lib/features/main/screens/account_associations_screen.dart renamed to mobile-app/lib/features/main/screens/quests/account_associations_screen.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'package:resonance_network_wallet/features/components/wallet_app_bar.dart
1414
import 'package:resonance_network_wallet/features/styles/app_colors_theme.dart';
1515
import 'package:resonance_network_wallet/features/styles/app_text_theme.dart';
1616
import 'package:resonance_network_wallet/providers/account_associations_providers.dart';
17+
import 'package:resonance_network_wallet/providers/raider_quest_providers.dart';
1718
import 'package:resonance_network_wallet/shared/extensions/clipboard_extensions.dart';
1819
import 'package:resonance_network_wallet/shared/extensions/media_query_data_extension.dart';
1920
import 'package:resonance_network_wallet/shared/extensions/snackbar_extensions.dart';
@@ -134,6 +135,7 @@ class _AccountAssociationsScreenState extends ConsumerState<AccountAssociationsS
134135

135136
case AppLifecycleState.resumed:
136137
ref.invalidate(accountAssociationsProvider);
138+
ref.invalidate(raiderSubmissionsProvider);
137139
break;
138140
}
139141
}

0 commit comments

Comments
 (0)