Update localization and add ad and SFW marking

This commit is contained in:
2023-11-30 15:04:20 +08:00
parent a154261acf
commit 7095e144b8
9 changed files with 160 additions and 15 deletions

View File

@@ -126,7 +126,7 @@ class Tag {
@JsonSerializable() @JsonSerializable()
class ExtendedPMeta { class ExtendedPMeta {
const ExtendedPMeta({ ExtendedPMeta({
required this.gid, required this.gid,
required this.index, required this.index,
required this.token, required this.token,
@@ -143,9 +143,9 @@ class ExtendedPMeta {
final int width; final int width;
final int height; final int height;
@JsonKey(name: 'is_nsfw') @JsonKey(name: 'is_nsfw')
final bool isNsfw; bool isNsfw;
@JsonKey(name: 'is_ad') @JsonKey(name: 'is_ad')
final bool isAd; bool isAd;
factory ExtendedPMeta.fromJson(Map<String, dynamic> json) => factory ExtendedPMeta.fromJson(Map<String, dynamic> json) =>
_$ExtendedPMetaFromJson(json); _$ExtendedPMetaFromJson(json);

View File

@@ -24,10 +24,29 @@ class _GalleryInfo extends State<GalleryInfo> with ThemeModeWidget {
setState(() {}); setState(() {});
} }
void _onAdChanged(dynamic args) {
final arguments = args as (String, bool)?;
if (arguments == null) return;
final token = arguments.$1;
final isAd = arguments.$2;
var changed = false;
for (var e in widget.gData.pages) {
if (e.token == token) {
e.isAd = isAd;
changed = true;
break;
}
}
if (changed) {
setState(() {});
}
}
@override @override
void initState() { void initState() {
listener.on("showNsfwChanged", stateChanged); listener.on("showNsfwChanged", stateChanged);
listener.on("displayAdChanged", stateChanged); listener.on("displayAdChanged", stateChanged);
listener.on("adChanged", _onAdChanged);
super.initState(); super.initState();
} }
@@ -100,6 +119,7 @@ class _GalleryInfo extends State<GalleryInfo> with ThemeModeWidget {
controller.dispose(); controller.dispose();
listener.removeEventListener("showNsfwChanged", stateChanged); listener.removeEventListener("showNsfwChanged", stateChanged);
listener.removeEventListener("displayAdChanged", stateChanged); listener.removeEventListener("displayAdChanged", stateChanged);
listener.removeEventListener("adChanged", _onAdChanged);
super.dispose(); super.dispose();
} }
} }

View File

@@ -10,13 +10,25 @@ final _log = Logger("ImageWithContextMenu");
class ImageWithContextMenu extends StatelessWidget { class ImageWithContextMenu extends StatelessWidget {
const ImageWithContextMenu(this.data, const ImageWithContextMenu(this.data,
{Key? key, this.uri, this.dir, this.fileName, this.fmt = ImageFmt.jpg}) {Key? key,
this.uri,
this.dir,
this.fileName,
this.fmt = ImageFmt.jpg,
this.isNsfw,
this.changeNsfw,
this.isAd,
this.changeAd})
: super(key: key); : super(key: key);
final Uint8List data; final Uint8List data;
final String? uri; final String? uri;
final ImageFmt fmt; final ImageFmt fmt;
final String? fileName; final String? fileName;
final String? dir; final String? dir;
final bool Function()? isNsfw;
final Function(bool isNsfw)? changeNsfw;
final bool Function()? isAd;
final Function(bool isAd)? changeAd;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ContextMenuWidget( return ContextMenuWidget(
@@ -53,6 +65,26 @@ class ImageWithContextMenu extends StatelessWidget {
} }
})); }));
} }
if (isNsfw != null && changeNsfw != null) {
final nsfw = isNsfw!();
list.add(MenuAction(
title: nsfw
? AppLocalizations.of(context)!.markAsSfw
: AppLocalizations.of(context)!.markAsNsfw,
callback: () {
changeNsfw!(!isNsfw!());
}));
}
if (isAd != null && changeAd != null) {
final ad = isAd!();
list.add(MenuAction(
title: ad
? AppLocalizations.of(context)!.markAsNonAd
: AppLocalizations.of(context)!.markAsAd,
callback: () {
changeAd!(!isAd!());
}));
}
return Menu(children: list); return Menu(children: list);
}, },
child: Image.memory(data)); child: Image.memory(data));

View File

