mirror of
https://github.com/lifegpc/eh_downloader_flutter.git
synced 2026-07-08 01:30:28 +08:00
Update
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:dio/dio.dart';
|
||||
import 'package:eh_downloader_flutter/api/file.dart';
|
||||
import 'package:retrofit/retrofit.dart';
|
||||
import 'api_result.dart';
|
||||
import 'gallery.dart';
|
||||
import 'status.dart';
|
||||
import 'token.dart';
|
||||
import 'user.dart';
|
||||
@@ -76,6 +77,14 @@ abstract class _EHApi {
|
||||
@GET('/files/{token}')
|
||||
// ignore: unused_element
|
||||
Future<ApiResult<EhFiles>> _getFiles(@Path("token") String token);
|
||||
|
||||
@GET('/gallery/{gid}')
|
||||
Future<ApiResult<GalleryData>> getGallery(@Path("gid") int gid);
|
||||
@GET('/gallery/list')
|
||||
Future<ApiResult<List<GMeta>>> listGalleries(
|
||||
{@Query("all") bool? all,
|
||||
@Query("offset") int? offset,
|
||||
@Query("limit") int? limit});
|
||||
}
|
||||
|
||||
class EHApi extends __EHApi {
|
||||
|
||||
@@ -408,6 +408,79 @@ class __EHApi implements _EHApi {
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResult<GalleryData>> getGallery(int gid) async {
|
||||
const _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{};
|
||||
final _headers = <String, dynamic>{};
|
||||
final Map<String, dynamic>? _data = null;
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(
|
||||
_setStreamType<ApiResult<GalleryData>>(Options(
|
||||
method: 'GET',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/gallery/${gid}',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
))));
|
||||
final value = ApiResult<GalleryData>.fromJson(
|
||||
_result.data!,
|
||||
(json) => GalleryData.fromJson(json as Map<String, dynamic>),
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ApiResult<List<GMeta>>> listGalleries({
|
||||
bool? all,
|
||||
int? offset,
|
||||
int? limit,
|
||||
}) async {
|
||||
const _extra = <String, dynamic>{};
|
||||
final queryParameters = <String, dynamic>{
|
||||
r'all': all,
|
||||
r'offset': offset,
|
||||
r'limit': limit,
|
||||
};
|
||||
queryParameters.removeWhere((k, v) => v == null);
|
||||
final _headers = <String, dynamic>{};
|
||||
final Map<String, dynamic>? _data = null;
|
||||
final _result = await _dio.fetch<Map<String, dynamic>>(
|
||||
_setStreamType<ApiResult<List<GMeta>>>(Options(
|
||||
method: 'GET',
|
||||
headers: _headers,
|
||||
extra: _extra,
|
||||
)
|
||||
.compose(
|
||||
_dio.options,
|
||||
'/gallery/list',
|
||||
queryParameters: queryParameters,
|
||||
data: _data,
|
||||
)
|
||||
.copyWith(
|
||||
baseUrl: _combineBaseUrls(
|
||||
_dio.options.baseUrl,
|
||||
baseUrl,
|
||||
))));
|
||||
final value = ApiResult<List<GMeta>>.fromJson(
|
||||
_result.data!,
|
||||
(json) => json is List<dynamic>
|
||||
? json
|
||||
.map<GMeta>((i) => GMeta.fromJson(i as Map<String, dynamic>))
|
||||
.toList()
|
||||
: List.empty(),
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
|
||||
if (T != dynamic &&
|
||||
!(requestOptions.responseType == ResponseType.bytes ||
|
||||
|
||||
163
lib/api/gallery.dart
Normal file
163
lib/api/gallery.dart
Normal file
@@ -0,0 +1,163 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'gallery.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class GMeta {
|
||||
const GMeta({
|
||||
required this.gid,
|
||||
required this.token,
|
||||
required this.title,
|
||||
required this.titleJpn,
|
||||
required this.category,
|
||||
required this.uploader,
|
||||
required this.posted,
|
||||
required this.filecount,
|
||||
required this.filesize,
|
||||
required this.expunged,
|
||||
required this.rating,
|
||||
this.parentGid,
|
||||
this.parentToken,
|
||||
this.firstGid,
|
||||
this.firstToken,
|
||||
});
|
||||
final int gid;
|
||||
final String token;
|
||||
final String title;
|
||||
@JsonKey(name: 'title_jpn')
|
||||
final String titleJpn;
|
||||
final String category;
|
||||
final String uploader;
|
||||
@JsonKey(fromJson: _fromJson, toJson: _toJson)
|
||||
final DateTime posted;
|
||||
final int filecount;
|
||||
final int filesize;
|
||||
final bool expunged;
|
||||
final double rating;
|
||||
@JsonKey(name: 'parent_gid')
|
||||
final int? parentGid;
|
||||
@JsonKey(name: 'parent_token')
|
||||
final String? parentToken;
|
||||
@JsonKey(name: 'first_gid')
|
||||
final int? firstGid;
|
||||
@JsonKey(name: 'first_token')
|
||||
final String? firstToken;
|
||||
|
||||
static DateTime _fromJson(int posted) =>
|
||||
DateTime.fromMillisecondsSinceEpoch(posted * 1000);
|
||||
static int _toJson(DateTime posted) => posted.millisecondsSinceEpoch ~/ 1000;
|
||||
factory GMeta.fromJson(Map<String, dynamic> json) => _$GMetaFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$GMetaToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class GMetaOptional {
|
||||
const GMetaOptional({
|
||||
this.gid,
|
||||
this.token,
|
||||
this.title,
|
||||
this.titleJpn,
|
||||
this.category,
|
||||
this.uploader,
|
||||
this.posted,
|
||||
this.filecount,
|
||||
this.filesize,
|
||||
this.expunged,
|
||||
this.rating,
|
||||
this.parentGid,
|
||||
this.parentToken,
|
||||
this.firstGid,
|
||||
this.firstToken,
|
||||
});
|
||||
final int? gid;
|
||||
final String? token;
|
||||
final String? title;
|
||||
@JsonKey(name: 'title_jpn')
|
||||
final String? titleJpn;
|
||||
final String? category;
|
||||
final String? uploader;
|
||||
@JsonKey(fromJson: _fromJson, toJson: _toJson)
|
||||
final DateTime? posted;
|
||||
final int? filecount;
|
||||
final int? filesize;
|
||||
final bool? expunged;
|
||||
final double? rating;
|
||||
@JsonKey(name: 'parent_gid')
|
||||
final int? parentGid;
|
||||
@JsonKey(name: 'parent_token')
|
||||
final String? parentToken;
|
||||
@JsonKey(name: 'first_gid')
|
||||
final int? firstGid;
|
||||
@JsonKey(name: 'first_token')
|
||||
final String? firstToken;
|
||||
|
||||
static DateTime? _fromJson(int? posted) => posted != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(posted! * 1000)
|
||||
: null;
|
||||
static int? _toJson(DateTime? posted) =>
|
||||
posted != null ? posted.millisecondsSinceEpoch ~/ 1000 : null;
|
||||
factory GMetaOptional.fromJson(Map<String, dynamic> json) =>
|
||||
_$GMetaOptionalFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$GMetaOptionalToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class Tag {
|
||||
const Tag({
|
||||
required this.id,
|
||||
required this.tag,
|
||||
this.translated,
|
||||
this.intro,
|
||||
});
|
||||
final int id;
|
||||
final String tag;
|
||||
final String? translated;
|
||||
final String? intro;
|
||||
|
||||
factory Tag.fromJson(Map<String, dynamic> json) => _$TagFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$TagToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class ExtendedPMeta {
|
||||
const ExtendedPMeta({
|
||||
required this.gid,
|
||||
required this.index,
|
||||
required this.token,
|
||||
required this.name,
|
||||
required this.width,
|
||||
required this.height,
|
||||
required this.isNsfw,
|
||||
required this.isAd,
|
||||
});
|
||||
final int gid;
|
||||
final int index;
|
||||
final String token;
|
||||
final String name;
|
||||
final int width;
|
||||
final int height;
|
||||
@JsonKey(name: 'is_nsfw')
|
||||
final bool isNsfw;
|
||||
@JsonKey(name: 'is_ad')
|
||||
final bool isAd;
|
||||
|
||||
factory ExtendedPMeta.fromJson(Map<String, dynamic> json) =>
|
||||
_$ExtendedPMetaFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$ExtendedPMetaToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class GalleryData {
|
||||
const GalleryData({
|
||||
required this.meta,
|
||||
required this.tags,
|
||||
required this.pages,
|
||||
});
|
||||
final GMeta meta;
|
||||
final List<Tag> tags;
|
||||
final List<ExtendedPMeta> pages;
|
||||
|
||||
factory GalleryData.fromJson(Map<String, dynamic> json) =>
|
||||
_$GalleryDataFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$GalleryDataToJson(this);
|
||||
}
|
||||
136
lib/api/gallery.g.dart
Normal file
136
lib/api/gallery.g.dart
Normal file
@@ -0,0 +1,136 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'gallery.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
GMeta _$GMetaFromJson(Map<String, dynamic> json) => GMeta(
|
||||
gid: json['gid'] as int,
|
||||
token: json['token'] as String,
|
||||
title: json['title'] as String,
|
||||
titleJpn: json['title_jpn'] as String,
|
||||
category: json['category'] as String,
|
||||
uploader: json['uploader'] as String,
|
||||
posted: GMeta._fromJson(json['posted'] as int),
|
||||
filecount: json['filecount'] as int,
|
||||
filesize: json['filesize'] as int,
|
||||
expunged: json['expunged'] as bool,
|
||||
rating: (json['rating'] as num).toDouble(),
|
||||
parentGid: json['parent_gid'] as int?,
|
||||
parentToken: json['parent_token'] as String?,
|
||||
firstGid: json['first_gid'] as int?,
|
||||
firstToken: json['first_token'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GMetaToJson(GMeta instance) => <String, dynamic>{
|
||||
'gid': instance.gid,
|
||||
'token': instance.token,
|
||||
'title': instance.title,
|
||||
'title_jpn': instance.titleJpn,
|
||||
'category': instance.category,
|
||||
'uploader': instance.uploader,
|
||||
'posted': GMeta._toJson(instance.posted),
|
||||
'filecount': instance.filecount,
|
||||
'filesize': instance.filesize,
|
||||
'expunged': instance.expunged,
|
||||
'rating': instance.rating,
|
||||
'parent_gid': instance.parentGid,
|
||||
'parent_token': instance.parentToken,
|
||||
'first_gid': instance.firstGid,
|
||||
'first_token': instance.firstToken,
|
||||
};
|
||||
|
||||
GMetaOptional _$GMetaOptionalFromJson(Map<String, dynamic> json) =>
|
||||
GMetaOptional(
|
||||
gid: json['gid'] as int?,
|
||||
token: json['token'] as String?,
|
||||
title: json['title'] as String?,
|
||||
titleJpn: json['title_jpn'] as String?,
|
||||
category: json['category'] as String?,
|
||||
uploader: json['uploader'] as String?,
|
||||
posted: GMetaOptional._fromJson(json['posted'] as int?),
|
||||
filecount: json['filecount'] as int?,
|
||||
filesize: json['filesize'] as int?,
|
||||
expunged: json['expunged'] as bool?,
|
||||
rating: (json['rating'] as num?)?.toDouble(),
|
||||
parentGid: json['parent_gid'] as int?,
|
||||
parentToken: json['parent_token'] as String?,
|
||||
firstGid: json['first_gid'] as int?,
|
||||
firstToken: json['first_token'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GMetaOptionalToJson(GMetaOptional instance) =>
|
||||
<String, dynamic>{
|
||||
'gid': instance.gid,
|
||||
'token': instance.token,
|
||||
'title': instance.title,
|
||||
'title_jpn': instance.titleJpn,
|
||||
'category': instance.category,
|
||||
'uploader': instance.uploader,
|
||||
'posted': GMetaOptional._toJson(instance.posted),
|
||||
'filecount': instance.filecount,
|
||||
'filesize': instance.filesize,
|
||||
'expunged': instance.expunged,
|
||||
'rating': instance.rating,
|
||||
'parent_gid': instance.parentGid,
|
||||
'parent_token': instance.parentToken,
|
||||
'first_gid': instance.firstGid,
|
||||
'first_token': instance.firstToken,
|
||||
};
|
||||
|
||||
Tag _$TagFromJson(Map<String, dynamic> json) => Tag(
|
||||
id: json['id'] as int,
|
||||
tag: json['tag'] as String,
|
||||
translated: json['translated'] as String?,
|
||||
intro: json['intro'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$TagToJson(Tag instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'tag': instance.tag,
|
||||
'translated': instance.translated,
|
||||
'intro': instance.intro,
|
||||
};
|
||||
|
||||
ExtendedPMeta _$ExtendedPMetaFromJson(Map<String, dynamic> json) =>
|
||||
ExtendedPMeta(
|
||||
gid: json['gid'] as int,
|
||||
index: json['index'] as int,
|
||||
token: json['token'] as String,
|
||||
name: json['name'] as String,
|
||||
width: json['width'] as int,
|
||||
height: json['height'] as int,
|
||||
isNsfw: json['is_nsfw'] as bool,
|
||||
isAd: json['is_ad'] as bool,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ExtendedPMetaToJson(ExtendedPMeta instance) =>
|
||||
<String, dynamic>{
|
||||
'gid': instance.gid,
|
||||
'index': instance.index,
|
||||
'token': instance.token,
|
||||
'name': instance.name,
|
||||
'width': instance.width,
|
||||
'height': instance.height,
|
||||
'is_nsfw': instance.isNsfw,
|
||||
'is_ad': instance.isAd,
|
||||
};
|
||||
|
||||
GalleryData _$GalleryDataFromJson(Map<String, dynamic> json) => GalleryData(
|
||||
meta: GMeta.fromJson(json['meta'] as Map<String, dynamic>),
|
||||
tags: (json['tags'] as List<dynamic>)
|
||||
.map((e) => Tag.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
pages: (json['pages'] as List<dynamic>)
|
||||
.map((e) => ExtendedPMeta.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$GalleryDataToJson(GalleryData instance) =>
|
||||
<String, dynamic>{
|
||||
'meta': instance.meta,
|
||||
'tags': instance.tags,
|
||||
'pages': instance.pages,
|
||||
};
|
||||
@@ -181,7 +181,7 @@ class _CreateRootUserPage extends State<CreateRootUserPage>
|
||||
});
|
||||
_createRootUser(_username, _password).then((re) {
|
||||
if (re) {
|
||||
auth.clear();
|
||||
clearAllStates(context);
|
||||
context.canPop()
|
||||
? context.pop()
|
||||
: context.go("/");
|
||||
|
||||
86
lib/galleries.dart
Normal file
86
lib/galleries.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'api/gallery.dart';
|
||||
import 'globals.dart';
|
||||
|
||||
final _log = Logger("GalleriesPage");
|
||||
|
||||
class GalleriesPage extends StatefulWidget {
|
||||
const GalleriesPage({Key? key}) : super(key: key);
|
||||
|
||||
static const String routeName = '/galleries';
|
||||
|
||||
@override
|
||||
State<GalleriesPage> createState() => _GalleriesPage();
|
||||
}
|
||||
|
||||
class _GalleriesPage extends State<GalleriesPage> with ThemeModeWidget {
|
||||
static const int _pageSize = 20;
|
||||
|
||||
final PagingController<int, GMeta> _pagingController =
|
||||
PagingController(firstPageKey: 0);
|
||||
|
||||
Future<void> _fetchPage(int pageKey) async {
|
||||
try {
|
||||
final list =
|
||||
(await api.listGalleries(offset: pageKey, limit: _pageSize)).unwrap();
|
||||
final isLastPage = list.length < _pageSize;
|
||||
if (isLastPage) {
|
||||
_pagingController.appendLastPage(list);
|
||||
} else {
|
||||
final nextPageKey = pageKey + list.length;
|
||||
_pagingController.appendPage(list, nextPageKey);
|
||||
}
|
||||
} catch (e) {
|
||||
_log.severe("Failed to load page with offset $pageKey:", e);
|
||||
_pagingController.error = e;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_pagingController.addPageRequestListener((pageKey) {
|
||||
_fetchPage(pageKey);
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
context.canPop() ? context.pop() : context.go("/");
|
||||
},
|
||||
),
|
||||
title: Text(AppLocalizations.of(context)!.galleries),
|
||||
actions: [
|
||||
buildThemeModeIcon(context),
|
||||
buildMoreVertSettingsButon(context),
|
||||
]),
|
||||
body: PagedListView<int, GMeta>(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
pagingController: _pagingController,
|
||||
builderDelegate: PagedChildBuilderDelegate<GMeta>(
|
||||
itemBuilder: (context, item, index) {
|
||||
return ListTile(
|
||||
title: SelectableText(item.title),
|
||||
onTap: () {
|
||||
context.push("/gallery/${item.gid}");
|
||||
},
|
||||
);
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_pagingController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@@ -92,8 +93,8 @@ bool tryInitApi(BuildContext context) {
|
||||
if (_api != null && _api!.baseUrl == baseUrl) {
|
||||
return true;
|
||||
}
|
||||
auth.clear();
|
||||
initApi(baseUrl);
|
||||
clearAllStates(context);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -213,3 +214,24 @@ enum Lang {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final _authLog = Logger("AuthLog");
|
||||
|
||||
void clearAllStates(BuildContext context) {
|
||||
auth.clear();
|
||||
checkAuth(context);
|
||||
}
|
||||
|
||||
void checkAuth(BuildContext context) {
|
||||
if (!auth.isAuthed && !auth.checked && !auth.isChecking) {
|
||||
auth.checkAuth().then((re) {
|
||||
if (!re) {
|
||||
if (auth.status!.noUser && prefs.getBool("skipCreateRootUser") == true)
|
||||
return;
|
||||
context.push(auth.status!.noUser ? "/create_root_user" : "/login");
|
||||
}
|
||||
}).catchError((err) {
|
||||
_authLog.log(Level.SEVERE, "Failed to check auth info:", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,20 +15,7 @@ class HomePage extends HookWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (tryInitApi(context) &&
|
||||
!auth.isAuthed &&
|
||||
!auth.checked &&
|
||||
!auth.isChecking) {
|
||||
auth.checkAuth().then((re) {
|
||||
if (!re) {
|
||||
if (auth.status!.noUser &&
|
||||
prefs.getBool("skipCreateRootUser") == true) return;
|
||||
context.push(auth.status!.noUser ? "/create_root_user" : "/login");
|
||||
}
|
||||
}).catchError((err) {
|
||||
_log.log(Level.SEVERE, "Failed to check auth info:", err);
|
||||
});
|
||||
}
|
||||
tryInitApi(context);
|
||||
var mode = useState(MainApp.of(context).themeMode);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
@@ -48,8 +35,13 @@ class HomePage extends HookWidget {
|
||||
buildMoreVertSettingsButon(context),
|
||||
],
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('Hello World!'),
|
||||
body: Center(
|
||||
child: TextButton(
|
||||
child: Text('Hello World!'),
|
||||
onPressed: () {
|
||||
context.push("/galleries");
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"@@locale": "en",
|
||||
"title": "E-Hentai Downloader Dashboard",
|
||||
"serverHost": "EH Downloader Server Host",
|
||||
"apiPath": "API Path",
|
||||
@@ -17,5 +18,6 @@
|
||||
"settings": "Settings",
|
||||
"lang": "Language",
|
||||
"systemLang": "System Language",
|
||||
"reset": "Reset"
|
||||
"reset": "Reset",
|
||||
"galleries": "Galleries"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"@@locale": "zh",
|
||||
"title": "E-Hentai 下载器面板",
|
||||
"serverHost": "EH Downloader 服务器主机",
|
||||
"apiPath": "API 路径",
|
||||
@@ -17,5 +18,6 @@
|
||||
"settings": "设置",
|
||||
"lang": "语言",
|
||||
"systemLang": "系统语言",
|
||||
"reset": "重置"
|
||||
"reset": "重置",
|
||||
"galleries": "画廊"
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ class _LoginPageState extends State<LoginPage> with ThemeModeWidget {
|
||||
});
|
||||
login(_username, _password).then((re) {
|
||||
if (re) {
|
||||
auth.clear();
|
||||
clearAllStates(context);
|
||||
context.canPop()
|
||||
? context.pop()
|
||||
: context.go("/");
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
import 'create_root_user.dart';
|
||||
import 'galleries.dart';
|
||||
import 'globals.dart';
|
||||
import 'home.dart';
|
||||
import 'login.dart';
|
||||
@@ -19,9 +20,7 @@ final _router = GoRouter(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: HomePage.routeName,
|
||||
// Add const will cause checkAuth not works when pop to home page.
|
||||
// ignore: prefer_const_constructors
|
||||
builder: (context, state) => HomePage(),
|
||||
builder: (context, state) => const HomePage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: SetServerPage.routeName,
|
||||
@@ -38,6 +37,10 @@ final _router = GoRouter(
|
||||
GoRoute(
|
||||
path: SettingsPage.routeName,
|
||||
builder: (context, state) => const SettingsPage(),
|
||||
),
|
||||
GoRoute(
|
||||
path: GalleriesPage.routeName,
|
||||
builder: (context, state) => const GalleriesPage(),
|
||||
)
|
||||
],
|
||||
);
|
||||
@@ -105,7 +108,7 @@ class _MainApp extends State<MainApp> {
|
||||
ThemeData _darkThemeData = ThemeData.dark();
|
||||
ThemeMode get themeMode => _themeMode;
|
||||
Lang _lang = Lang.system;
|
||||
Lang get lang => lang;
|
||||
Lang get lang => _lang;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
||||
@@ -121,8 +121,16 @@ class _SetServerPageState extends State<SetServerPage> with ThemeModeWidget {
|
||||
ElevatedButton(
|
||||
onPressed: _isValid
|
||||
? () {
|
||||
prefs.setString('baseUrl', _serverUrl + _apiPath);
|
||||
context.go('/');
|
||||
prefs
|
||||
.setString('baseUrl', _serverUrl + _apiPath)
|
||||
.then((re) {
|
||||
if (re) {
|
||||
tryInitApi(context);
|
||||
context.canPop()
|
||||
? context.pop()
|
||||
: context.go("/");
|
||||
}
|
||||
});
|
||||
}
|
||||
: null,
|
||||
child: Text(AppLocalizations.of(context)!.save)),
|
||||
|
||||
@@ -17,24 +17,24 @@ class SettingsPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SettingsPage extends State<SettingsPage> with ThemeModeWidget {
|
||||
Lang _ori_lang = Lang.system;
|
||||
Lang _oriLang = Lang.system;
|
||||
Lang _lang = Lang.system;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
try {
|
||||
_ori_lang = Lang.values[prefs.getInt("lang") ?? 0];
|
||||
_lang = _ori_lang;
|
||||
_oriLang = Lang.values[prefs.getInt("lang") ?? 0];
|
||||
_lang = _oriLang;
|
||||
} catch (e) {
|
||||
_log.warning("Failed to get lang:", e);
|
||||
_ori_lang = Lang.system;
|
||||
_oriLang = Lang.system;
|
||||
_lang = Lang.system;
|
||||
}
|
||||
}
|
||||
|
||||
void fallback(BuildContext context) {
|
||||
if (_ori_lang != _lang) {
|
||||
MainApp.of(context).changeLang(_ori_lang);
|
||||
if (_oriLang != _lang) {
|
||||
MainApp.of(context).changeLang(_oriLang);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class _SettingsPage extends State<SettingsPage> with ThemeModeWidget {
|
||||
re = false;
|
||||
_log.warning("Failed to save lang.");
|
||||
} else {
|
||||
_ori_lang = _lang;
|
||||
_oriLang = _lang;
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user