@@ -6,6 +6,7 @@ import 'package:threebotlogin/helpers/logger.dart';
66import 'package:threebotlogin/widgets/layout_drawer.dart' ;
77import 'package:threebotlogin/widgets/dao/proposals.dart' ;
88import 'package:threebotlogin/services/tfchain_service.dart' ;
9+ import 'package:connectivity_plus/connectivity_plus.dart' ;
910
1011class DaoPage extends StatefulWidget {
1112 const DaoPage ({super .key});
@@ -21,78 +22,96 @@ class _DaoPageState extends State<DaoPage> with SingleTickerProviderStateMixin {
2122 bool failed = false ;
2223 late final TabController _tabController;
2324
25+ @override
26+ void initState () {
27+ super .initState ();
28+ loadProposals ();
29+ _tabController = TabController (length: 2 , vsync: this );
30+ }
31+
32+ @override
33+ void dispose () {
34+ _tabController.dispose ();
35+ super .dispose ();
36+ }
37+
2438 Future <void > loadProposals () async {
25- setState (() {
26- loading = true ;
27- failed = false ;
28- });
39+ _setLoadingState ();
2940
3041 try {
42+ final connectivityResult = await (Connectivity ().checkConnectivity ());
43+
44+ if (connectivityResult.contains (ConnectivityResult .none)) {
45+ _handleFailure (
46+ 'No internet connection. Please check your network.' ,
47+ );
48+ return ;
49+ }
50+
3151 final proposals = await getProposals ().timeout (
3252 const Duration (minutes: 1 ),
3353 onTimeout: () {
3454 throw TimeoutException ('Loading DAO proposals timed out' );
3555 },
3656 );
3757
38- logger.i ('Proposals loaded successfully' );
39-
40- if (activeList.isNotEmpty) activeList.clear ();
41- if (inactiveList.isNotEmpty) inactiveList.clear ();
42- activeList.addAll (proposals['activeProposals' ]! );
43- inactiveList.addAll (proposals['inactiveProposals' ]! );
44- setState (() {
45- loading = false ;
46- failed = false ;
47- });
58+ _handleSuccess (proposals);
4859 } on TimeoutException catch (e) {
49- logger.e ('Loading proposals timed out: $e ' );
50- if (context.mounted) {
51- final timeoutFailure = SnackBar (
52- content: Text (
53- 'Loading proposals timed out. Please try again.' ,
54- style: Theme .of (context)
55- .textTheme
56- .bodyMedium!
57- .copyWith (color: Theme .of (context).colorScheme.errorContainer),
58- ),
59- duration: const Duration (seconds: 3 ),
60- );
61- ScaffoldMessenger .of (context).clearSnackBars ();
62- ScaffoldMessenger .of (context).showSnackBar (timeoutFailure);
63- }
64- setState (() {
65- loading = false ;
66- failed = true ;
67- });
68- } catch (e) {
69- logger.e ('Failed to load proposals due to $e ' );
70- if (context.mounted) {
71- final loadingProposalFailure = SnackBar (
72- content: Text (
73- 'Failed to load proposals' ,
74- style: Theme .of (context)
75- .textTheme
76- .bodyMedium!
77- .copyWith (color: Theme .of (context).colorScheme.errorContainer),
78- ),
79- duration: const Duration (seconds: 3 ),
80- );
81- ScaffoldMessenger .of (context).clearSnackBars ();
82- ScaffoldMessenger .of (context).showSnackBar (loadingProposalFailure);
83- }
84- setState (() {
85- loading = false ;
86- failed = true ;
87- });
60+ _handleFailure (
61+ 'Loading proposals timed out. Please check your network' ,
62+ error: e,
63+ );
64+ } on Exception catch (e) {
65+ _handleFailure (
66+ 'Failed to load proposals. Please try again.' ,
67+ error: e,
68+ );
8869 }
8970 }
9071
91- @override
92- void initState () {
93- super .initState ();
94- loadProposals ();
95- _tabController = TabController (length: 2 , vsync: this );
72+ void _setLoadingState () {
73+ setState (() {
74+ loading = true ;
75+ failed = false ;
76+ });
77+ }
78+
79+ void _handleSuccess (Map <String , List <Proposal >?> proposals) {
80+ activeList.clear ();
81+ inactiveList.clear ();
82+ activeList.addAll (proposals['activeProposals' ] ?? []);
83+ inactiveList.addAll (proposals['inactiveProposals' ] ?? []);
84+
85+ setState (() {
86+ loading = false ;
87+ failed = false ;
88+ });
89+ }
90+
91+ void _handleFailure (String userMessage, {Object ? error}) {
92+ if (error != null ) {
93+ logger.e ('Load proposals failed' , error: error);
94+ }
95+
96+ if (mounted) {
97+ final errorSnackbar = SnackBar (
98+ content: Text (
99+ userMessage,
100+ style: Theme .of (context)
101+ .textTheme
102+ .bodyMedium!
103+ .copyWith (color: Theme .of (context).colorScheme.errorContainer),
104+ ),
105+ duration: const Duration (seconds: 3 ),
106+ );
107+ ScaffoldMessenger .of (context).clearSnackBars ();
108+ ScaffoldMessenger .of (context).showSnackBar (errorSnackbar);
109+ }
110+
111+ setState (() {
112+ loading = false ;
113+ failed = true ;
114+ });
96115 }
97116
98117 @override
@@ -102,9 +121,10 @@ class _DaoPageState extends State<DaoPage> with SingleTickerProviderStateMixin {
102121 content = Center (
103122 child: Column (
104123 mainAxisAlignment: MainAxisAlignment .center,
124+ mainAxisSize: MainAxisSize .min,
105125 children: [
106126 const CircularProgressIndicator (),
107- const SizedBox (height: 15 ),
127+ const SizedBox (height: 16 ),
108128 Text (
109129 'Loading Proposals...' ,
110130 style: Theme .of (context).textTheme.bodyLarge! .copyWith (
@@ -117,66 +137,61 @@ class _DaoPageState extends State<DaoPage> with SingleTickerProviderStateMixin {
117137 content = Center (
118138 child: Column (
119139 mainAxisAlignment: MainAxisAlignment .center,
140+ mainAxisSize: MainAxisSize .min,
120141 children: [
121- const SizedBox (height: 15 ),
122142 ElevatedButton .icon (
123143 icon: const Icon (Icons .refresh),
124144 label: const Text ('Try Again' ),
125- onPressed: () async {
126- setState (() {
127- failed = false ;
128- loading = true ;
129- });
130- await loadProposals ();
145+ onPressed: () {
146+ loadProposals ();
131147 },
132148 ),
149+ const SizedBox (height: 16 ),
133150 ],
134151 ),
135152 );
136153 } else {
137- content = DefaultTabController (
138- length: 2 ,
139- child: Column (
140- children: [
141- PreferredSize (
142- preferredSize: const Size .fromHeight (50.0 ),
143- child: Container (
144- color: Theme .of (context).scaffoldBackgroundColor,
145- child: TabBar (
146- controller: _tabController,
147- labelColor: Theme .of (context).colorScheme.primary,
148- indicatorColor: Theme .of (context).colorScheme.primary,
149- unselectedLabelColor: Theme .of (context).colorScheme.onSurface,
150- dividerColor: Theme .of (context).scaffoldBackgroundColor,
151- labelStyle: Theme .of (context).textTheme.titleLarge,
152- unselectedLabelStyle: Theme .of (context).textTheme.titleMedium,
153- tabs: const [
154- Tab (text: 'Active' ),
155- Tab (text: 'Executable' ),
156- ],
157- ),
158- ),
159- ),
160- Expanded (
161- child: TabBarView (
154+ content = Column (
155+ children: [
156+ PreferredSize (
157+ preferredSize: const Size .fromHeight (50.0 ),
158+ child: Container (
159+ color: Theme .of (context).scaffoldBackgroundColor,
160+ child: TabBar (
162161 controller: _tabController,
163- children: [
164- RefreshIndicator (
165- onRefresh: loadProposals,
166- child: ProposalsWidget (
167- proposals: activeList,
168- active: true ,
169- )),
170- RefreshIndicator (
171- onRefresh: loadProposals,
172- child: ProposalsWidget (
173- proposals: inactiveList,
174- )),
162+ labelColor: Theme .of (context).colorScheme.primary,
163+ indicatorColor: Theme .of (context).colorScheme.primary,
164+ unselectedLabelColor: Theme .of (context).colorScheme.onSurface,
165+ dividerColor: Theme .of (context).scaffoldBackgroundColor,
166+ labelStyle: Theme .of (context).textTheme.titleLarge,
167+ unselectedLabelStyle: Theme .of (context).textTheme.titleMedium,
168+ tabs: const [
169+ Tab (text: 'Active' ),
170+ Tab (text: 'Executable' ),
175171 ],
176172 ),
177173 ),
178- ],
179- ),
174+ ),
175+ Expanded (
176+ child: TabBarView (
177+ controller: _tabController,
178+ children: [
179+ RefreshIndicator (
180+ onRefresh: loadProposals,
181+ child: ProposalsWidget (
182+ proposals: activeList,
183+ active: true ,
184+ )),
185+ RefreshIndicator (
186+ onRefresh: loadProposals,
187+ child: ProposalsWidget (
188+ proposals: inactiveList,
189+ active: false ,
190+ )),
191+ ],
192+ ),
193+ ),
194+ ],
180195 );
181196 }
182197 return LayoutDrawer (titleText: 'Dao' , content: content);
0 commit comments