mirror of
https://github.com/simon-ding/polaris.git
synced 2026-06-09 11:39:46 +08:00
feat: support push notification clients
This commit is contained in:
@@ -33,6 +33,10 @@ class APIs {
|
||||
static final logFilesUrl = "$_baseUrl/api/v1/setting/logfiles";
|
||||
static final aboutUrl = "$_baseUrl/api/v1/setting/about";
|
||||
|
||||
static final notifierAllUrl = "$_baseUrl/api/v1/notifier/all";
|
||||
static final notifierDeleteUrl = "$_baseUrl/api/v1/notifier/id/";
|
||||
static final notifierAddUrl = "$_baseUrl/api/v1/notifier/add/";
|
||||
|
||||
static final tmdbImgBaseUrl = "$_baseUrl/api/v1/posters";
|
||||
|
||||
static const tmdbApiKey = "tmdb_api_key";
|
||||
|
||||
75
ui/lib/providers/notifier.dart
Normal file
75
ui/lib/providers/notifier.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/providers/server_response.dart';
|
||||
|
||||
final notifiersDataProvider =
|
||||
AsyncNotifierProvider.autoDispose<NotifiersData, List<NotifierData>>(
|
||||
NotifiersData.new);
|
||||
|
||||
class NotifierData {
|
||||
int? id;
|
||||
String? name;
|
||||
String? service;
|
||||
Map<String, dynamic>? settings;
|
||||
bool? enabled;
|
||||
|
||||
NotifierData({this.id, this.name, this.service, this.enabled, this.settings});
|
||||
|
||||
factory NotifierData.fromJson(Map<String, dynamic> json) {
|
||||
return NotifierData(
|
||||
id: json["id"],
|
||||
name: json["name"],
|
||||
service: json["service"],
|
||||
enabled: json["enabled"] ?? true,
|
||||
settings: json["settings"]);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final data = <String, dynamic>{};
|
||||
data["name"] = name;
|
||||
data["service"] = service;
|
||||
data["enabled"] = enabled;
|
||||
|
||||
data["settings"] = jsonEncode(settings);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class NotifiersData extends AutoDisposeAsyncNotifier<List<NotifierData>> {
|
||||
@override
|
||||
FutureOr<List<NotifierData>> build() async {
|
||||
final dio = APIs.getDio();
|
||||
final resp = await dio.get(APIs.notifierAllUrl);
|
||||
final sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
|
||||
return sp.data == null
|
||||
? List.empty()
|
||||
: (sp.data as List).map((e) => NotifierData.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
Future<void> delete(int id) async {
|
||||
final dio = APIs.getDio();
|
||||
final resp = await dio.delete(APIs.notifierDeleteUrl + id.toString());
|
||||
final sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
|
||||
Future<void> add(NotifierData n) async {
|
||||
final dio = APIs.getDio();
|
||||
final resp = await dio.post(APIs.notifierAddUrl, data: n.toJson());
|
||||
final sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:quiver/strings.dart';
|
||||
import 'package:ui/providers/login.dart';
|
||||
import 'package:ui/providers/notifier.dart';
|
||||
import 'package:ui/providers/settings.dart';
|
||||
import 'package:ui/utils.dart';
|
||||
import 'package:ui/widgets/progress_indicator.dart';
|
||||
@@ -52,14 +53,18 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
FormBuilderTextField(
|
||||
name: "download_dir",
|
||||
decoration: Commons.requiredTextFieldStyle(
|
||||
text: "下载路径", icon: const Icon(Icons.folder), helperText: "媒体文件临时下载路径,非最终存储路径"),
|
||||
text: "下载路径",
|
||||
icon: const Icon(Icons.folder),
|
||||
helperText: "媒体文件临时下载路径,非最终存储路径"),
|
||||
//
|
||||
validator: FormBuilderValidators.required(),
|
||||
),
|
||||
FormBuilderTextField(
|
||||
name: "proxy",
|
||||
decoration: const InputDecoration(
|
||||
labelText: "代理地址", icon: Icon(Icons.folder), helperText: "后台联网代理地址,留空表示不启用代理"),
|
||||
labelText: "代理地址",
|
||||
icon: Icon(Icons.folder),
|
||||
helperText: "后台联网代理地址,留空表示不启用代理"),
|
||||
),
|
||||
SizedBox(
|
||||
width: 300,
|
||||
@@ -246,40 +251,137 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: true,
|
||||
title: const Text("常规设置"),
|
||||
title: const Text("常规"),
|
||||
children: [tmdbSetting],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: const Text("索引器设置"),
|
||||
title: const Text("索引器"),
|
||||
children: [indexerSetting],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: const Text("下载器设置"),
|
||||
title: const Text("下载器"),
|
||||
children: [downloadSetting],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: const EdgeInsets.fromLTRB(20, 0, 50, 0),
|
||||
initiallyExpanded: false,
|
||||
title: const Text("存储设置"),
|
||||
title: const Text("存储"),
|
||||
children: [storageSetting],
|
||||
),
|
||||
ExpansionTile(
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: const EdgeInsets.fromLTRB(20, 0, 50, 0),
|
||||
initiallyExpanded: false,
|
||||
title: const Text("通知客户端"),
|
||||
children: [notifers()],
|
||||
),
|
||||
ExpansionTile(
|
||||
childrenPadding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
||||
initiallyExpanded: false,
|
||||
title: const Text("认证设置"),
|
||||
title: const Text("认证"),
|
||||
children: [authSetting],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget notifers() {
|
||||
final notifierData = ref.watch(notifiersDataProvider);
|
||||
return notifierData.when(
|
||||
data: (v) => Wrap(
|
||||
children: List.generate(v.length + 1, (i) {
|
||||
if (i < v.length) {
|
||||
final client = v[i];
|
||||
return SettingsCard(
|
||||
child: Text("${client.name!} (${client.service})"),
|
||||
onTap: () => showNotifierDetails(client),
|
||||
);
|
||||
}
|
||||
return SettingsCard(
|
||||
onTap: () => showNotifierDetails(NotifierData()),
|
||||
child: const Icon(Icons.add));
|
||||
}),
|
||||
),
|
||||
error: (err, trace) => Text("$err"),
|
||||
loading: () => const MyProgressIndicator());
|
||||
}
|
||||
|
||||
Future<void> showNotifierDetails(NotifierData notifier) {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
|
||||
var body = FormBuilder(
|
||||
key: _formKey,
|
||||
initialValue: {
|
||||
"name": notifier.name,
|
||||
"service": notifier.service,
|
||||
"enabled": notifier.enabled ?? true,
|
||||
"app_token":
|
||||
notifier.settings != null ? notifier.settings!["app_token"] : "",
|
||||
"user_key":
|
||||
notifier.settings != null ? notifier.settings!["user_key"] : "",
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
FormBuilderDropdown(
|
||||
name: "service",
|
||||
decoration: const InputDecoration(labelText: "类型"),
|
||||
items: const [
|
||||
DropdownMenuItem(value: "pushover", child: Text("Pushover")),
|
||||
],
|
||||
),
|
||||
FormBuilderTextField(
|
||||
name: "name",
|
||||
decoration: Commons.requiredTextFieldStyle(text: "名称"),
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: FormBuilderValidators.required(),
|
||||
),
|
||||
FormBuilderTextField(
|
||||
name: "app_token",
|
||||
decoration: Commons.requiredTextFieldStyle(text: "APP密钥"),
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: FormBuilderValidators.required(),
|
||||
),
|
||||
FormBuilderTextField(
|
||||
name: "user_key",
|
||||
decoration: Commons.requiredTextFieldStyle(text: "用户密钥"),
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: FormBuilderValidators.required(),
|
||||
),
|
||||
FormBuilderSwitch(name: "enabled", title: const Text("启用"))
|
||||
],
|
||||
),
|
||||
);
|
||||
onDelete() async {
|
||||
return ref.read(notifiersDataProvider.notifier).delete(notifier.id!);
|
||||
}
|
||||
|
||||
onSubmit() async {
|
||||
if (_formKey.currentState!.saveAndValidate()) {
|
||||
var values = _formKey.currentState!.value;
|
||||
return ref.read(notifiersDataProvider.notifier).add(NotifierData(
|
||||
name: values["name"],
|
||||
service: values["service"],
|
||||
enabled: values["enabled"],
|
||||
settings: {
|
||||
"app_token": values["app_token"],
|
||||
"user_key": values["user_key"]
|
||||
}));
|
||||
} else {
|
||||
throw "validation_error";
|
||||
}
|
||||
}
|
||||
|
||||
return showSettingDialog(
|
||||
"通知客户端", notifier.id != null, body, onSubmit, onDelete);
|
||||
}
|
||||
|
||||
Future<void> showIndexerDetails(Indexer indexer) {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
|
||||
@@ -564,7 +666,9 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
"user": values["user"],
|
||||
"password": values["password"],
|
||||
"change_file_hash":
|
||||
(values["change_file_hash"]??false) as bool ? "true" : "false"
|
||||
(values["change_file_hash"] ?? false) as bool
|
||||
? "true"
|
||||
: "false"
|
||||
},
|
||||
));
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user