@@ -64,6 +64,10 @@ enum _ThumbnailMenu {
copyImage, copyImage,
copyImgUrl, copyImgUrl,
saveAs, saveAs,
markAsNsfw,
markAsSfw,
markAsAd,
markAsNonAd,
} }
class _Thumbnail extends State<Thumbnail> { class _Thumbnail extends State<Thumbnail> {
@@ -74,11 +78,52 @@ class _Thumbnail extends State<Thumbnail> {
bool _showNsfw = false; bool _showNsfw = false;
String? _uri; String? _uri;
CancelToken? _cancel; CancelToken? _cancel;
CancelToken? _markAsNsfwCancel;
CancelToken? _markAsAdCancel;
String? _fileName; String? _fileName;
String _dir = ""; String _dir = "";
Color? _iconColor; Color? _iconColor;
double? _iconSize; double? _iconSize;
bool _disposed = false; bool _disposed = false;
void _onNsfwChanged(dynamic args) {
final arguments = args as (String, bool)?;
if (arguments == null) return;
final token = arguments.$1;
final isNsfw = arguments.$2;
if (token != widget._pMeta.token) return;
widget._pMeta.isNsfw = isNsfw;
setState(() {});
}
Future<void> _markAsNsfw(bool isNsfw) async {
try {
_markAsNsfwCancel = CancelToken();
final token = widget._pMeta.token;
(await api.updateFileMeta(token,
isNsfw: isNsfw, cancel: _markAsNsfwCancel))
.unwrap();
listener.tryEmit("nsfwChanged", (token, isNsfw));
} catch (e) {
if (!_markAsNsfwCancel!.isCancelled) {
_log.warning("Failed to mark as nsfw:", e);
}
}
}
Future<void> _markAsAd(bool isAd) async {
try {
_markAsAdCancel = CancelToken();
final token = widget._pMeta.token;
(await api.updateFileMeta(token, isAd: isAd, cancel: _markAsAdCancel))
.unwrap();
listener.tryEmit("adChanged", (token, isAd));
} catch (e) {
if (!_markAsAdCancel!.isCancelled) {
_log.warning("Failed to mark as ad:", e);
}
}
}
Future<void> _fetchData() async { Future<void> _fetchData() async {
try { try {
_cancel = CancelToken(); _cancel = CancelToken();
@@ -133,6 +178,7 @@ class _Thumbnail extends State<Thumbnail> {
_uri = null; _uri = null;
_fileName = "${basenameWithoutExtension(widget._pMeta.name)}_thumb"; _fileName = "${basenameWithoutExtension(widget._pMeta.name)}_thumb";
_dir = isAndroid && widget.gid != null ? widget.gid!.toString() : ""; _dir = isAndroid && widget.gid != null ? widget.gid!.toString() : "";
listener.on("nsfwChanged", _onNsfwChanged);
super.initState(); super.initState();
} }
@@ -174,6 +220,9 @@ class _Thumbnail extends State<Thumbnail> {
void dispose() { void dispose() {
_disposed = true; _disposed = true;
_cancel?.cancel(); _cancel?.cancel();
_markAsNsfwCancel?.cancel();
_markAsAdCancel?.cancel();
listener.removeEventListener("nsfwChanged", _onNsfwChanged);
super.dispose(); super.dispose();
} }
@@ -201,6 +250,18 @@ class _Thumbnail extends State<Thumbnail> {
_log.warning("Failed to save image:", err); _log.warning("Failed to save image:", err);
} }
break; break;
case _ThumbnailMenu.markAsNsfw:
await _markAsNsfw(true);
break;
case _ThumbnailMenu.markAsSfw:
await _markAsNsfw(false);
break;
case _ThumbnailMenu.markAsAd:
await _markAsAd(true);
break;
case _ThumbnailMenu.markAsNonAd:
await _markAsAd(false);
break;
} }
} }
@@ -208,6 +269,7 @@ class _Thumbnail extends State<Thumbnail> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isLoading = _data == null && _error == null; final isLoading = _data == null && _error == null;
final isNsfw = widget._pMeta.isNsfw; final isNsfw = widget._pMeta.isNsfw;
final isAd = widget._pMeta.isAd;
if (isLoading && !_isLoading) _fetchData(); if (isLoading && !_isLoading) _fetchData();
_iconSize ??= Theme.of(context).iconTheme.size; _iconSize ??= Theme.of(context).iconTheme.size;
final iconSize = MediaQuery.of(context).size.width < 400 final iconSize = MediaQuery.of(context).size.width < 400
@@ -234,12 +296,37 @@ class _Thumbnail extends State<Thumbnail> {
PopupMenuItem( PopupMenuItem(
value: _ThumbnailMenu.saveAs, value: _ThumbnailMenu.saveAs,
child: Text(AppLocalizations.of(context)!.saveAs)), child: Text(AppLocalizations.of(context)!.saveAs)),
const PopupMenuDivider(),
PopupMenuItem(
value: isNsfw
? _ThumbnailMenu.markAsSfw
: _ThumbnailMenu.markAsNsfw,
child: Text(isNsfw
? AppLocalizations.of(context)!.markAsSfw
: AppLocalizations.of(context)!.markAsNsfw)),
PopupMenuItem(
value: isAd
? _ThumbnailMenu.markAsNonAd
: _ThumbnailMenu.markAsAd,
child: Text(isAd
? AppLocalizations.of(context)!.markAsNonAd
: AppLocalizations.of(context)!.markAsAd)),
]; ];
return list; return list;
})); }));
final timg = _data != null final timg = _data != null
? ImageWithContextMenu(_data!, ? ImageWithContextMenu(_data!,
uri: _uri, fileName: _fileName, dir: _dir) uri: _uri,
fileName: _fileName,
dir: _dir,
isNsfw: () => widget._pMeta.isNsfw,
changeNsfw: (isNsfw) {
_markAsNsfw(isNsfw);
},
isAd: () => widget._pMeta.isAd,
changeAd: (isAd) {
_markAsAd(isAd);
})
: null; : null;
final img = widget.gid != null && widget.index != null && _data != null final img = widget.gid != null && widget.index != null && _data != null
? GestureDetector( ? GestureDetector(

View File

@@ -25,9 +25,11 @@ class ThumbnailGridView extends StatelessWidget {
final page = npages[index]!; final page = npages[index]!;
final fileId = final fileId =
files != null ? files!.files[page.token]!.first.id : null; files != null ? files!.files[page.token]!.first.id : null;
final key = Key("thumbnail$gid-${page.index}");
return Container( return Container(
padding: const EdgeInsets.all(4), padding: const EdgeInsets.all(4),
child: Thumbnail(page, child: Thumbnail(page,
key: key,
fileId: fileId, fileId: fileId,
gid: gid, gid: gid,
index: index + 1, index: index + 1,

View File

@@ -52,12 +52,12 @@ class _GalleryPage extends State<GalleryPage>
(await api.updateGalleryFileMeta(_gid, (await api.updateGalleryFileMeta(_gid,
isNsfw: isNsfw, cancel: _markAsNsfwCancel)) isNsfw: isNsfw, cancel: _markAsNsfwCancel))
.unwrap(); .unwrap();
if (!_cancel!.isCancelled) { if (!_markAsNsfwCancel!.isCancelled) {
_fetchData(); _fetchData();
} }
} catch (e) { } catch (e) {
if (!_cancel!.isCancelled) { if (!_markAsNsfwCancel!.isCancelled) {
_log.severe("Failed to mark gallery $_gid:", e); _log.warning("Failed to mark gallery $_gid:", e);
} }
} }
} }

View File

@@ -128,14 +128,14 @@ final Path platformPath = Path();
final TagsInfo tags = TagsInfo(); final TagsInfo tags = TagsInfo();
final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey =
GlobalKey<ScaffoldMessengerState>(); GlobalKey<ScaffoldMessengerState>();
final EventListener listener = EventListener(); final EventListener listener = EventListener()..maxListeners = 0;
enum MoreVertSettings { enum MoreVertSettings {
setServerUrl, setServerUrl,
createRootUser, createRootUser,
settings, settings,
markAsNsfw, markAsNsfw,
markAsNonNsfw, markAsSfw,
} }
void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) { void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
@@ -152,7 +152,7 @@ void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
case MoreVertSettings.markAsNsfw: case MoreVertSettings.markAsNsfw:
GalleryPage.maybeOf(context)?.markGalleryAsNsfw(true); GalleryPage.maybeOf(context)?.markGalleryAsNsfw(true);
break; break;
case MoreVertSettings.markAsNonNsfw: case MoreVertSettings.markAsSfw:
GalleryPage.maybeOf(context)?.markGalleryAsNsfw(false); GalleryPage.maybeOf(context)?.markGalleryAsNsfw(false);
break; break;
default: default:
@@ -225,10 +225,10 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
if (isAllNsfw != null) { if (isAllNsfw != null) {
list.add(PopupMenuItem( list.add(PopupMenuItem(
value: isAllNsfw value: isAllNsfw
? MoreVertSettings.markAsNonNsfw ? MoreVertSettings.markAsSfw
: MoreVertSettings.markAsNsfw, : MoreVertSettings.markAsNsfw,
child: Text(isAllNsfw child: Text(isAllNsfw
? AppLocalizations.of(context)!.markAsNonNsfw ? AppLocalizations.of(context)!.markAsSfw
: AppLocalizations.of(context)!.markAsNsfw), : AppLocalizations.of(context)!.markAsNsfw),
)); ));
} }

View File

@@ -89,5 +89,7 @@
"preventScreenCapture": "Prevent screen capture", "preventScreenCapture": "Prevent screen capture",
"seeMoreInfo": "See more information", "seeMoreInfo": "See more information",
"markAsNsfw": "Mark as NSFW", "markAsNsfw": "Mark as NSFW",
"markAsNonNsfw": "Mark as non-NSFW" "markAsSfw": "Mark as SFW",
"markAsAd": "Mark as Ad",
"markAsNonAd": "Mark as non-Ad"
} }

View File

@@ -89,5 +89,7 @@
"preventScreenCapture": "防止截屏", "preventScreenCapture": "防止截屏",
"seeMoreInfo": "显示更多信息", "seeMoreInfo": "显示更多信息",
"markAsNsfw": "标记为NSFW", "markAsNsfw": "标记为NSFW",
"markAsNonNsfw": "标记为非NSFW" "markAsSfw": "标记为SFW",
"markAsAd": "标记为广告",
"markAsNonAd": "标记为非广告"
} }