mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-07-08 01:30:28 +08:00
Add select mode to gallery page
This commit is contained in:
@@ -12,11 +12,20 @@ import 'thumbnail_gridview.dart';
|
|||||||
|
|
||||||
class GalleryInfo extends StatefulWidget {
|
class GalleryInfo extends StatefulWidget {
|
||||||
const GalleryInfo(this.gData,
|
const GalleryInfo(this.gData,
|
||||||
{super.key, this.files, this.refreshIndicatorKey, this.onRefresh});
|
{super.key,
|
||||||
|
this.files,
|
||||||
|
this.refreshIndicatorKey,
|
||||||
|
this.onRefresh,
|
||||||
|
this.isSelectMode = false,
|
||||||
|
this.onSelectChanged,
|
||||||
|
this.selected});
|
||||||
final GalleryData gData;
|
final GalleryData gData;
|
||||||
final EhFiles? files;
|
final EhFiles? files;
|
||||||
final GlobalKey<RefreshIndicatorState>? refreshIndicatorKey;
|
final GlobalKey<RefreshIndicatorState>? refreshIndicatorKey;
|
||||||
final Future<void> Function()? onRefresh;
|
final Future<void> Function()? onRefresh;
|
||||||
|
final bool isSelectMode;
|
||||||
|
final ValueChanged<bool>? onSelectChanged;
|
||||||
|
final List<String>? selected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GalleryInfo> createState() => _GalleryInfo();
|
State<GalleryInfo> createState() => _GalleryInfo();
|
||||||
@@ -66,14 +75,29 @@ class _GalleryInfo extends State<GalleryInfo> with ThemeModeWidget {
|
|||||||
slivers: [
|
slivers: [
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: Icon(widget.isSelectMode ? Icons.close : Icons.arrow_back),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.canPop() ? context.pop() : context.go("/");
|
if (widget.isSelectMode) {
|
||||||
|
if (widget.onSelectChanged != null) {
|
||||||
|
widget.onSelectChanged!(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.canPop() ? context.pop() : context.go("/");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
title: SelectableText(widget.gData.meta.preferredTitle,
|
title: SelectableText(widget.gData.meta.preferredTitle,
|
||||||
maxLines: 1, minLines: 1),
|
maxLines: 1, minLines: 1),
|
||||||
actions: [
|
actions: [
|
||||||
|
widget.isSelectMode || widget.onSelectChanged == null
|
||||||
|
? Container()
|
||||||
|
: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (widget.onSelectChanged != null) {
|
||||||
|
widget.onSelectChanged!(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.check_box)),
|
||||||
buildThemeModeIcon(context),
|
buildThemeModeIcon(context),
|
||||||
buildMoreVertSettingsButon(context),
|
buildMoreVertSettingsButon(context),
|
||||||
],
|
],
|
||||||
@@ -113,7 +137,10 @@ class _GalleryInfo extends State<GalleryInfo> with ThemeModeWidget {
|
|||||||
SliverGridDelegateWithFixedCrossAxisCount(
|
SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: useMobile ? 2 : 5),
|
crossAxisCount: useMobile ? 2 : 5),
|
||||||
files: widget.files,
|
files: widget.files,
|
||||||
gid: widget.gData.meta.gid),
|
gid: widget.gData.meta.gid,
|
||||||
|
isSelectMode: widget.isSelectMode,
|
||||||
|
selected: widget.selected,
|
||||||
|
onSelectedChange: () => setState(() {})),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (widget.refreshIndicatorKey != null && widget.onRefresh != null) {
|
if (widget.refreshIndicatorKey != null && widget.onRefresh != null) {
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ class Thumbnail extends StatefulWidget {
|
|||||||
this.gid,
|
this.gid,
|
||||||
this.index,
|
this.index,
|
||||||
this.files,
|
this.files,
|
||||||
this.gdata})
|
this.gdata,
|
||||||
|
this.isSelectMode = false,
|
||||||
|
this.isSelected = false,
|
||||||
|
this.onSelectedChange})
|
||||||
: _pMeta = pMeta,
|
: _pMeta = pMeta,
|
||||||
_max = max ?? 1200,
|
_max = max ?? 1200,
|
||||||
_width = width,
|
_width = width,
|
||||||
@@ -43,6 +46,9 @@ class Thumbnail extends StatefulWidget {
|
|||||||
final int? index;
|
final int? index;
|
||||||
final EhFiles? files;
|
final EhFiles? files;
|
||||||
final GalleryData? gdata;
|
final GalleryData? gdata;
|
||||||
|
final bool isSelectMode;
|
||||||
|
final bool isSelected;
|
||||||
|
final ValueChanged<bool>? onSelectedChange;
|
||||||
|
|
||||||
int get height => _height != null
|
int get height => _height != null
|
||||||
? _height!
|
? _height!
|
||||||
@@ -246,7 +252,8 @@ class _Thumbnail extends State<Thumbnail> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get showNsfw => _showNsfw || (prefs.getBool("showNsfw") ?? false);
|
bool get showNsfw =>
|
||||||
|
widget.isSelectMode || _showNsfw || (prefs.getBool("showNsfw") ?? false);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -391,6 +398,7 @@ class _Thumbnail extends State<Thumbnail> {
|
|||||||
},
|
},
|
||||||
child: timg)
|
child: timg)
|
||||||
: timg;
|
: timg;
|
||||||
|
final cs = Theme.of(context).colorScheme;
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: widget.width.toDouble(),
|
width: widget.width.toDouble(),
|
||||||
height: widget.height.toDouble(),
|
height: widget.height.toDouble(),
|
||||||
@@ -431,7 +439,21 @@ class _Thumbnail extends State<Thumbnail> {
|
|||||||
width: widget.width.toDouble(),
|
width: widget.width.toDouble(),
|
||||||
height: widget.height.toDouble(),
|
height: widget.height.toDouble(),
|
||||||
child: img),
|
child: img),
|
||||||
moreVertMenu
|
widget.isSelectMode ? Container() : moreVertMenu,
|
||||||
|
Visibility(
|
||||||
|
visible: widget.isSelectMode,
|
||||||
|
child: const ModalBarrier()),
|
||||||
|
widget.isSelectMode
|
||||||
|
? Center(
|
||||||
|
child: Checkbox(
|
||||||
|
value: widget.isSelected,
|
||||||
|
onChanged: (v) {
|
||||||
|
if (widget.onSelectedChange != null &&
|
||||||
|
v != null) {
|
||||||
|
widget.onSelectedChange!(v);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
: Container(),
|
||||||
])
|
])
|
||||||
: Center(
|
: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
@@ -6,11 +6,19 @@ import 'thumbnail.dart';
|
|||||||
|
|
||||||
class ThumbnailGridView extends StatelessWidget {
|
class ThumbnailGridView extends StatelessWidget {
|
||||||
const ThumbnailGridView(this.gdata, this.gridDelegate,
|
const ThumbnailGridView(this.gdata, this.gridDelegate,
|
||||||
{super.key, this.files, this.gid});
|
{super.key,
|
||||||
|
this.files,
|
||||||
|
this.gid,
|
||||||
|
this.isSelectMode = false,
|
||||||
|
this.selected,
|
||||||
|
this.onSelectedChange});
|
||||||
final GalleryData gdata;
|
final GalleryData gdata;
|
||||||
final int? gid;
|
final int? gid;
|
||||||
final EhFiles? files;
|
final EhFiles? files;
|
||||||
final SliverGridDelegate gridDelegate;
|
final SliverGridDelegate gridDelegate;
|
||||||
|
final bool isSelectMode;
|
||||||
|
final List<String>? selected;
|
||||||
|
final Function? onSelectedChange;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -27,13 +35,27 @@ class ThumbnailGridView extends StatelessWidget {
|
|||||||
final key = Key("thumbnail$gid-${page.index}-$fileId");
|
final key = Key("thumbnail$gid-${page.index}-$fileId");
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
child: Thumbnail(page,
|
child: Thumbnail(
|
||||||
key: key,
|
page,
|
||||||
fileId: fileId,
|
key: key,
|
||||||
gid: gid,
|
fileId: fileId,
|
||||||
index: page.index,
|
gid: gid,
|
||||||
files: files,
|
index: page.index,
|
||||||
gdata: gdata));
|
files: files,
|
||||||
|
gdata: gdata,
|
||||||
|
isSelectMode: isSelectMode,
|
||||||
|
isSelected: selected?.contains(page.token) ?? false,
|
||||||
|
onSelectedChange: (v) {
|
||||||
|
if (v) {
|
||||||
|
selected?.add(page.token);
|
||||||
|
} else {
|
||||||
|
selected?.remove(page.token);
|
||||||
|
}
|
||||||
|
if (onSelectedChange != null) {
|
||||||
|
onSelectedChange!();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ enum MoreVertSettings {
|
|||||||
markAsNsfw,
|
markAsNsfw,
|
||||||
markAsSfw,
|
markAsSfw,
|
||||||
taskManager,
|
taskManager,
|
||||||
|
markAsAd,
|
||||||
|
markAsNonAd,
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
|
void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
|
||||||
@@ -162,6 +164,12 @@ void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
|
|||||||
case MoreVertSettings.taskManager:
|
case MoreVertSettings.taskManager:
|
||||||
context.push("/task_manager");
|
context.push("/task_manager");
|
||||||
break;
|
break;
|
||||||
|
case MoreVertSettings.markAsAd:
|
||||||
|
GalleryPage.maybeOf(context)?.markAsAd(true);
|
||||||
|
break;
|
||||||
|
case MoreVertSettings.markAsNonAd:
|
||||||
|
GalleryPage.maybeOf(context)?.markAsAd(false);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -171,24 +179,23 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
|
|||||||
BuildContext context) {
|
BuildContext context) {
|
||||||
var list = <PopupMenuEntry<MoreVertSettings>>[];
|
var list = <PopupMenuEntry<MoreVertSettings>>[];
|
||||||
var path = GoRouterState.of(context).path;
|
var path = GoRouterState.of(context).path;
|
||||||
|
final i18n = AppLocalizations.of(context)!;
|
||||||
if (auth.status != null &&
|
if (auth.status != null &&
|
||||||
auth.status!.noUser &&
|
auth.status!.noUser &&
|
||||||
prefs.getBool("skipCreateRootUser") == true &&
|
prefs.getBool("skipCreateRootUser") == true &&
|
||||||
path != "/create_root_user") {
|
path != "/create_root_user") {
|
||||||
list.add(PopupMenuItem(
|
list.add(PopupMenuItem(
|
||||||
value: MoreVertSettings.createRootUser,
|
value: MoreVertSettings.createRootUser,
|
||||||
child: Text(AppLocalizations.of(context)!.createRootUser)));
|
child: Text(i18n.createRootUser)));
|
||||||
}
|
}
|
||||||
if (path == null ||
|
if (path == null ||
|
||||||
(path != "/settings" && !path!.startsWith("/settings/"))) {
|
(path != "/settings" && !path!.startsWith("/settings/"))) {
|
||||||
list.add(PopupMenuItem(
|
list.add(PopupMenuItem(
|
||||||
value: MoreVertSettings.settings,
|
value: MoreVertSettings.settings, child: Text(i18n.settings)));
|
||||||
child: Text(AppLocalizations.of(context)!.settings)));
|
|
||||||
}
|
}
|
||||||
if (path != "/task_manager" && auth.canManageTasks == true) {
|
if (path != "/task_manager" && auth.canManageTasks == true) {
|
||||||
list.add(PopupMenuItem(
|
list.add(PopupMenuItem(
|
||||||
value: MoreVertSettings.taskManager,
|
value: MoreVertSettings.taskManager, child: Text(i18n.taskManager)));
|
||||||
child: Text(AppLocalizations.of(context)!.taskManager)));
|
|
||||||
}
|
}
|
||||||
var showNsfw = prefs.getBool("showNsfw") ?? false;
|
var showNsfw = prefs.getBool("showNsfw") ?? false;
|
||||||
list.add(PopupMenuItem(
|
list.add(PopupMenuItem(
|
||||||
@@ -206,7 +213,7 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: Text(AppLocalizations.of(context)!.showNsfw),
|
title: Text(i18n.showNsfw),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
var displayAd = prefs.getBool("displayAd") ?? false;
|
var displayAd = prefs.getBool("displayAd") ?? false;
|
||||||
@@ -225,22 +232,30 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
title: Text(AppLocalizations.of(context)!.displayAd),
|
title: Text(i18n.displayAd),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
if (path == "/gallery/:gid" && auth.canEditGallery == true) {
|
if (path == "/gallery/:gid" && auth.canEditGallery == true) {
|
||||||
list.add(const PopupMenuDivider());
|
list.add(const PopupMenuDivider());
|
||||||
final isAllNsfw = GalleryPage.of(context).isAllNsfw;
|
final gp = GalleryPage.of(context);
|
||||||
if (isAllNsfw != null) {
|
if (!gp.isSelectMode && gp.isAllNsfw != null) {
|
||||||
list.add(PopupMenuItem(
|
list.add(PopupMenuItem(
|
||||||
value: isAllNsfw
|
value: gp.isAllNsfw!
|
||||||
? MoreVertSettings.markAsSfw
|
? MoreVertSettings.markAsSfw
|
||||||
: MoreVertSettings.markAsNsfw,
|
: MoreVertSettings.markAsNsfw,
|
||||||
child: Text(isAllNsfw
|
child: Text(gp.isAllNsfw! ? i18n.markAsSfw : i18n.markAsNsfw),
|
||||||
? AppLocalizations.of(context)!.markAsSfw
|
|
||||||
: AppLocalizations.of(context)!.markAsNsfw),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if (gp.isSelectMode) {
|
||||||
|
list.add(PopupMenuItem(
|
||||||
|
value: MoreVertSettings.markAsNsfw, child: Text(i18n.markAsNsfw)));
|
||||||
|
list.add(PopupMenuItem(
|
||||||
|
value: MoreVertSettings.markAsSfw, child: Text(i18n.markAsSfw)));
|
||||||
|
list.add(PopupMenuItem(
|
||||||
|
value: MoreVertSettings.markAsAd, child: Text(i18n.markAsAd)));
|
||||||
|
list.add(PopupMenuItem(
|
||||||
|
value: MoreVertSettings.markAsNonAd, child: Text(i18n.markAsNonAd)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,14 +44,25 @@ class _GalleryPage extends State<GalleryPage>
|
|||||||
Object? _error;
|
Object? _error;
|
||||||
CancelToken? _cancel;
|
CancelToken? _cancel;
|
||||||
CancelToken? _markAsNsfwCancel;
|
CancelToken? _markAsNsfwCancel;
|
||||||
|
CancelToken? _markAsAdCancel;
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
|
bool _isSelectMode = false;
|
||||||
|
final List<String> _selected = [];
|
||||||
bool? get isAllNsfw => _data?.isAllNsfw;
|
bool? get isAllNsfw => _data?.isAllNsfw;
|
||||||
|
bool get isSelectMode => _isSelectMode;
|
||||||
Future<void> markGalleryAsNsfw(bool isNsfw) async {
|
Future<void> markGalleryAsNsfw(bool isNsfw) async {
|
||||||
try {
|
try {
|
||||||
_markAsNsfwCancel = CancelToken();
|
_markAsNsfwCancel = CancelToken();
|
||||||
(await api.updateGalleryFileMeta(_gid,
|
if (_isSelectMode) {
|
||||||
isNsfw: isNsfw, cancel: _markAsNsfwCancel))
|
if (_selected.isEmpty) return;
|
||||||
.unwrap();
|
(await api.updateFilesMeta(_selected.join(","),
|
||||||
|
isNsfw: isNsfw, cancel: _markAsNsfwCancel))
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
(await api.updateGalleryFileMeta(_gid,
|
||||||
|
isNsfw: isNsfw, cancel: _markAsNsfwCancel))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
if (!_markAsNsfwCancel!.isCancelled) {
|
if (!_markAsNsfwCancel!.isCancelled) {
|
||||||
_fetchData();
|
_fetchData();
|
||||||
}
|
}
|
||||||
@@ -62,6 +73,23 @@ class _GalleryPage extends State<GalleryPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> markAsAd(bool isAd) async {
|
||||||
|
if (!_isSelectMode || _selected.isEmpty) return;
|
||||||
|
try {
|
||||||
|
_markAsAdCancel = CancelToken();
|
||||||
|
(await api.updateFilesMeta(_selected.join(","),
|
||||||
|
isAd: isAd, cancel: _markAsAdCancel))
|
||||||
|
.unwrap();
|
||||||
|
if (!_markAsAdCancel!.isCancelled) {
|
||||||
|
_fetchData();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!_markAsAdCancel!.isCancelled) {
|
||||||
|
_log.warning("Failed to mark gallery $_gid:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _fetchData() async {
|
Future<void> _fetchData() async {
|
||||||
try {
|
try {
|
||||||
_cancel = CancelToken();
|
_cancel = CancelToken();
|
||||||
@@ -76,6 +104,7 @@ class _GalleryPage extends State<GalleryPage>
|
|||||||
setState(() {
|
setState(() {
|
||||||
_files = fileData;
|
_files = fileData;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
_selected.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -138,11 +167,22 @@ class _GalleryPage extends State<GalleryPage>
|
|||||||
body: isLoading
|
body: isLoading
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: _data != null
|
: _data != null
|
||||||
? GalleryInfo(_data!,
|
? GalleryInfo(
|
||||||
files: _files, refreshIndicatorKey: _refreshIndicatorKey,
|
_data!,
|
||||||
|
files: _files,
|
||||||
|
refreshIndicatorKey: _refreshIndicatorKey,
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await _fetchData();
|
await _fetchData();
|
||||||
})
|
},
|
||||||
|
onSelectChanged: (v) {
|
||||||
|
setState(() {
|
||||||
|
_isSelectMode = v;
|
||||||
|
if (v) _selected.clear();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isSelectMode: _isSelectMode,
|
||||||
|
selected: _selected,
|
||||||
|
)
|
||||||
: Center(
|
: Center(
|
||||||
child: Text("Error: $_error"),
|
child: Text("Error: $_error"),
|
||||||
));
|
));
|
||||||
@@ -152,6 +192,7 @@ class _GalleryPage extends State<GalleryPage>
|
|||||||
void dispose() {
|
void dispose() {
|
||||||
_cancel?.cancel();
|
_cancel?.cancel();
|
||||||
_markAsNsfwCancel?.cancel();
|
_markAsNsfwCancel?.cancel();
|
||||||
|
_markAsAdCancel?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user