mirror of
https://github.com/simon-ding/polaris.git
synced 2026-02-06 15:10:49 +08:00
separate api calls
This commit is contained in:
@@ -88,7 +88,7 @@ class MyApp extends StatelessWidget {
|
||||
|
||||
return ProviderScope(
|
||||
child: MaterialApp.router(
|
||||
title: 'Flutter Demo',
|
||||
title: 'Polaris',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
|
||||
@@ -1,16 +1,40 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
|
||||
var seriesDetailsProvider = FutureProvider.family((ref, seriesId) async {
|
||||
var resp = await Dio().get("${APIs.seriesDetailUrl}$seriesId");
|
||||
var seriesDetailsProvider = AsyncNotifierProvider.autoDispose
|
||||
.family<SeriesDetailData, SeriesDetails, String>(SeriesDetailData.new);
|
||||
|
||||
class SeriesDetailData
|
||||
extends AutoDisposeFamilyAsyncNotifier<SeriesDetails, String> {
|
||||
@override
|
||||
FutureOr<SeriesDetails> build(String arg) async {
|
||||
var resp = await Dio().get("${APIs.seriesDetailUrl}$arg");
|
||||
var rsp = ServerResponse.fromJson(resp.data);
|
||||
if (rsp.code != 0) {
|
||||
throw rsp.message;
|
||||
}
|
||||
return SeriesDetails.fromJson(rsp.data);
|
||||
}
|
||||
|
||||
Future<String> searchAndDownload(
|
||||
String seriesId, int seasonNum, int episodeNum) async {
|
||||
var resp = await Dio().post(APIs.searchAndDownloadUrl, data: {
|
||||
"id": int.parse(seriesId),
|
||||
"season": seasonNum,
|
||||
"episode": episodeNum,
|
||||
});
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
var name = (sp.data as Map<String, dynamic>)["name"];
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
class SeriesDetails {
|
||||
int? id;
|
||||
|
||||
191
ui/lib/providers/settings.dart
Normal file
191
ui/lib/providers/settings.dart
Normal file
@@ -0,0 +1,191 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:quiver/strings.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
|
||||
var tmdbApiSettingProvider =
|
||||
AsyncNotifierProvider<TmdbApiSetting, String>(TmdbApiSetting.new);
|
||||
|
||||
var indexersProvider =
|
||||
AsyncNotifierProvider<IndexerSetting, List<Indexer>>(IndexerSetting.new);
|
||||
|
||||
var dwonloadClientsProvider =
|
||||
AsyncNotifierProvider<DownloadClientSetting, List<DownloadClient>>(
|
||||
DownloadClientSetting.new);
|
||||
|
||||
class TmdbApiSetting extends AsyncNotifier<String> {
|
||||
@override
|
||||
FutureOr<String> build() async {
|
||||
final dio = Dio();
|
||||
var resp = await dio
|
||||
.get(APIs.settingsUrl, queryParameters: {"key": APIs.tmdbApiKey});
|
||||
var rrr = ServerResponse.fromJson(resp.data);
|
||||
if (rrr.code != 0) {
|
||||
throw rrr.message;
|
||||
}
|
||||
var data = rrr.data as Map<String, dynamic>;
|
||||
var key = data[APIs.tmdbApiKey] as String;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
Future<void> submitSettings(String v) async {
|
||||
var resp = await Dio().post(APIs.settingsUrl, data: {APIs.tmdbApiKey: v});
|
||||
var sp = ServerResponse.fromJson(resp.data as Map<String, dynamic>);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IndexerSetting extends AsyncNotifier<List<Indexer>> {
|
||||
final dio = Dio();
|
||||
|
||||
@override
|
||||
FutureOr<List<Indexer>> build() async {
|
||||
var resp = await dio.get(APIs.allIndexersUrl);
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
List<Indexer> indexers = List.empty(growable: true);
|
||||
for (final item in sp.data as List) {
|
||||
indexers.add(Indexer.fromJson(item));
|
||||
}
|
||||
return indexers;
|
||||
}
|
||||
|
||||
Future<void> addIndexer(Indexer indexer) async {
|
||||
if (isBlank(indexer.name) ||
|
||||
isBlank(indexer.url) ||
|
||||
isBlank(indexer.apiKey)) {
|
||||
return;
|
||||
}
|
||||
var resp = await dio.post(APIs.addIndexerUrl, data: indexer.toJson());
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
|
||||
Future<void> deleteIndexer(int id) async {
|
||||
var resp = await dio.delete("${APIs.delIndexerUrl}$id");
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
class Indexer {
|
||||
String? name;
|
||||
String? url;
|
||||
String? apiKey;
|
||||
int? id;
|
||||
|
||||
Indexer({this.name, this.url, this.apiKey});
|
||||
|
||||
Indexer.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
url = json['url'];
|
||||
apiKey = json['api_key'];
|
||||
id = json["id"];
|
||||
}
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['name'] = this.name;
|
||||
data['url'] = this.url;
|
||||
data['api_key'] = this.apiKey;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadClientSetting extends AsyncNotifier<List<DownloadClient>> {
|
||||
final dio = Dio();
|
||||
|
||||
@override
|
||||
FutureOr<List<DownloadClient>> build() async {
|
||||
var resp = await dio.get(APIs.allDownloadClientsUrl);
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
List<DownloadClient> indexers = List.empty(growable: true);
|
||||
for (final item in sp.data as List) {
|
||||
indexers.add(DownloadClient.fromJson(item));
|
||||
}
|
||||
return indexers;
|
||||
}
|
||||
|
||||
Future<void> addDownloadClients(String name, String url) async {
|
||||
if (name.isEmpty || url.isEmpty) {
|
||||
return;
|
||||
}
|
||||
var dio = Dio();
|
||||
var resp = await dio.post(APIs.addDownloadClientUrl, data: {
|
||||
"name": name,
|
||||
"url": url,
|
||||
});
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
|
||||
Future<void> deleteDownloadClients(int id) async {
|
||||
var dio = Dio();
|
||||
var resp = await dio.delete("${APIs.delDownloadClientUrl}$id");
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
ref.invalidateSelf();
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadClient {
|
||||
int? id;
|
||||
bool? enable;
|
||||
String? name;
|
||||
String? implementation;
|
||||
String? url;
|
||||
bool? removeCompletedDownloads;
|
||||
bool? removeFailedDownloads;
|
||||
|
||||
DownloadClient(
|
||||
{this.id,
|
||||
this.enable,
|
||||
this.name,
|
||||
this.implementation,
|
||||
this.url,
|
||||
this.removeCompletedDownloads,
|
||||
this.removeFailedDownloads});
|
||||
|
||||
DownloadClient.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
enable = json['enable'];
|
||||
name = json['name'];
|
||||
implementation = json['implementation'];
|
||||
url = json['url'];
|
||||
removeCompletedDownloads = json['remove_completed_downloads'];
|
||||
removeFailedDownloads = json['remove_failed_downloads'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['enable'] = this.enable;
|
||||
data['name'] = this.name;
|
||||
data['implementation'] = this.implementation;
|
||||
data['url'] = this.url;
|
||||
data['remove_completed_downloads'] = this.removeCompletedDownloads;
|
||||
data['remove_failed_downloads'] = this.removeFailedDownloads;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
|
||||
final welcomePageDataProvider = FutureProvider((ref) async {
|
||||
@@ -43,107 +43,6 @@ class TvSeries {
|
||||
}
|
||||
}
|
||||
|
||||
var tmdbApiSettingProvider = FutureProvider(
|
||||
(ref) async {
|
||||
final dio = Dio();
|
||||
var resp = await dio
|
||||
.get(APIs.settingsUrl, queryParameters: {"key": APIs.tmdbApiKey});
|
||||
var rrr = resp.data as Map<String, dynamic>;
|
||||
var data = rrr["data"] as Map<String, dynamic>;
|
||||
var key = data[APIs.tmdbApiKey] as String;
|
||||
|
||||
return key;
|
||||
},
|
||||
);
|
||||
|
||||
var indexersProvider = FutureProvider((ref) async {
|
||||
final dio = Dio();
|
||||
var resp = await dio.get(APIs.allIndexersUrl);
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
List<Indexer> indexers = List.empty(growable: true);
|
||||
for (final item in sp.data as List) {
|
||||
indexers.add(Indexer.fromJson(item));
|
||||
}
|
||||
return indexers;
|
||||
});
|
||||
|
||||
class Indexer {
|
||||
String? name;
|
||||
String? url;
|
||||
String? apiKey;
|
||||
int? id;
|
||||
|
||||
Indexer({this.name, this.url, this.apiKey});
|
||||
|
||||
Indexer.fromJson(Map<String, dynamic> json) {
|
||||
name = json['name'];
|
||||
url = json['url'];
|
||||
apiKey = json['api_key'];
|
||||
id = json["id"];
|
||||
}
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['name'] = this.name;
|
||||
data['url'] = this.url;
|
||||
data['api_key'] = this.apiKey;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
var dwonloadClientsProvider = FutureProvider((ref) async {
|
||||
final dio = Dio();
|
||||
var resp = await dio.get(APIs.allDownloadClientsUrl);
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0) {
|
||||
throw sp.message;
|
||||
}
|
||||
List<DownloadClient> indexers = List.empty(growable: true);
|
||||
for (final item in sp.data as List) {
|
||||
indexers.add(DownloadClient.fromJson(item));
|
||||
}
|
||||
return indexers;
|
||||
});
|
||||
|
||||
class DownloadClient {
|
||||
int? id;
|
||||
bool? enable;
|
||||
String? name;
|
||||
String? implementation;
|
||||
String? url;
|
||||
bool? removeCompletedDownloads;
|
||||
bool? removeFailedDownloads;
|
||||
|
||||
DownloadClient(
|
||||
{this.id,
|
||||
this.enable,
|
||||
this.name,
|
||||
this.implementation,
|
||||
this.url,
|
||||
this.removeCompletedDownloads,
|
||||
this.removeFailedDownloads});
|
||||
|
||||
DownloadClient.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
enable = json['enable'];
|
||||
name = json['name'];
|
||||
implementation = json['implementation'];
|
||||
url = json['url'];
|
||||
removeCompletedDownloads = json['remove_completed_downloads'];
|
||||
removeFailedDownloads = json['remove_failed_downloads'];
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['id'] = this.id;
|
||||
data['enable'] = this.enable;
|
||||
data['name'] = this.name;
|
||||
data['implementation'] = this.implementation;
|
||||
data['url'] = this.url;
|
||||
data['remove_completed_downloads'] = this.removeCompletedDownloads;
|
||||
data['remove_failed_downloads'] = this.removeFailedDownloads;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/providers/welcome_data.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
import 'package:ui/utils.dart';
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/welcome_data.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
import 'package:ui/providers/settings.dart';
|
||||
import 'package:ui/utils.dart';
|
||||
|
||||
class SystemSettingsPage extends ConsumerStatefulWidget {
|
||||
@@ -18,20 +15,18 @@ class SystemSettingsPage extends ConsumerStatefulWidget {
|
||||
|
||||
class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
final GlobalKey _formKey = GlobalKey<FormState>();
|
||||
|
||||
List<dynamic> indexers = List.empty();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
Future<void>? _pendingTmdb;
|
||||
Future<void>? _pendingIndexer;
|
||||
Future<void>? _pendingDownloadClient;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var key = ref.watch(tmdbApiSettingProvider);
|
||||
|
||||
var tmdbSetting = key.when(
|
||||
data: (data) => Container(
|
||||
var tmdbSetting = FutureBuilder(
|
||||
// We listen to the pending operation, to update the UI accordingly.
|
||||
future: _pendingTmdb,
|
||||
builder: (context, snapshot) {
|
||||
return key.when(
|
||||
data: (value) => Container(
|
||||
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
|
||||
child: Form(
|
||||
key: _formKey, //设置globalKey,用于后面获取FormState
|
||||
@@ -40,17 +35,27 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
children: [
|
||||
TextFormField(
|
||||
autofocus: true,
|
||||
initialValue: data,
|
||||
initialValue: value,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "TMDB Api Key",
|
||||
icon: Icon(Icons.key),
|
||||
),
|
||||
//
|
||||
validator: (v) {
|
||||
return v!.trim().isNotEmpty ? null : "ApiKey 不能为空";
|
||||
return v!.trim().isNotEmpty
|
||||
? null
|
||||
: "ApiKey 不能为空";
|
||||
},
|
||||
onSaved: (newValue) {
|
||||
_submitSettings(context, newValue!);
|
||||
var furture = ref
|
||||
.read(tmdbApiSettingProvider.notifier)
|
||||
.submitSettings(newValue!);
|
||||
setState(() {
|
||||
_pendingTmdb = furture;
|
||||
});
|
||||
if (!showError(snapshot)) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
Center(
|
||||
@@ -79,9 +84,14 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
),
|
||||
error: (err, trace) => Text("$err"),
|
||||
loading: () => const CircularProgressIndicator());
|
||||
});
|
||||
|
||||
var indexers = ref.watch(indexersProvider);
|
||||
var indexerSetting = indexers.when(
|
||||
var indexerSetting = FutureBuilder(
|
||||
// We listen to the pending operation, to update the UI accordingly.
|
||||
future: _pendingIndexer,
|
||||
builder: (context, snapshot) {
|
||||
return indexers.when(
|
||||
data: (value) => GridView.builder(
|
||||
itemCount: value.length + 1,
|
||||
scrollDirection: Axis.vertical,
|
||||
@@ -97,7 +107,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
child: InkWell(
|
||||
//splashColor: Colors.blue.withAlpha(30),
|
||||
onTap: () {
|
||||
showIndexerDetails(context, indexer);
|
||||
showIndexerDetails(snapshot, context, indexer);
|
||||
},
|
||||
child: Center(child: Text(indexer.name!))));
|
||||
}
|
||||
@@ -107,7 +117,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
child: InkWell(
|
||||
//splashColor: Colors.blue.withAlpha(30),
|
||||
onTap: () {
|
||||
showIndexerDetails(context, Indexer());
|
||||
showIndexerDetails(snapshot, context, Indexer());
|
||||
},
|
||||
child: const Center(
|
||||
child: Icon(Icons.add),
|
||||
@@ -115,9 +125,14 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
}),
|
||||
error: (err, trace) => Text("$err"),
|
||||
loading: () => const CircularProgressIndicator());
|
||||
});
|
||||
|
||||
var downloadClients = ref.watch(dwonloadClientsProvider);
|
||||
var downloadSetting = downloadClients.when(
|
||||
var downloadSetting = FutureBuilder(
|
||||
// We listen to the pending operation, to update the UI accordingly.
|
||||
future: _pendingDownloadClient,
|
||||
builder: (context, snapshot) {
|
||||
return downloadClients.when(
|
||||
data: (value) => GridView.builder(
|
||||
itemCount: value.length + 1,
|
||||
scrollDirection: Axis.vertical,
|
||||
@@ -133,7 +148,8 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
child: InkWell(
|
||||
//splashColor: Colors.blue.withAlpha(30),
|
||||
onTap: () {
|
||||
showDownloadClientDetails(context, client);
|
||||
showDownloadClientDetails(
|
||||
snapshot, context, client);
|
||||
},
|
||||
child: Center(child: Text(client.name!))));
|
||||
}
|
||||
@@ -143,7 +159,8 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
child: InkWell(
|
||||
//splashColor: Colors.blue.withAlpha(30),
|
||||
onTap: () {
|
||||
showDownloadClientDetails(context, DownloadClient());
|
||||
showDownloadClientDetails(
|
||||
snapshot, context, DownloadClient());
|
||||
},
|
||||
child: const Center(
|
||||
child: Icon(Icons.add),
|
||||
@@ -151,6 +168,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
}),
|
||||
error: (err, trace) => Text("$err"),
|
||||
loading: () => const CircularProgressIndicator());
|
||||
});
|
||||
|
||||
return ListView(
|
||||
children: [
|
||||
@@ -179,17 +197,8 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
);
|
||||
}
|
||||
|
||||
void _submitSettings(BuildContext context, String v) async {
|
||||
var resp = await Dio().post(APIs.settingsUrl, data: {APIs.tmdbApiKey: v});
|
||||
var sp = ServerResponse.fromJson(resp.data as Map<String, dynamic>);
|
||||
if (sp.code != 0) {
|
||||
if (context.mounted) {
|
||||
Utils.showAlertDialog(context, sp.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showIndexerDetails(BuildContext context, Indexer indexer) {
|
||||
Future<void> showIndexerDetails(
|
||||
AsyncSnapshot<void> snapshot, BuildContext context, Indexer indexer) {
|
||||
var nameController = TextEditingController(text: indexer.name);
|
||||
var urlController = TextEditingController(text: indexer.url);
|
||||
var apiKeyController = TextEditingController(text: indexer.apiKey);
|
||||
@@ -219,9 +228,19 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
),
|
||||
actions: <Widget>[
|
||||
indexer.id == null
|
||||
? Text("")
|
||||
? const Text("")
|
||||
: TextButton(
|
||||
onPressed: () => {deleteIndexer(context, indexer.id!)},
|
||||
onPressed: () {
|
||||
var f = ref
|
||||
.read(indexersProvider.notifier)
|
||||
.deleteIndexer(indexer.id!);
|
||||
setState(() {
|
||||
_pendingIndexer = f;
|
||||
});
|
||||
if (!showError(snapshot)) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: const Text('删除')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
@@ -229,8 +248,18 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
TextButton(
|
||||
child: const Text('确定'),
|
||||
onPressed: () {
|
||||
addIndexer(context, nameController.text, urlController.text,
|
||||
apiKeyController.text);
|
||||
var f = ref.read(indexersProvider.notifier).addIndexer(
|
||||
Indexer(
|
||||
name: nameController.text,
|
||||
url: urlController.text,
|
||||
apiKey: apiKeyController.text));
|
||||
setState(() {
|
||||
_pendingIndexer = f;
|
||||
});
|
||||
|
||||
if (!showError(snapshot)) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -238,36 +267,7 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
});
|
||||
}
|
||||
|
||||
void addIndexer(
|
||||
BuildContext context, String name, String url, String apiKey) async {
|
||||
if (name.isEmpty || url.isEmpty || apiKey.isEmpty) {
|
||||
return;
|
||||
}
|
||||
var dio = Dio();
|
||||
var resp = await dio.post(APIs.addIndexerUrl,
|
||||
data: Indexer(name: name, url: url, apiKey: apiKey).toJson());
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0 && context.mounted) {
|
||||
Utils.showAlertDialog(context, sp.message);
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
ref.refresh(indexersProvider);
|
||||
}
|
||||
|
||||
void deleteIndexer(BuildContext context, int id) async {
|
||||
var dio = Dio();
|
||||
var resp = await dio.delete("${APIs.delIndexerUrl}$id");
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0 && context.mounted) {
|
||||
Utils.showAlertDialog(context, sp.message);
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
ref.refresh(indexersProvider);
|
||||
}
|
||||
|
||||
Future<void> showDownloadClientDetails(
|
||||
Future<void> showDownloadClientDetails(AsyncSnapshot<void> snapshot,
|
||||
BuildContext context, DownloadClient client) {
|
||||
var nameController = TextEditingController(text: client.name);
|
||||
var urlController = TextEditingController(text: client.url);
|
||||
@@ -294,10 +294,19 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
),
|
||||
actions: <Widget>[
|
||||
client.id == null
|
||||
? Text("")
|
||||
? const Text("")
|
||||
: TextButton(
|
||||
onPressed: () =>
|
||||
{deleteDownloadClients(context, client.id!)},
|
||||
onPressed: () {
|
||||
var f = ref
|
||||
.read(dwonloadClientsProvider.notifier)
|
||||
.deleteDownloadClients(client.id!);
|
||||
setState(() {
|
||||
_pendingDownloadClient = f;
|
||||
});
|
||||
if (!showError(snapshot)) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: const Text('删除')),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
@@ -305,8 +314,16 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
TextButton(
|
||||
child: const Text('确定'),
|
||||
onPressed: () {
|
||||
addDownloadClients(
|
||||
context, nameController.text, urlController.text);
|
||||
var f = ref
|
||||
.read(dwonloadClientsProvider.notifier)
|
||||
.addDownloadClients(
|
||||
nameController.text, urlController.text);
|
||||
setState(() {
|
||||
_pendingDownloadClient = f;
|
||||
});
|
||||
if (!showError(snapshot)) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
@@ -314,33 +331,13 @@ class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
});
|
||||
}
|
||||
|
||||
void addDownloadClients(BuildContext context, String name, String url) async {
|
||||
if (name.isEmpty || url.isEmpty) {
|
||||
return;
|
||||
bool showError(AsyncSnapshot<void> snapshot) {
|
||||
final isErrored = snapshot.hasError &&
|
||||
snapshot.connectionState != ConnectionState.waiting;
|
||||
if (isErrored) {
|
||||
Utils.showSnakeBar(context, "当前操作出错: ${snapshot.error}");
|
||||
return true;
|
||||
}
|
||||
var dio = Dio();
|
||||
var resp = await dio.post(APIs.addDownloadClientUrl, data: {
|
||||
"name": name,
|
||||
"url": url,
|
||||
});
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0 && context.mounted) {
|
||||
Utils.showAlertDialog(context, sp.message);
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
ref.refresh(dwonloadClientsProvider);
|
||||
}
|
||||
|
||||
void deleteDownloadClients(BuildContext context, int id) async {
|
||||
var dio = Dio();
|
||||
var resp = await dio.delete("${APIs.delDownloadClientUrl}$id");
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0 && context.mounted) {
|
||||
Utils.showAlertDialog(context, sp.message);
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
ref.refresh(dwonloadClientsProvider);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/providers/series_details.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
import 'package:ui/utils.dart';
|
||||
|
||||
class TvDetailsPage extends ConsumerStatefulWidget {
|
||||
@@ -27,6 +25,7 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
final String seriesId;
|
||||
|
||||
_TvDetailsPageState({required this.seriesId});
|
||||
Future<String>? _pendingFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -36,6 +35,10 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var seriesDetails = ref.watch(seriesDetailsProvider(seriesId));
|
||||
return FutureBuilder(
|
||||
// We listen to the pending operation, to update the UI accordingly.
|
||||
future: _pendingFuture,
|
||||
builder: (context, snapshot) {
|
||||
return seriesDetails.when(
|
||||
data: (details) {
|
||||
Map<int, List<Widget>> m = Map();
|
||||
@@ -58,9 +61,20 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
Text("${ep.title}", textAlign: TextAlign.left),
|
||||
const Expanded(child: Text("")),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_searchAndDownload(context, seriesId, ep.seasonNumber!,
|
||||
onPressed: () async {
|
||||
var f = ref
|
||||
.read(
|
||||
seriesDetailsProvider(seriesId).notifier)
|
||||
.searchAndDownload(seriesId, ep.seasonNumber!,
|
||||
ep.episodeNumber!);
|
||||
setState(() {
|
||||
_pendingFuture = f;
|
||||
});
|
||||
if (!Utils.showError(context, snapshot)) {
|
||||
var name = await f;
|
||||
Utils.showSnakeBar(
|
||||
context, "开始下载: $name");
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.search))
|
||||
],
|
||||
@@ -106,7 +120,8 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
Text(
|
||||
"${details!.name}",
|
||||
style: const TextStyle(
|
||||
fontSize: 14, fontWeight: FontWeight.bold),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Text(""),
|
||||
Text(details!.overview!)
|
||||
@@ -126,23 +141,6 @@ class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
return Text("$err");
|
||||
},
|
||||
loading: () => const CircularProgressIndicator());
|
||||
}
|
||||
|
||||
void _searchAndDownload(BuildContext context, String seriesId, int seasonNum,
|
||||
int episodeNum) async {
|
||||
var resp = await Dio().post(APIs.searchAndDownloadUrl, data: {
|
||||
"id": int.parse(seriesId),
|
||||
"season": seasonNum,
|
||||
"episode": episodeNum,
|
||||
});
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
if (sp.code != 0 && context.mounted) {
|
||||
Utils.showAlertDialog(context, sp.message);
|
||||
return;
|
||||
}
|
||||
var name = (sp.data as Map<String, dynamic>)["name"];
|
||||
if (context.mounted) {
|
||||
Utils.showSnakeBar(context, "$name 开始下载...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,14 @@ class Utils {
|
||||
static showSnakeBar(BuildContext context, String msg) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));
|
||||
}
|
||||
|
||||
static bool showError(BuildContext context, AsyncSnapshot snapshot) {
|
||||
final isErrored = snapshot.hasError &&
|
||||
snapshot.connectionState != ConnectionState.waiting;
|
||||
if (isErrored) {
|
||||
Utils.showSnakeBar(context, "当前操作出错: ${snapshot.error}");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/APIs.dart';
|
||||
import 'package:ui/providers/welcome_data.dart';
|
||||
import 'package:ui/tv_details.dart';
|
||||
|
||||
|
||||
@@ -184,6 +184,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
quiver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: quiver
|
||||
sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -38,6 +38,7 @@ dependencies:
|
||||
cupertino_icons: ^1.0.6
|
||||
go_router: ^14.2.0
|
||||
flutter_riverpod: ^2.5.1
|
||||
quiver: ^3.2.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user