import 'package:flutter/material.dart'; import '../core/app_trash.dart'; import '../core/file_ops.dart' show formatSize; import '../l10n/tr.dart'; import 'confirm.dart'; import 'theme.dart'; /// X-plore 비교 e: 앱 휴지통 화면 (Android) - 항목 · 원래 자리 · 지운 때, 골라서 되살리기 · 영구 삭제, 모두 비우기. /// 돌려주는 값: 되살린 경로들 (탐색기가 그 폴더를 새로 읽는다) class TrashPage extends StatefulWidget { final AppTrash trash; /// 보관 일 (0 = 끔) - 안내 줄에 final int days; const TrashPage({super.key, required this.trash, required this.days}); @override State createState() => _TrashPageState(); } class _TrashPageState extends State { List? _items; final _picked = {}; final _restored = []; bool _busy = false; @override void initState() { super.initState(); _load(); } Future _load() async { final items = await widget.trash.list(); if (!mounted) return; setState(() { _items = items; _picked.removeWhere((x) => !items.any((i) => i.entryDir == x)); }); } List get _chosen => [for (final i in _items ?? const []) if (_picked.contains(i.entryDir)) i]; void _say(String t) => ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(t))); Future _restore(List items) async { setState(() => _busy = true); var n = 0; final fails = []; for (final i in items) { try { _restored.add(await widget.trash.restore(i)); n++; } catch (e) { fails.add('${i.name}: $e'); } } await _load(); if (!mounted) return; setState(() => _busy = false); _say([ if (n > 0) trf('{0}개를 원래 자리로 되살렸습니다', [n]), if (fails.isNotEmpty) trf('되살리지 못함: {0}', [fails.take(3).join(' · ')]), ].join(' · ')); } Future _purge(List items, {bool all = false}) async { final ok = await confirmAction( context, title: all ? tr('휴지통 비우기') : trf('{0}개를 영구 삭제할까요?', [items.length]), body: tr('휴지통에서 지운 것은 되살릴 수 없습니다.'), ok: tr('영구 삭제'), ); if (!ok || !mounted) return; setState(() => _busy = true); for (final i in items) { try { await widget.trash.purge(i); } catch (_) {} } await _load(); if (mounted) setState(() => _busy = false); } String _date(DateTime d) => '${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')} ${d.hour.toString().padLeft(2, '0')}:${d.minute.toString().padLeft(2, '0')}'; @override Widget build(BuildContext context) { final items = _items; final chosen = _chosen; final total = (items ?? const []).fold(0, (a, b) => a + b.size); return PopScope( canPop: false, onPopInvokedWithResult: (didPop, _) { if (!didPop) Navigator.pop(context, _restored); }, child: Scaffold( appBar: AppBar( title: Text(items == null ? tr('휴지통') : trf('휴지통 · {0}개 · {1}', [items.length, formatSize(total)])), actions: [ if (items != null && items.isNotEmpty) TextButton.icon( onPressed: _busy ? null : () => _purge(items, all: true), icon: const Icon(Icons.delete_forever_outlined, color: JjColors.danger), label: Text(tr('비우기'), style: const TextStyle(color: JjColors.danger)), ), ], ), body: Column(children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 4), child: Text( widget.days > 0 ? trf('지운 것은 {0}일 동안 여기에 두었다가 저절로 지웁니다 (환경 설정 > 파일 탐색기 > 휴지통 보관).', [widget.days]) : tr('휴지통이 꺼져 있습니다 - 지우면 바로 영구 삭제됩니다 (환경 설정 > 파일 탐색기 > 휴지통 보관).'), style: const TextStyle(fontSize: 12, color: JjColors.textDim), ), ), if (_busy) const LinearProgressIndicator(minHeight: 2), Expanded( child: items == null ? const Center(child: CircularProgressIndicator()) : items.isEmpty ? Center(child: Text(tr('휴지통이 비어 있습니다'))) : ListView.builder( itemCount: items.length, itemBuilder: (_, k) { final i = items[k]; final on = _picked.contains(i.entryDir); return CheckboxListTile( value: on, onChanged: (v) => setState(() => v == true ? _picked.add(i.entryDir) : _picked.remove(i.entryDir)), secondary: Icon(i.isDir ? Icons.folder : Icons.insert_drive_file_outlined, color: i.isDir ? Colors.amber : null), title: Text(i.name, maxLines: 1, overflow: TextOverflow.ellipsis), subtitle: Text('${i.original}\n${_date(i.deletedAt)} · ${formatSize(i.size)}', maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle(fontSize: 11)), isThreeLine: true, ); }, ), ), if (chosen.isNotEmpty) SafeArea( top: false, child: Padding( padding: const EdgeInsets.all(8), child: Row(children: [ Expanded(child: Text(trf('{0}개 고름', [chosen.length]))), TextButton( onPressed: _busy ? null : () => _purge(chosen), child: Text(tr('영구 삭제'), style: const TextStyle(color: JjColors.danger)), ), const SizedBox(width: 8), FilledButton.icon( onPressed: _busy ? null : () => _restore(chosen), icon: const Icon(Icons.restore), label: Text(tr('되살리기')), ), ]), ), ), ]), ), ); } }