mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-06-06 05:49:03 +08:00
Add NSFW marking functionality
This commit is contained in:
@@ -170,6 +170,27 @@ abstract class _EHApi {
|
||||
@Query("max_length") int? maxLength,
|
||||
@Query("export_ad") bool? exportAd,
|
||||
@CancelRequest() CancelToken? cancel});
|
||||
|
||||
@POST('/filemeta')
|
||||
@MultiPart()
|
||||
Future<ApiResult<dynamic>> updateGalleryFileMeta(@Part(name: "gid") int gid,
|
||||
{@Part(name: "is_nsfw") bool? isNsfw,
|
||||
@Part(name: "is_ad") bool? isAd,
|
||||
@Part(name: "excludes") String? excludes,
|
||||
@CancelRequest() CancelToken? cancel});
|
||||
@POST('/filemeta')
|
||||
@MultiPart()
|
||||
Future<ApiResult<dynamic>> updateFileMeta(@Part(name: "token") String token,
|
||||
{@Part(name: "is_nsfw") bool? isNsfw,
|
||||
@Part(name: "is_ad") bool? isAd,
|
||||
@CancelRequest() CancelToken? cancel});
|
||||
@POST('/filemeta')
|
||||
@MultiPart()
|
||||
Future<ApiResult<dynamic>> updateFilesMeta(
|
||||
@Part(name: "tokens") String tokens,
|
||||
{@Part(name: "is_nsfw") bool? isNsfw,
|
||||
@Part(name: "is_ad") bool? isAd,
|
||||
@CancelRequest() CancelToken? cancel});
|
||||
}
|
||||
|
||||
class EHApi extends __EHApi {
|
||||
|
||||
@@ -688,6 +688,175 @@ class __EHApi implements _EHApi {
|
||||
return httpResponse;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResult<dynamic>> updateGalleryFileMeta(
|
||||
int gid, {
|
||||
bool? isNsfw,
|
||||
bool? isAd,
|
||||
String? excludes,
|
||||
CancelToken? cancel,
|
||||
}) async {
|
||||
const _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
_data.fields.add(MapEntry(
|
||||
'gid',
|
||||
gid.toString(),
|
||||
));
|
||||
if (isNsfw != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'is_nsfw',
|
||||
isNsfw.toString(),
|
||||
));
|
||||
}
|
||||
if (isAd != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'is_ad',
|
||||
isAd.toString(),
|
||||
));
|
||||
}
|
||||
if (excludes != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'excludes',
|
||||
excludes,
|
||||
));
|
||||
}
|
||||
final _result = await _dio
|
||||
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<dynamic>>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
contentType: 'multipart/form-data',
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/filemeta',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
cancelToken: cancel,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
))));
|
||||
final value = ApiResult<dynamic>.fromJson(
|
||||
_result.data!,
|
||||
(json) => json as dynamic,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResult<dynamic>> updateFileMeta(
|
||||
String token, {
|
||||
bool? isNsfw,
|
||||
bool? isAd,
|
||||
CancelToken? cancel,
|
||||
}) async {
|
||||
const _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
_data.fields.add(MapEntry(
|
||||
'token',
|
||||
token,
|
||||
));
|
||||
if (isNsfw != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'is_nsfw',
|
||||
isNsfw.toString(),
|
||||
));
|
||||
}
|
||||
if (isAd != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'is_ad',
|
||||
isAd.toString(),
|
||||
));
|
||||
}
|
||||
final _result = await _dio
|
||||
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<dynamic>>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
contentType: 'multipart/form-data',
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/filemeta',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
cancelToken: cancel,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
))));
|
||||
final value = ApiResult<dynamic>.fromJson(
|
||||
_result.data!,
|
||||
(json) => json as dynamic,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResult<dynamic>> updateFilesMeta(
|
||||
String tokens, {
|
||||
bool? isNsfw,
|
||||
bool? isAd,
|
||||
CancelToken? cancel,
|
||||
}) async {
|
||||
const _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final _data = FormData();
|
||||
_data.fields.add(MapEntry(
|
||||
'tokens',
|
||||
tokens,
|
||||
));
|
||||
if (isNsfw != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'is_nsfw',
|
||||
isNsfw.toString(),
|
||||
));
|
||||
}
|
||||
if (isAd != null) {
|
||||
_data.fields.add(MapEntry(
|
||||
'is_ad',
|
||||
isAd.toString(),
|
||||
));
|
||||
}
|
||||
final _result = await _dio
|
||||
.fetch<Map<String, dynamic>>(_setStreamType<ApiResult<dynamic>>(Options(
|
||||
method: 'POST',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
contentType: 'multipart/form-data',
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/filemeta',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
cancelToken: cancel,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
))));
|
||||
final value = ApiResult<dynamic>.fromJson(
|
||||
_result.data!,
|
||||
(json) => json as dynamic,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
|
||||
if (T != dynamic &&
|
||||
!(requestOptions.responseType == ResponseType.bytes ||
|
||||
|
||||
@@ -162,6 +162,7 @@ class GalleryData {
|
||||
final GMeta meta;
|
||||
final List<Tag> tags;
|
||||
final List<ExtendedPMeta> pages;
|
||||
bool get isAllNsfw => pages.every((page) => page.isNsfw);
|
||||
|
||||
factory GalleryData.fromJson(Map<String, dynamic> json) =>
|
||||
_$GalleryDataFromJson(json);
|
||||
|
||||
@@ -27,6 +27,12 @@ class GalleryPage extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<GalleryPage> createState() => _GalleryPage();
|
||||
// ignore: library_private_types_in_public_api
|
||||
static _GalleryPage of(BuildContext context) =>
|
||||
context.findAncestorStateOfType<_GalleryPage>()!;
|
||||
// ignore: library_private_types_in_public_api
|
||||
static _GalleryPage? maybeOf(BuildContext context) =>
|
||||
context.findAncestorStateOfType<_GalleryPage>();
|
||||
}
|
||||
|
||||
class _GalleryPage extends State<GalleryPage>
|
||||
@@ -37,7 +43,24 @@ class _GalleryPage extends State<GalleryPage>
|
||||
EhFiles? _files;
|
||||
Object? _error;
|
||||
CancelToken? _cancel;
|
||||
CancelToken? _markAsNsfwCancel;
|
||||
bool _isLoading = false;
|
||||
bool? get isAllNsfw => _data?.isAllNsfw;
|
||||
Future<void> markGalleryAsNsfw(bool isNsfw) async {
|
||||
try {
|
||||
_markAsNsfwCancel = CancelToken();
|
||||
(await api.updateGalleryFileMeta(_gid,
|
||||
isNsfw: isNsfw, cancel: _markAsNsfwCancel))
|
||||
.unwrap();
|
||||
if (!_cancel!.isCancelled) {
|
||||
_fetchData();
|
||||
}
|
||||
} catch (e) {
|
||||
if (!_cancel!.isCancelled) {
|
||||
_log.severe("Failed to mark gallery $_gid:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _fetchData() async {
|
||||
try {
|
||||
@@ -124,6 +147,7 @@ class _GalleryPage extends State<GalleryPage>
|
||||
@override
|
||||
void dispose() {
|
||||
_cancel?.cancel();
|
||||
_markAsNsfwCancel?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import 'auth.dart';
|
||||
import 'config/base.dart';
|
||||
import 'config/shared_preferences.dart';
|
||||
import 'config/windows.dart';
|
||||
import 'gallery.dart';
|
||||
import 'main.dart';
|
||||
import 'platform/clipboard.dart';
|
||||
import 'platform/display.dart';
|
||||
@@ -133,6 +134,8 @@ enum MoreVertSettings {
|
||||
setServerUrl,
|
||||
createRootUser,
|
||||
settings,
|
||||
markAsNsfw,
|
||||
markAsNonNsfw,
|
||||
}
|
||||
|
||||
void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
|
||||
@@ -146,6 +149,12 @@ void onMoreVertSettingsSelected(BuildContext context, MoreVertSettings value) {
|
||||
case MoreVertSettings.settings:
|
||||
context.push("/settings");
|
||||
break;
|
||||
case MoreVertSettings.markAsNsfw:
|
||||
GalleryPage.maybeOf(context)?.markGalleryAsNsfw(true);
|
||||
break;
|
||||
case MoreVertSettings.markAsNonNsfw:
|
||||
GalleryPage.maybeOf(context)?.markGalleryAsNsfw(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -183,11 +192,7 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
prefs.setBool("showNsfw", value);
|
||||
try {
|
||||
listener.emit("showNsfwChanged", null);
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
listener.tryEmit("showNsfwChanged", null);
|
||||
setState(() {
|
||||
showNsfw = value;
|
||||
});
|
||||
@@ -205,11 +210,7 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
prefs.setBool("displayAd", value);
|
||||
try {
|
||||
listener.emit("displayAdChanged", null);
|
||||
} catch (e) {
|
||||
// Do nothing.
|
||||
}
|
||||
listener.tryEmit("displayAdChanged", null);
|
||||
setState(() {
|
||||
displayAd = value;
|
||||
});
|
||||
@@ -218,6 +219,20 @@ List<PopupMenuEntry<MoreVertSettings>> buildMoreVertSettings(
|
||||
title: Text(AppLocalizations.of(context)!.displayAd),
|
||||
),
|
||||
)));
|
||||
if (path == "/gallery/:gid") {
|
||||
list.add(const PopupMenuDivider());
|
||||
final isAllNsfw = GalleryPage.of(context).isAllNsfw;
|
||||
if (isAllNsfw != null) {
|
||||
list.add(PopupMenuItem(
|
||||
value: isAllNsfw
|
||||
? MoreVertSettings.markAsNonNsfw
|
||||
: MoreVertSettings.markAsNsfw,
|
||||
child: Text(isAllNsfw
|
||||
? AppLocalizations.of(context)!.markAsNonNsfw
|
||||
: AppLocalizations.of(context)!.markAsNsfw),
|
||||
));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,5 +87,7 @@
|
||||
"downloadZipFailed": "Failed to download ZIP file.",
|
||||
"rating": "Rating",
|
||||
"preventScreenCapture": "Prevent screen capture",
|
||||
"seeMoreInfo": "See more information"
|
||||
"seeMoreInfo": "See more information",
|
||||
"markAsNsfw": "Mark as NSFW",
|
||||
"markAsNonNsfw": "Mark as non-NSFW"
|
||||
}
|
||||
|
||||
@@ -87,5 +87,7 @@
|
||||
"downloadZipFailed": "Zip文件下载失败。",
|
||||
"rating": "评分",
|
||||
"preventScreenCapture": "防止截屏",
|
||||
"seeMoreInfo": "显示更多信息"
|
||||
"seeMoreInfo": "显示更多信息",
|
||||
"markAsNsfw": "标记为NSFW",
|
||||
"markAsNonNsfw": "标记为非NSFW"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user