mirror of
https://github.com/simon-ding/polaris.git
synced 2026-06-07 10:37:39 +08:00
feat: add import list & calendar
This commit is contained in:
60
ui/lib/calendar.dart
Normal file
60
ui/lib/calendar.dart
Normal file
@@ -0,0 +1,60 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:table_calendar/table_calendar.dart';
|
||||
|
||||
class Calendar extends ConsumerStatefulWidget {
|
||||
@override
|
||||
ConsumerState<ConsumerStatefulWidget> createState() {
|
||||
return _CalendarSate();
|
||||
}
|
||||
}
|
||||
|
||||
class _CalendarSate extends ConsumerState<Calendar> {
|
||||
DateTime? _selectedDay;
|
||||
DateTime _focusedDay = DateTime.now();
|
||||
CalendarFormat _calendarFormat = CalendarFormat.month;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TableCalendar(
|
||||
locale: "zh_CN",
|
||||
firstDay: DateTime.utc(2010, 10, 16),
|
||||
lastDay: DateTime.utc(2030, 3, 14),
|
||||
focusedDay: _focusedDay,
|
||||
selectedDayPredicate: (day) {
|
||||
return isSameDay(_selectedDay, day);
|
||||
},
|
||||
onDaySelected: (selectedDay, focusedDay) {
|
||||
setState(() {
|
||||
_selectedDay = selectedDay;
|
||||
_focusedDay = focusedDay; // update `_focusedDay` here as well
|
||||
});
|
||||
},
|
||||
calendarFormat: _calendarFormat,
|
||||
onFormatChanged: (format) {
|
||||
setState(() {
|
||||
_calendarFormat = format;
|
||||
});
|
||||
},
|
||||
|
||||
//locale: "zh_CN",
|
||||
//eventLoader: (day) {},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
showCalendar(BuildContext context) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
//title: Text("资源"),
|
||||
content: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.7,
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
child: Calendar()),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:intl/date_symbol_data_local.dart';
|
||||
import 'package:ui/activity.dart';
|
||||
import 'package:ui/calendar.dart';
|
||||
import 'package:ui/login_page.dart';
|
||||
import 'package:ui/movie_watchlist.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
@@ -14,7 +16,7 @@ import 'package:ui/welcome_page.dart';
|
||||
import 'package:ui/widgets/utils.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
initializeDateFormatting().then((_) => runApp(MyApp()));
|
||||
}
|
||||
|
||||
class MyApp extends ConsumerStatefulWidget {
|
||||
@@ -219,6 +221,9 @@ class _MainSkeletonState extends State<MainSkeleton> {
|
||||
(BuildContext context, SearchController controller) {
|
||||
return [Text("dadada")];
|
||||
}),
|
||||
IconButton(
|
||||
onPressed: () => showCalendar(context),
|
||||
icon: Icon(Icons.calendar_month)),
|
||||
MenuAnchor(
|
||||
menuChildren: [
|
||||
MenuItemButton(
|
||||
|
||||
@@ -36,6 +36,9 @@ class APIs {
|
||||
static final logFilesUrl = "$_baseUrl/api/v1/setting/logfiles";
|
||||
static final aboutUrl = "$_baseUrl/api/v1/setting/about";
|
||||
static final changeMonitoringUrl = "$_baseUrl/api/v1/setting/monitoring";
|
||||
static final addImportlistUrl = "$_baseUrl/api/v1/importlist/add";
|
||||
static final deleteImportlistUrl = "$_baseUrl/api/v1/importlist/delete";
|
||||
static final getAllImportlists = "$_baseUrl/api/v1/importlist/";
|
||||
|
||||
static final notifierAllUrl = "$_baseUrl/api/v1/notifier/all";
|
||||
static final notifierDeleteUrl = "$_baseUrl/api/v1/notifier/id/";
|
||||
|
||||
@@ -21,6 +21,10 @@ var storageSettingProvider =
|
||||
AsyncNotifierProvider.autoDispose<StorageSettingData, List<Storage>>(
|
||||
StorageSettingData.new);
|
||||
|
||||
var importlistProvider =
|
||||
AsyncNotifierProvider.autoDispose<ImportListData, List<ImportList>>(
|
||||
ImportListData.new);
|
||||
|
||||
class EditSettingData extends AutoDisposeAsyncNotifier<GeneralSetting> {
|
||||
@override
|
||||
FutureOr<GeneralSetting> build() async {
|
||||
@@ -72,7 +76,7 @@ class GeneralSetting {
|
||||
downloadDIr: json["download_dir"],
|
||||
logLevel: json["log_level"],
|
||||
proxy: json["proxy"],
|
||||
enableAdult: json["enable_adult_content"]??false,
|
||||
enableAdult: json["enable_adult_content"] ?? false,
|
||||
allowQiangban: json["allow_qiangban"] ?? false,
|
||||
enableNfo: json["enable_nfo"] ?? false,
|
||||
enablePlexmatch: json["enable_plexmatch"] ?? false);
|
||||
@@ -413,3 +417,77 @@ class About {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ImportList {
|
||||
final int? id;
|
||||
final String? name;
|
||||
final String? url;
|
||||
final String? qulity;
|
||||
final int? storageId;
|
||||
final String? type;
|
||||
ImportList({
|
||||
this.id,
|
||||
this.name,
|
||||
this.url,
|
||||
this.qulity,
|
||||
this.storageId,
|
||||
this.type,
|
||||
});
|
||||
|
||||
factory ImportList.fromJson(Map<String, dynamic> json) {
|
||||
return ImportList(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
url: json["url"],
|
||||
qulity: json["qulity"],
|
||||
type: json["type"],
|
||||
storageId: json["storage_id"]);
|
||||
}
|
||||
|
||||
Map<String, dynamic> tojson() => {
|
||||
"name": name,
|
||||
"url": url,
|
||||
"qulity": qulity,
|
||||
"type": type,
|
||||
"storage_id": storageId
|
||||
};
|
||||
}
|
||||
|
||||
class ImportListData extends AutoDisposeAsyncNotifier<List<ImportList>> {
|
||||
@override
|
||||
FutureOr<List<ImportList>> build() async {
|
||||
final dio = APIs.getDio();
|
||||
var resp = await dio.get(APIs.getAllImportlists);
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
List<ImportList> list = List.empty(growable: true);
|
||||
|
||||
for (var item in sp.data as List) {
|
||||
var il = ImportList.fromJson(item);
|
||||
list.add(il);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
addImportlist(ImportList il) async {
|
||||
final dio = APIs.getDio();
|
||||
var resp = await dio.post(APIs.addImportlistUrl, data: il.tojson());
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
|
||||
deleteimportlist(int id) async {
|
||||
final dio = APIs.getDio();
|
||||
var resp = await dio.post(APIs.deleteImportlistUrl, data: {"id": id});
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
122
ui/lib/settings/importlist.dart
Normal file
122
ui/lib/settings/importlist.dart
Normal file
@@ -0,0 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:ui/providers/settings.dart';
|
||||
import 'package:ui/settings/dialog.dart';
|
||||
import 'package:ui/widgets/progress_indicator.dart';
|
||||
import 'package:ui/widgets/widgets.dart';
|
||||
|
||||
class Importlist extends ConsumerStatefulWidget {
|
||||
const Importlist({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<ConsumerStatefulWidget> createState() {
|
||||
return _ImportlistState();
|
||||
}
|
||||
}
|
||||
|
||||
class _ImportlistState extends ConsumerState<Importlist> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var importlists = ref.watch(importlistProvider);
|
||||
|
||||
return importlists.when(
|
||||
data: (value) => Wrap(
|
||||
children: List.generate(value.length + 1, (i) {
|
||||
if (i < value.length) {
|
||||
var indexer = value[i];
|
||||
return SettingsCard(
|
||||
onTap: () => showImportlistDetails(indexer),
|
||||
child: Text(indexer.name ?? ""));
|
||||
}
|
||||
return SettingsCard(
|
||||
onTap: () => showImportlistDetails(ImportList()),
|
||||
child: const Icon(Icons.add));
|
||||
}),
|
||||
),
|
||||
error: (err, trace) => Text("$err"),
|
||||
loading: () => const MyProgressIndicator());
|
||||
}
|
||||
|
||||
Future<void> showImportlistDetails(ImportList list) {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
|
||||
var body = FormBuilder(
|
||||
key: _formKey,
|
||||
initialValue: {
|
||||
"name": list.name,
|
||||
"qulity": list.qulity,
|
||||
"storage_id": list.storageId
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
FormBuilderTextField(
|
||||
name: "name",
|
||||
decoration: Commons.requiredTextFieldStyle(text: "名称"),
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: FormBuilderValidators.required(),
|
||||
),
|
||||
FormBuilderTextField(
|
||||
name: "url",
|
||||
decoration: Commons.requiredTextFieldStyle(text: "地址"),
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: FormBuilderValidators.required(),
|
||||
),
|
||||
FormBuilderDropdown(
|
||||
name: "qulity",
|
||||
decoration: const InputDecoration(labelText: "清晰度"),
|
||||
items: const [
|
||||
DropdownMenuItem(value: "720p", child: Text("720p")),
|
||||
DropdownMenuItem(value: "1080p", child: Text("1080p")),
|
||||
DropdownMenuItem(value: "2160p", child: Text("2160p")),
|
||||
],
|
||||
),
|
||||
Consumer(
|
||||
builder: (context, ref, child) {
|
||||
var storage = ref.watch(storageSettingProvider);
|
||||
return storage.when(
|
||||
data: (v) {
|
||||
return FormBuilderDropdown(
|
||||
name: "storage_id",
|
||||
decoration: const InputDecoration(labelText: "存储"),
|
||||
items: List.generate(
|
||||
v.length,
|
||||
(i) => DropdownMenuItem(
|
||||
value: v[i].id,
|
||||
child: Text(v[i].name!),
|
||||
)),
|
||||
);
|
||||
},
|
||||
error: (err, trace) => Text("$err"),
|
||||
loading: () => const MyProgressIndicator());
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
onDelete() async {
|
||||
return ref.read(importlistProvider.notifier).deleteimportlist(list.id!);
|
||||
}
|
||||
|
||||
onSubmit() async {
|
||||
if (_formKey.currentState!.saveAndValidate()) {
|
||||
var values = _formKey.currentState!.value;
|
||||
|
||||
return ref.read(importlistProvider.notifier).addImportlist(ImportList(
|
||||
id: list.id,
|
||||
name: values["name"],
|
||||
url: values["url"],
|
||||
type: "plex",
|
||||
qulity: values["qulity"],
|
||||
storageId: values["storage_id"],
|
||||
));
|
||||
} else {
|
||||
throw "validation_error";
|
||||
}
|
||||
}
|
||||
|
||||
return showSettingDialog(
|
||||
context, "导入列表", list.id != null, body, onSubmit, onDelete);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/settings/auth.dart';
|
||||
import 'package:ui/settings/downloader.dart';
|
||||
import 'package:ui/settings/general.dart';
|
||||
import 'package:ui/settings/importlist.dart';
|
||||
import 'package:ui/settings/indexer.dart';
|
||||
import 'package:ui/settings/notifier.dart';
|
||||
import 'package:ui/settings/storage.dart';
|
||||
@@ -21,49 +22,25 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
children: const [
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: true,
|
||||
title: Text("常规"),
|
||||
children: [GeneralSettings()],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: Text("索引器"),
|
||||
children: [IndexerSettings()],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: Text("下载器"),
|
||||
children: [DownloaderSettings()],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: Text("存储"),
|
||||
children: [StorageSettings()],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: Text("通知客户端"),
|
||||
children: [NotifierSettings()],
|
||||
),
|
||||
ExpansionTile(
|
||||
childrenPadding: EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: Text("认证"),
|
||||
children: [AuthSettings()],
|
||||
),
|
||||
children: [
|
||||
getExpansionTile("常规", const GeneralSettings()),
|
||||
getExpansionTile("索引器", const IndexerSettings()),
|
||||
getExpansionTile("下载器", const DownloaderSettings()),
|
||||
getExpansionTile("存储", const StorageSettings()),
|
||||
getExpansionTile("通知客户端", const NotifierSettings()),
|
||||
getExpansionTile("导入列表", const Importlist()),
|
||||
getExpansionTile("认证", const AuthSettings())
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget getExpansionTile(String name, Widget body) {
|
||||
return ExpansionTile(
|
||||
childrenPadding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
expandedAlignment: Alignment.topLeft,
|
||||
initiallyExpanded: false,
|
||||
title: Text(name),
|
||||
children: [body],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +365,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
simple_gesture_detector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: simple_gesture_detector
|
||||
sha256: ba2cd5af24ff20a0b8d609cec3f40e5b0744d2a71804a2616ae086b9c19d19a3
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -410,6 +418,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
table_calendar:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: table_calendar
|
||||
sha256: "4ca32b2fc919452c9974abd4c6ea611a63e33b9e4f0b8c38dba3ac1f4a6549d1"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -518,10 +534,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "14.2.4"
|
||||
version: "14.2.5"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -46,6 +46,7 @@ dependencies:
|
||||
form_builder_validators: ^11.0.0
|
||||
url_launcher: ^6.3.0
|
||||
timeago: ^3.7.0
|
||||
table_calendar: ^3.1.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user