@@ -62,12 +62,16 @@ class ActiveDownloadsWidget extends ConsumerWidget {
6262 color: Theme .of (context).colorScheme.surface,
6363 borderRadius: BorderRadius .circular (12 ),
6464 border: Border .all (
65- color: Theme .of (context).colorScheme.outline.withValues (alpha: 0.2 ),
65+ color:
66+ Theme .of (context).colorScheme.outline.withValues (alpha: 0.2 ),
6667 width: 1.5 ,
6768 ),
6869 boxShadow: [
6970 BoxShadow (
70- color: Theme .of (context).colorScheme.shadow.withValues (alpha: 0.08 ),
71+ color: Theme .of (context)
72+ .colorScheme
73+ .shadow
74+ .withValues (alpha: 0.08 ),
7175 blurRadius: 10 ,
7276 offset: const Offset (0 , 3 ),
7377 ),
@@ -79,7 +83,10 @@ class ActiveDownloadsWidget extends ConsumerWidget {
7983 Container (
8084 padding: const EdgeInsets .all (14.0 ),
8185 decoration: BoxDecoration (
82- color: Theme .of (context).colorScheme.secondary.withValues (alpha: 0.1 ),
86+ color: Theme .of (context)
87+ .colorScheme
88+ .secondary
89+ .withValues (alpha: 0.1 ),
8390 borderRadius: const BorderRadius .only (
8491 topLeft: Radius .circular (12 ),
8592 topRight: Radius .circular (12 ),
@@ -103,7 +110,8 @@ class ActiveDownloadsWidget extends ConsumerWidget {
103110 ),
104111 const SizedBox (width: 6 ),
105112 Container (
106- padding: const EdgeInsets .symmetric (horizontal: 8 , vertical: 2 ),
113+ padding: const EdgeInsets .symmetric (
114+ horizontal: 8 , vertical: 2 ),
107115 decoration: BoxDecoration (
108116 color: Theme .of (context).colorScheme.secondary,
109117 borderRadius: BorderRadius .circular (12 ),
@@ -132,7 +140,10 @@ class ActiveDownloadsWidget extends ConsumerWidget {
132140 separatorBuilder: (context, index) => Divider (
133141 height: 1 ,
134142 thickness: 0.5 ,
135- color: Theme .of (context).colorScheme.outline.withValues (alpha: 0.2 ),
143+ color: Theme .of (context)
144+ .colorScheme
145+ .outline
146+ .withValues (alpha: 0.2 ),
136147 ),
137148 itemBuilder: (context, index) {
138149 final task = downloads.values.elementAt (index);
@@ -154,7 +165,7 @@ class ActiveDownloadsWidget extends ConsumerWidget {
154165 }
155166}
156167
157- class _DownloadItem extends ConsumerWidget {
168+ class _DownloadItem extends ConsumerStatefulWidget {
158169 final DownloadTask task;
159170 final String Function (int ) bytesToFileSize;
160171 final String Function (DownloadStatus ) getStatusText;
@@ -166,8 +177,76 @@ class _DownloadItem extends ConsumerWidget {
166177 });
167178
168179 @override
169- Widget build (BuildContext context, WidgetRef ref) {
180+ ConsumerState <_DownloadItem > createState () => _DownloadItemState ();
181+ }
182+
183+ class _DownloadItemState extends ConsumerState <_DownloadItem > {
184+ // Track if auto-verification has been triggered
185+ bool _autoVerificationTriggered = false ;
186+
187+ @override
188+ void initState () {
189+ super .initState ();
190+ // Auto-trigger verification if manual verification is required
191+ _checkAndTriggerAutoVerification ();
192+ }
193+
194+ @override
195+ void didUpdateWidget (_DownloadItem oldWidget) {
196+ super .didUpdateWidget (oldWidget);
197+ // Check again if task status changed
198+ if (oldWidget.task.status != widget.task.status) {
199+ _checkAndTriggerAutoVerification ();
200+ }
201+ }
202+
203+ // Automatically trigger verification when manual verification is required
204+ void _checkAndTriggerAutoVerification () {
205+ if (_autoVerificationTriggered) return ;
206+
207+ final task = widget.task;
208+ if (task.status == DownloadStatus .failed &&
209+ task.errorMessage? .contains ('Manual verification required' ) == true &&
210+ task.mirrorUrl != null ) {
211+ _autoVerificationTriggered = true ;
212+ // Use post-frame callback to ensure context is ready
213+ WidgetsBinding .instance.addPostFrameCallback ((_) {
214+ if (mounted) {
215+ _triggerVerification ();
216+ }
217+ });
218+ }
219+ }
220+
221+ // Open webview for manual verification with visible countdown/CAPTCHA
222+ Future <void > _triggerVerification () async {
223+ final task = widget.task;
224+ if (task.mirrorUrl == null ) return ;
225+
226+ final downloadManager = ref.read (downloadManagerProvider);
227+
228+ final List <String >? mirrors = await Navigator .push (
229+ context,
230+ MaterialPageRoute (
231+ builder: (BuildContext context) => Webview (
232+ url: task.mirrorUrl! ,
233+ showOverlay: false , // Show full page for CAPTCHA interaction
234+ ),
235+ ),
236+ );
237+
238+ if (mirrors != null && mirrors.isNotEmpty && mounted) {
239+ // Update task with fetched mirrors and restart download
240+ final updatedTask = task.copyWith (mirrors: mirrors);
241+ downloadManager.removeDownload (task.id);
242+ await downloadManager.addDownload (updatedTask);
243+ }
244+ }
245+
246+ @override
247+ Widget build (BuildContext context) {
170248 final downloadManager = ref.read (downloadManagerProvider);
249+ final task = widget.task;
171250
172251 return Padding (
173252 padding: const EdgeInsets .symmetric (horizontal: 14.0 , vertical: 12.0 ),
@@ -182,7 +261,8 @@ class _DownloadItem extends ConsumerWidget {
182261 width: 36 ,
183262 height: 36 ,
184263 decoration: BoxDecoration (
185- color: _getStatusColor (task.status, context).withValues (alpha: 0.15 ),
264+ color: _getStatusColor (task.status, context)
265+ .withValues (alpha: 0.15 ),
186266 borderRadius: BorderRadius .circular (8 ),
187267 ),
188268 child: Center (
@@ -208,7 +288,7 @@ class _DownloadItem extends ConsumerWidget {
208288 Row (
209289 children: [
210290 Text (
211- getStatusText (task.status),
291+ widget. getStatusText (task.status),
212292 style: TextStyle (
213293 fontSize: 11 ,
214294 fontWeight: FontWeight .w500,
@@ -254,7 +334,8 @@ class _DownloadItem extends ConsumerWidget {
254334 icon: Icon (
255335 Icons .close_rounded,
256336 size: 20 ,
257- color: Theme .of (context).colorScheme.tertiary.withAlpha (170 ),
337+ color:
338+ Theme .of (context).colorScheme.tertiary.withAlpha (170 ),
258339 ),
259340 onPressed: () {
260341 downloadManager.cancelDownload (task.id);
@@ -290,11 +371,12 @@ class _DownloadItem extends ConsumerWidget {
290371 ),
291372 ),
292373 Text (
293- '${bytesToFileSize (task .downloadedBytes )} / ${bytesToFileSize (task .totalBytes )}' ,
374+ '${widget . bytesToFileSize (task .downloadedBytes )} / ${widget . bytesToFileSize (task .totalBytes )}' ,
294375 style: TextStyle (
295376 fontSize: 10 ,
296377 fontWeight: FontWeight .w500,
297- color: Theme .of (context).colorScheme.tertiary.withAlpha (140 ),
378+ color:
379+ Theme .of (context).colorScheme.tertiary.withAlpha (140 ),
298380 ),
299381 ),
300382 ],
@@ -313,10 +395,12 @@ class _DownloadItem extends ConsumerWidget {
313395 ),
314396 ),
315397 ] else if (task.status == DownloadStatus .failed) ...[
316- if (task.errorMessage? .contains ('Manual verification required' ) == true ) ...[
398+ if (task.errorMessage? .contains ('Manual verification required' ) ==
399+ true ) ...[
317400 // Show "Verify" button for manual verification required error
318401 Container (
319- padding: const EdgeInsets .symmetric (horizontal: 10 , vertical: 6 ),
402+ padding:
403+ const EdgeInsets .symmetric (horizontal: 10 , vertical: 6 ),
320404 decoration: BoxDecoration (
321405 color: Colors .orange.withValues (alpha: 0.1 ),
322406 borderRadius: BorderRadius .circular (6 ),
@@ -346,17 +430,22 @@ class _DownloadItem extends ConsumerWidget {
346430 const SizedBox (width: 8 ),
347431 TextButton (
348432 onPressed: () async {
349- // Open webview for manual verification
433+ // Open webview for manual verification with full page visibility
350434 if (task.mirrorUrl != null ) {
351435 final List <String >? mirrors = await Navigator .push (
352436 context,
353437 MaterialPageRoute (
354- builder: (BuildContext context) =>
355- Webview (url: task.mirrorUrl! ),
438+ builder: (BuildContext context) => Webview (
439+ url: task.mirrorUrl! ,
440+ showOverlay:
441+ false , // Show full page for CAPTCHA
442+ ),
356443 ),
357444 );
358-
359- if (mirrors != null && mirrors.isNotEmpty && context.mounted) {
445+
446+ if (mirrors != null &&
447+ mirrors.isNotEmpty &&
448+ context.mounted) {
360449 // Update task with fetched mirrors and restart download
361450 final updatedTask = task.copyWith (mirrors: mirrors);
362451 downloadManager.removeDownload (task.id);
@@ -366,7 +455,8 @@ class _DownloadItem extends ConsumerWidget {
366455 },
367456 style: TextButton .styleFrom (
368457 backgroundColor: Colors .orange,
369- padding: const EdgeInsets .symmetric (horizontal: 12 , vertical: 4 ),
458+ padding: const EdgeInsets .symmetric (
459+ horizontal: 12 , vertical: 4 ),
370460 minimumSize: Size .zero,
371461 tapTargetSize: MaterialTapTargetSize .shrinkWrap,
372462 ),
@@ -385,7 +475,8 @@ class _DownloadItem extends ConsumerWidget {
385475 ] else ...[
386476 // Show regular error for other failures
387477 Container (
388- padding: const EdgeInsets .symmetric (horizontal: 10 , vertical: 6 ),
478+ padding:
479+ const EdgeInsets .symmetric (horizontal: 10 , vertical: 6 ),
389480 decoration: BoxDecoration (
390481 color: Colors .red.withValues (alpha: 0.1 ),
391482 borderRadius: BorderRadius .circular (6 ),
0 commit comments