feat: Enhance TTS player functionality and UI
- Added resume functionality to TTS player when paused. - Display voice name or language in TTS player UI. - Improved error handling in reader provider with debug messages. - Updated TTS service to configure Vietnamese voice and handle platform-specific audio settings. - Removed wakelock dependency and related code. - Fixed search screen error handling. - Updated settings screen to navigate to home after sign out. - Improved splash screen with timer management. - Enhanced main app error handling with logging. - Removed unused package_info_plus and wakelock_plus dependencies. - Added environment variable support for mobile runtime. - Integrated Google Sign-In configuration for Android. - Created logging observer for Riverpod providers. - Added scripts for environment setup and Google Sign-In validation.
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../../../core/models/novel_model.dart';
|
||||
import '../../../core/network/providers.dart';
|
||||
|
||||
@@ -17,22 +19,46 @@ class HomeData {
|
||||
final homeProvider = FutureProvider<HomeData>((ref) async {
|
||||
final client = ref.read(apiClientProvider);
|
||||
|
||||
final results = await Future.wait([
|
||||
final results = await Future.wait<Response<dynamic>>([
|
||||
client.dio.get('/api/novels/browse', queryParameters: {'sort': 'popular', 'limit': '10', 'page': '1'}),
|
||||
client.dio.get('/api/novels/browse', queryParameters: {'sort': 'latest', 'limit': '20', 'page': '1'}),
|
||||
client.dio.get('/api/novels/browse', queryParameters: {'sort': 'rating', 'limit': '10', 'page': '1'}),
|
||||
]);
|
||||
|
||||
List<NovelModel> parseItems(dynamic res) {
|
||||
final data = res.data as Map<String, dynamic>;
|
||||
return (data['items'] as List)
|
||||
.map((e) => NovelModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList();
|
||||
List<NovelModel> parseItems(Response<dynamic> res, String feedName) {
|
||||
final raw = res.data;
|
||||
if (raw is! Map<String, dynamic>) {
|
||||
throw FormatException('Feed $feedName response is not a JSON object: ${raw.runtimeType}');
|
||||
}
|
||||
|
||||
final rawItems = raw['items'];
|
||||
if (rawItems is! List) {
|
||||
throw FormatException('Feed $feedName missing items list');
|
||||
}
|
||||
|
||||
final parsed = <NovelModel>[];
|
||||
for (var i = 0; i < rawItems.length; i++) {
|
||||
final item = rawItems[i];
|
||||
if (item is! Map<String, dynamic>) {
|
||||
debugPrint('[HOME][SKIP] $feedName item#$i has invalid type: ${item.runtimeType}');
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
parsed.add(NovelModel.fromJson(item));
|
||||
} catch (e) {
|
||||
final id = item['id'];
|
||||
debugPrint('[HOME][SKIP] $feedName item#$i id=$id parse failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('[HOME] $feedName parsed ${parsed.length}/${rawItems.length} items');
|
||||
return parsed;
|
||||
}
|
||||
|
||||
return HomeData(
|
||||
hot: parseItems(results[0]),
|
||||
latest: parseItems(results[1]),
|
||||
topRated: parseItems(results[2]),
|
||||
hot: parseItems(results[0], 'popular'),
|
||||
latest: parseItems(results[1], 'latest'),
|
||||
topRated: parseItems(results[2], 'rating'),
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user