diff --git a/.gitignore b/.gitignore index 14f443b..1c5017b 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ app.*.map.json /android/app/release .vscode/settings.json -.vscode/PythonImportHelper-v2-Completion.json \ No newline at end of file +.vscode/PythonImportHelper-v2-Completion.json +.dockerrun \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 52fa884..52baf6c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -30,8 +30,8 @@ if (keystorePropertiesFile.exists()) { android { namespace "com.teen.dailyanimelist" - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + compileSdk 34 + ndkVersion = "26.1.10909125" signingConfigs { release { @@ -43,6 +43,8 @@ android { } compileOptions { + coreLibraryDesugaringEnabled true + // Sets Java compatibility to Java 8 sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -58,12 +60,13 @@ android { defaultConfig { applicationId "io.github.jica98" minSdkVersion 21 - targetSdkVersion flutter.targetSdkVersion + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName manifestPlaceholders = [ 'appAuthRedirectScheme': 'com.teen.dailyanimelist' ] + multiDexEnabled = true } buildTypes { @@ -75,6 +78,11 @@ android { } dependencies { implementation 'com.android.support:multidex:1.0.3' + implementation 'com.google.errorprone:error_prone_annotations:2.23.0' + implementation 'javax.annotation:javax.annotation-api:1.3.2' + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2' + implementation 'androidx.window:window:1.0.0' + implementation 'androidx.window:window-java:1.0.0' } lint { disable 'InvalidPackage' diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 0000000..f31c3f1 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,37 @@ + ## Flutter wrapper + -keep class io.flutter.app.** { *; } + -keep class io.flutter.plugin.** { *; } + -keep class io.flutter.util.** { *; } + -keep class io.flutter.view.** { *; } + -keep class io.flutter.** { *; } + -keep class io.flutter.plugins.** { *; } + -keep class com.google.firebase.** { *; } + -dontwarn io.flutter.embedding.** + -ignorewarnings +## Gson rules +# Gson uses generic type information stored in a class file when working with fields. Proguard +# removes such information by default, so configure it to keep all of it. +-keepattributes Signature + +# For using GSON @Expose annotation +-keepattributes *Annotation* + +# Gson specific classes +-dontwarn sun.misc.** +#-keep class com.google.gson.stream.** { *; } + +# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, +# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) +-keep class * extends com.google.gson.TypeAdapter +-keep class * implements com.google.gson.TypeAdapterFactory +-keep class * implements com.google.gson.JsonSerializer +-keep class * implements com.google.gson.JsonDeserializer + +# Prevent R8 from leaving Data object members always null +-keepclassmembers,allowobfuscation class * { + @com.google.gson.annotations.SerializedName ; +} + +# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. +-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken +-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8860dd0..99f269b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,8 +5,8 @@ + - @@ -15,6 +15,7 @@ diff --git a/android/build.gradle b/android/build.gradle index 802640d..cfc471f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.9.22' + ext.kotlin_version = '1.9.25' repositories { google() mavenCentral() @@ -21,6 +21,17 @@ rootProject.buildDir = '../build' subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } +subprojects { + afterEvaluate { project -> + if (project.hasProperty('android')) { + project.android { + if (namespace == null) { + namespace project.group + } + } + } + } +} subprojects { project.evaluationDependsOn(':app') } diff --git a/android/gradle.properties b/android/gradle.properties index 598d13f..73c3168 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,6 @@ org.gradle.jvmargs=-Xmx4G android.useAndroidX=true android.enableJetifier=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index cc5527d..628d33c 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 7cd7128..ab1354d 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -23,7 +23,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false + id "com.android.application" version '8.4.0' apply false } include ":app" diff --git a/assets/images/gemini.png b/assets/images/gemini.png new file mode 100644 index 0000000..b0ef8d6 Binary files /dev/null and b/assets/images/gemini.png differ diff --git a/dailyal_support/pubspec.lock b/dailyal_support/pubspec.lock index 8ed92c3..d8010e1 100644 --- a/dailyal_support/pubspec.lock +++ b/dailyal_support/pubspec.lock @@ -53,18 +53,18 @@ packages: dependency: "direct main" description: name: dio - sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 + sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0" url: "https://pub.dev" source: hosted - version: "5.5.0+1" + version: "5.6.0" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" + sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" fake_async: dependency: transitive description: @@ -108,26 +108,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" line_icons: dependency: "direct main" description: @@ -156,18 +156,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" path: dependency: transitive description: @@ -233,10 +233,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.2" typed_data: dependency: transitive description: @@ -321,10 +321,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.4" web: dependency: transitive description: diff --git a/dal-api/dal_commons/lib/src/model/html/anime/animereviewhtml.dart b/dal-api/dal_commons/lib/src/model/html/anime/animereviewhtml.dart index ee04181..e5f20a2 100644 --- a/dal-api/dal_commons/lib/src/model/html/anime/animereviewhtml.dart +++ b/dal-api/dal_commons/lib/src/model/html/anime/animereviewhtml.dart @@ -11,16 +11,65 @@ final reactionBoxes = [ "Creative" ]; +class ContentReviewSummary with ToJson { + final List pros; + final List cons; + final String verdict; + ContentReviewSummary(this.pros, this.cons, this.verdict); + + factory ContentReviewSummary.fromJson(Map? json) { + return json != null + ? ContentReviewSummary( + (json['pros'] as List) + .map((e) => ReviewItem.fromJson(e)) + .toList(), + (json['cons'] as List) + .map((e) => ReviewItem.fromJson(e)) + .toList(), + json['verdict']) + : ContentReviewSummary([], [], ''); + } + + @override + Map toJson() { + return { + "pros": pros.map((e) => e.toJson()).toList(), + "cons": cons.map((e) => e.toJson()).toList(), + "verdict": verdict + }; + } +} + +class ReviewItem with ToJson { + final String title; + final String description; + ReviewItem({required this.title, required this.description}); + + @override + Map toJson() { + return { + "title": title, + "description": description + }; + } + + factory ReviewItem.fromJson(Map? json) { + return json != null + ? ReviewItem(title: json['title'], description: json['description']) + : ReviewItem(title: '', description: ''); + } +} + class AnimeReviewHtml with ToJson { - final String ?userName; - final String ?timeAdded; - final String ?userPicture; - final String ?overallRating; - final String ?reviewText; - final List ?tags; - final List ?reactionBox; - bool ?fromCache; - final dal_node.Node ?relatedNode; + final String? userName; + final String? timeAdded; + final String? userPicture; + final String? overallRating; + final String? reviewText; + final List? tags; + final List? reactionBox; + bool? fromCache; + final dal_node.Node? relatedNode; AnimeReviewHtml({ this.userName, diff --git a/dal-api/dal_commons/lib/src/model/servers.dart b/dal-api/dal_commons/lib/src/model/servers.dart index 1a32456..87b7826 100644 --- a/dal-api/dal_commons/lib/src/model/servers.dart +++ b/dal-api/dal_commons/lib/src/model/servers.dart @@ -9,6 +9,7 @@ class Servers { String? dalAPIUrl; String? telegramLink; String? storeUrl; + Map? featureFlags; List? platformMaintenances; Servers({ @@ -23,6 +24,7 @@ class Servers { this.errorLogging, this.maxLoad, this.platformMaintenances, + this.featureFlags, }); Servers.fromJson(Map? json) { @@ -42,6 +44,12 @@ class Servers { platformMaintenances?.add(PlatformMaintenances.fromJson(v)); }); } + if (json['featureFlags'] != null) { + featureFlags = {}; + json['featureFlags'].forEach((k, v) { + featureFlags![k] = v; + }); + } if (json['preferredServers'] != null) { preferredServers = []; json['preferredServers'].forEach((v) { @@ -63,6 +71,7 @@ class Servers { data['dalAPIUrl'] = dalAPIUrl; data['telegramLink'] = telegramLink; data['storeUrl'] = storeUrl; + data['featureFlags'] = featureFlags; data['platformMaintenances'] = platformMaintenances?.map((v) => v.toJson()).toList(); return data; diff --git a/dal-api/pubspec.lock b/dal-api/pubspec.lock index daa4f57..85a4551 100644 --- a/dal-api/pubspec.lock +++ b/dal-api/pubspec.lock @@ -268,10 +268,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -556,10 +556,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.4" + version: "14.2.5" watcher: dependency: transitive description: diff --git a/dal-api/pubspec.yaml b/dal-api/pubspec.yaml index c256d05..f98d1fb 100644 --- a/dal-api/pubspec.yaml +++ b/dal-api/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: shelf: ^1.3.2 - intl: ^0.18.1 + intl: ^0.19.0 html: ^0.15.0 html_unescape: ^2.0.0 retry: ^3.1.0 diff --git a/dal-rust-api/Cargo.toml b/dal-rust-api/Cargo.toml index 1a07f9a..bfd3435 100644 --- a/dal-rust-api/Cargo.toml +++ b/dal-rust-api/Cargo.toml @@ -14,8 +14,8 @@ chrono = "0.4.31" dotenv = "0.15.0" jsonwebtoken = "9.2.0" rand_core = "0.6.4" -redis = { version = "0.24.0", features = ["tokio-comp", "json", "aio"] } -reqwest = { version = "0.11.24", features = ["json"] } +redis = { version = "0.26.1", features = ["tokio-comp", "json", "aio"] } +reqwest = { version = "0.12.5", features = ["json"] } serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" time = "0.3.31" @@ -23,6 +23,8 @@ futures = "0.3" tokio = { version = "1.35.1", features = ["full"] } tower-http = { version = "0.5.0", features = ["cors"] } uuid = { version = "1.6.1", features = ["serde", "v4"] } +seahash = "4.1.0" +openssl = { version = "0.10.59", features = ["vendored"] } [profile.release] overflow-checks = true diff --git a/dal-rust-api/Dockerfile b/dal-rust-api/Dockerfile index 1c4de43..321890f 100644 --- a/dal-rust-api/Dockerfile +++ b/dal-rust-api/Dockerfile @@ -4,6 +4,8 @@ WORKDIR /builder COPY . . +RUN cargo update -p jsonwebtoken --precise 9.2.0 + RUN cargo build -p dal-rust-api --release FROM gcr.io/distroless/cc diff --git a/dal-rust-api/src/anime_service.rs b/dal-rust-api/src/anime_service.rs index 7d0510c..091e7ff 100644 --- a/dal-rust-api/src/anime_service.rs +++ b/dal-rust-api/src/anime_service.rs @@ -1,179 +1,227 @@ -use std::collections::HashSet; -use std::error::Error; -use std::sync::{Arc, Mutex}; - -use crate::model::{Anime, Edge, RelatedAnime, RelationType}; - -use crate::config::Config; -use crate::model_dto::{ContentGraphDTO, ContentNodeDTO}; -use async_recursion::async_recursion; -use chrono::{DateTime, Utc}; -use futures::{stream, StreamExt}; - -pub struct AnimeService { - pub config: Config, - pub mal_api: crate::mal_api::MalAPI, - pub cache_service: crate::cache_service::CacheService, -} - -impl AnimeService { - pub async fn get_related_anime(&self, id: i64) -> Result> { - let mut graph = ContentGraphDTO { - nodes: HashSet::new(), - edges: Vec::new(), - }; - self.get_related_anime_with_graph(id, &mut graph, false, true) - .await?; - return Ok(graph); - } - - #[async_recursion] - pub async fn get_related_anime_with_graph( - &self, - id: i64, - graph: &mut ContentGraphDTO, - from_cache: bool, - include_others: bool, - ) -> Result<(), Box> { - let anime = self.get_anime_by_id(id, from_cache).await.unwrap(); - graph.nodes.insert(anime.clone().into()); - - let unvisited_edges = self.filter_by_nodes( - self.get_unvisited_edges(id, anime.related_anime.clone(), include_others), - &graph.nodes, - ); - - let combined_edges: Arc>> = Arc::new(Mutex::new(Vec::new())); - let combined_anime: Arc>> = Arc::new(Mutex::new(Vec::new())); - - stream::iter(unvisited_edges.clone()) - .for_each_concurrent(5, |edge| { - let combined_edges = Arc::clone(&combined_edges); - let combined_anime = Arc::clone(&combined_anime); - async move { - let (anime, edges_from_id) = self.get_edges_from_id(edge.target).await; - let mut combined_edges = combined_edges.lock().unwrap(); - combined_edges.extend(edges_from_id); - combined_anime.lock().unwrap().push(anime.clone().into()); - } - }) - .await; - - graph - .edges - .extend(unvisited_edges.iter().map(|e| e.clone().into())); - graph - .nodes - .extend(combined_anime.lock().unwrap().clone().into_iter()); - let filter_by_nodes = - self.filter_by_nodes(combined_edges.lock().unwrap().clone(), &graph.nodes); - - for edge in filter_by_nodes.iter() { - graph.edges.push(edge.clone().into()); - let _ = self - .get_related_anime_with_graph(edge.target, graph, true, false) - .await; - } - return Ok(()); - } - - async fn get_edges_from_id(&self, id: i64) -> (Anime, Vec) { - let anime = self.get_anime_by_id(id, true).await.unwrap(); - let vec = anime.related_anime.clone(); - (anime, self.get_unvisited_edges(id, vec, false)) - } - - fn get_unvisited_edges(&self, id: i64, related_anime: Option>, include_others: bool) -> Vec { - let mut unvisited_edges: Vec = Vec::new(); - if related_anime.is_some() { - unvisited_edges.extend( - related_anime - .unwrap() - .iter() - .filter(|related_anime| self.valid_relation(&related_anime.relation_type, include_others)) - .map(|related_anime| Edge { - source: id, - target: related_anime.node.id, - relation_type: related_anime.relation_type.clone(), - }) - .collect::>(), - ); - } - unvisited_edges - } - - fn filter_by_nodes(&self, edges: Vec, nodes: &HashSet) -> Vec { - edges - .iter() - .filter(|edge| { - !nodes.contains(&ContentNodeDTO { - id: edge.target, - ..Default::default() - }) - }) - .map(|e| e.clone()) - .collect() - } - - fn valid_relation(&self, relation_type: &RelationType, include_others: bool) -> bool { - match relation_type { - RelationType::alternative_setting => true, - RelationType::sequel => true, - RelationType::prequel => true, - RelationType::alternative_version => true, - RelationType::side_story => true, - RelationType::parent_story => true, - RelationType::summary => true, - RelationType::full_story => true, - RelationType::spin_off => true, - RelationType::character => false, - RelationType::other => include_others, - } - } - - async fn get_anime_by_id(&self, id: i64, from_cache: bool) -> Result> { - let now = chrono::Utc::now(); - let result = match from_cache { - true => self.cache_service.get_by_id("anime", id.to_string()).await, - false => None, - }; - - if result.is_none() { - println!( - "{}: Cache miss anime: {}", - now.format("%d/%m/%Y %H:%M:%S"), - id - ); - // If the anime is not in the cache, get it from the MAL API - let anime = self.mal_api.get_anime_details(id).await?; - - // Store the anime in the cache for future use - self.cache_service.set_by_id("anime", id.to_string(), &anime, None).await; - let then = chrono::Utc::now(); - self.log_anime(&anime, "Saved".to_string(), then, now); - Ok(anime) - } else { - let anime = result.unwrap(); - let then = chrono::Utc::now(); - self.log_anime(&anime, "Cache hit".to_string(), then, now); - Ok(anime) - } - } - - fn log_anime( - &self, - anime: &Anime, - hit_or_miss: String, - then: DateTime, - now: DateTime, - ) { - println!( - "{}: {} anime: {} and {} in {}ms", - then.format("%d/%m/%Y %H:%M:%S"), - hit_or_miss, - anime.id, - anime.title, - then.timestamp_millis() - now.timestamp_millis() - ); - } -} +use seahash::SeaHasher; +use std::collections::HashSet; +use std::error::Error; +use std::hash::Hasher; +use std::sync::{Arc, Mutex}; + +use crate::model::{Anime, Edge, RelatedAnime, RelationType, ReviewResponse}; + +use crate::config::Config; +use crate::model_dto::{ContentGraphDTO, ContentNodeDTO}; +use async_recursion::async_recursion; +use chrono::{DateTime, Utc}; +use futures::{stream, StreamExt}; + +const REVIEW_SYSTEM: &str = "You are an anime review critic, you are given the task to go through all the anime reviews and provide a review under 500 words. Split it into 3-4 Pros and Cons and a final Verdict. No need for any intro. Each pros/cons should be descriptive along with a concise title for it. Output should be in the format { pros: [ { title, description }, cons: [ { title, description } ], verdict }"; + +pub struct AnimeService { + pub config: Config, + pub mal_api: crate::mal_api::MalAPI, + pub cache_service: crate::cache_service::CacheService, + pub ai_service: crate::gemini_api::GeminiAPI, +} + +impl AnimeService { + pub async fn get_related_anime(&self, id: i64) -> Result> { + let mut graph = ContentGraphDTO { + nodes: HashSet::new(), + edges: Vec::new(), + }; + self.get_related_anime_with_graph(id, &mut graph, false, true) + .await?; + return Ok(graph); + } + + #[async_recursion] + pub async fn get_related_anime_with_graph( + &self, + id: i64, + graph: &mut ContentGraphDTO, + from_cache: bool, + include_others: bool, + ) -> Result<(), Box> { + let anime = self.get_anime_by_id(id, from_cache).await.unwrap(); + graph.nodes.insert(anime.clone().into()); + + let unvisited_edges = self.filter_by_nodes( + self.get_unvisited_edges(id, anime.related_anime.clone(), include_others), + &graph.nodes, + ); + + let combined_edges: Arc>> = Arc::new(Mutex::new(Vec::new())); + let combined_anime: Arc>> = Arc::new(Mutex::new(Vec::new())); + + stream::iter(unvisited_edges.clone()) + .for_each_concurrent(5, |edge| { + let combined_edges = Arc::clone(&combined_edges); + let combined_anime = Arc::clone(&combined_anime); + async move { + let (anime, edges_from_id) = self.get_edges_from_id(edge.target).await; + let mut combined_edges = combined_edges.lock().unwrap(); + combined_edges.extend(edges_from_id); + combined_anime.lock().unwrap().push(anime.clone().into()); + } + }) + .await; + + graph + .edges + .extend(unvisited_edges.iter().map(|e| e.clone().into())); + graph + .nodes + .extend(combined_anime.lock().unwrap().clone().into_iter()); + let filter_by_nodes = + self.filter_by_nodes(combined_edges.lock().unwrap().clone(), &graph.nodes); + + for edge in filter_by_nodes.iter() { + graph.edges.push(edge.clone().into()); + let _ = self + .get_related_anime_with_graph(edge.target, graph, true, false) + .await; + } + return Ok(()); + } + + async fn get_edges_from_id(&self, id: i64) -> (Anime, Vec) { + let anime = self.get_anime_by_id(id, true).await.unwrap(); + let vec = anime.related_anime.clone(); + (anime, self.get_unvisited_edges(id, vec, false)) + } + + fn get_unvisited_edges( + &self, + id: i64, + related_anime: Option>, + include_others: bool, + ) -> Vec { + let mut unvisited_edges: Vec = Vec::new(); + if related_anime.is_some() { + unvisited_edges.extend( + related_anime + .unwrap() + .iter() + .filter(|related_anime| { + self.valid_relation(&related_anime.relation_type, include_others) + }) + .map(|related_anime| Edge { + source: id, + target: related_anime.node.id, + relation_type: related_anime.relation_type.clone(), + }) + .collect::>(), + ); + } + unvisited_edges + } + + fn filter_by_nodes(&self, edges: Vec, nodes: &HashSet) -> Vec { + edges + .iter() + .filter(|edge| { + !nodes.contains(&ContentNodeDTO { + id: edge.target, + ..Default::default() + }) + }) + .map(|e| e.clone()) + .collect() + } + + fn valid_relation(&self, relation_type: &RelationType, include_others: bool) -> bool { + match relation_type { + RelationType::alternative_setting => true, + RelationType::sequel => true, + RelationType::prequel => true, + RelationType::alternative_version => true, + RelationType::side_story => true, + RelationType::parent_story => true, + RelationType::summary => true, + RelationType::full_story => true, + RelationType::spin_off => true, + RelationType::character => false, + RelationType::other => include_others, + } + } + + async fn get_anime_by_id(&self, id: i64, from_cache: bool) -> Result> { + let now = chrono::Utc::now(); + let result = match from_cache { + true => self.cache_service.get_by_id("anime", id.to_string()).await, + false => None, + }; + + if result.is_none() { + println!( + "{}: Cache miss anime: {}", + now.format("%d/%m/%Y %H:%M:%S"), + id + ); + // If the anime is not in the cache, get it from the MAL API + let anime = self.mal_api.get_anime_details(id).await?; + + // Store the anime in the cache for future use + self.cache_service + .set_by_id("anime", id.to_string(), &anime, None) + .await; + let then = chrono::Utc::now(); + self.log_anime(&anime, "Saved".to_string(), then, now); + Ok(anime) + } else { + let anime = result.unwrap(); + let then = chrono::Utc::now(); + self.log_anime(&anime, "Cache hit".to_string(), then, now); + Ok(anime) + } + } + + fn log_anime( + &self, + anime: &Anime, + hit_or_miss: String, + then: DateTime, + now: DateTime, + ) { + println!( + "{}: {} anime: {} and {} in {}ms", + then.format("%d/%m/%Y %H:%M:%S"), + hit_or_miss, + anime.id, + anime.title, + then.timestamp_millis() - now.timestamp_millis() + ); + } + + pub fn hash_str(&self, s: &str) -> String { + let mut hasher = SeaHasher::new(); + hasher.write(s.as_bytes()); + let finish = hasher.finish(); + format!("{:x}", finish) + } + + pub async fn summarize_review(&self, reviews: &str) -> Result { + println!("Summarizing review {}", reviews.len()); + + let hash_str = self.hash_str(reviews); + + println!("Using hash_key: {}", hash_str); + + let cached_review: Option = + self.cache_service.get_by_id("reviews", hash_str.clone()).await; + + if cached_review.is_some() { + return Ok(cached_review.unwrap()); + } else { + println!("Cache miss for {}", hash_str); + let review_response: ReviewResponse = self + .ai_service + .talk(REVIEW_SYSTEM, reviews) + .await + .map(|text| serde_json::from_str(&text).unwrap()).unwrap(); + + self.cache_service + .set_by_id("reviews", hash_str, &review_response, Some(3600 * 24 * 30)) + .await; + return Ok(review_response); + } + } +} diff --git a/dal-rust-api/src/cache_service.rs b/dal-rust-api/src/cache_service.rs index 04496a5..5ea2324 100644 --- a/dal-rust-api/src/cache_service.rs +++ b/dal-rust-api/src/cache_service.rs @@ -8,9 +8,9 @@ pub struct CacheService { } impl CacheService { - async fn get_connection(&self) -> redis::aio::Connection { + async fn get_connection(&self) -> redis::aio::MultiplexedConnection { let client = redis::Client::open(self.config.secrets.rediscloud_url.to_string()).unwrap(); - return client.get_async_connection().await.unwrap(); + return client.get_multiplexed_async_connection().await.unwrap(); } pub async fn get_by_id( diff --git a/dal-rust-api/src/config.rs b/dal-rust-api/src/config.rs index 29d68c6..2469258 100644 --- a/dal-rust-api/src/config.rs +++ b/dal-rust-api/src/config.rs @@ -5,6 +5,7 @@ pub struct Secrets { pub rediscloud_url: String, pub subastorage_url: String, pub subastorage_key: String, + pub gemini_api_key: String, } #[derive(Debug, Clone)] @@ -21,12 +22,14 @@ impl Config { let rediscloud_url = std::env::var("REDISCLOUD_URL").expect("missing REDISCLOUD_URL"); let subastorage_url = std::env::var("SUPABASE_URL_STORAGE").expect("missing SUPABASE_URL_STORAGE"); let subastorage_key = std::env::var("SUPABASE_API_KEY").expect("missing SUPABASE_API_KEY"); + let gemini_api_key = std::env::var("GEMINI_API_KEY").expect("missing GEMINI_API_KEY"); let secrets = Secrets { mal_client_id, bearer_secret, rediscloud_url, subastorage_url, - subastorage_key + subastorage_key, + gemini_api_key }; Config { secrets, base_url } diff --git a/dal-rust-api/src/gemini_api.rs b/dal-rust-api/src/gemini_api.rs new file mode 100644 index 0000000..803b743 --- /dev/null +++ b/dal-rust-api/src/gemini_api.rs @@ -0,0 +1,90 @@ +use serde_json::json; + +use crate::{ + config::Config, + model::GeminiReponse, +}; + +const GEMINI_API_URL: &str = + "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key="; + +#[derive(Debug, Clone)] +pub struct GeminiAPI { + pub config: Config, +} + +impl GeminiAPI { + fn review_request(&self, system: &str, text: &str) -> serde_json::Value { + json!({ + "system_instruction": { + "parts": { + "text": system + } + }, + "contents": [ + { + "parts": [ + { + "text": text + } + ] + } + ], + "safetySettings": [ + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "BLOCK_NONE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "BLOCK_NONE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "BLOCK_NONE" + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "BLOCK_NONE" + } + ], + "generationConfig": { + "response_mime_type": "application/json" + } + }) + } + + pub async fn talk(&self, system: &str, prompt: &str) -> Result { + let gemini_api_key = self.config.secrets.gemini_api_key.as_str(); + let gemini_api_url = format!("{}{}", GEMINI_API_URL, gemini_api_key); + println!("Calling Gemini API with url"); + + let value = self.review_request(&system, &prompt); + + let text = reqwest::Client::new() + .post(gemini_api_url) + .json(&value) + .header("Content-Type", "application/json") + .send() + .await? + .text() + .await?; + + println!("Response from Gemini API"); + + let response: GeminiReponse = serde_json::from_str(&text).unwrap(); + + let text = response + .candidates + .first() + .unwrap() + .content + .parts + .first() + .unwrap() + .text + .clone(); + + Ok(text) + } +} diff --git a/dal-rust-api/src/handlers.rs b/dal-rust-api/src/handlers.rs index 8eb2bc3..54cbe0b 100644 --- a/dal-rust-api/src/handlers.rs +++ b/dal-rust-api/src/handlers.rs @@ -2,12 +2,11 @@ use core::panic; use std::sync::Arc; use crate::{ - file_storage_service::SignedURLResponse, model::File, model_dto::ContentGraphDTO, AppState, + file_storage_service::SignedURLResponse, model::{File, ReviewResponse}, model_dto::ContentGraphDTO, AppState, }; use axum::{ - extract::{Multipart, Path, State}, - Json, + extract::{Multipart, Path, State}, Json }; /// A function to handle GET requests at /anime/{id}/related @@ -48,6 +47,13 @@ pub async fn delete_image( "ok".to_string() } +pub async fn get_review_summary( + State(data): State>, + body: String +) -> Json { + Json(data.anime_service.summarize_review(body.as_str()).await.unwrap()) +} + async fn field_to_file(field: axum::extract::multipart::Field<'_>) -> File { let content_type = field.content_type().unwrap().to_string(); let file_name = field.file_name().unwrap().to_string(); diff --git a/dal-rust-api/src/main.rs b/dal-rust-api/src/main.rs index ea77196..aa06cc3 100644 --- a/dal-rust-api/src/main.rs +++ b/dal-rust-api/src/main.rs @@ -13,6 +13,7 @@ mod model_dto; mod cache_service; mod file_storage_service; mod image_service; +mod gemini_api; pub struct AppState { pub config: Config, @@ -27,7 +28,10 @@ async fn main() { let config = Config::init(); let app = routes::setup_app(config).await; - println!("Server started at http://localhost:8001"); - let listener = tokio::net::TcpListener::bind("0.0.0.0:8001").await.unwrap(); + let port = std::env::var("PORT").unwrap_or_else(|_| "8001".to_string()); + let addr = format!("0.0.0.0:{}", port); + + println!("Server started at http://{}", addr); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); axum::serve(listener, app).await.unwrap(); } diff --git a/dal-rust-api/src/model.rs b/dal-rust-api/src/model.rs index 6c83fd5..f4b755e 100644 --- a/dal-rust-api/src/model.rs +++ b/dal-rust-api/src/model.rs @@ -1,74 +1,107 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Anime { - pub id: i64, - pub title: String, - pub main_picture: Option, - pub mean: Option, - pub media_type: Option, - pub status: Option, - pub start_season: Option, - pub related_anime: Option>, - pub alternative_titles: Option -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct AlternateTitles { - pub en: Option, - pub ja: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct MainPicture { - pub medium: String, - pub large: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct RelatedAnime { - pub node: Node, - pub relation_type: RelationType, - pub relation_type_formatted: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Node { - pub id: i64, - pub title: String, - pub main_picture: MainPicture, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Season { - pub year: i64, - pub season: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Edge { - pub source: i64, - pub target: i64, - pub relation_type: RelationType, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum RelationType { - sequel, - prequel, - alternative_setting, - alternative_version, - side_story, - parent_story, - summary, - full_story, - spin_off, - character, - other, -} - -pub struct File { - pub content: Vec, - pub content_type: String, - pub file_name: String, -} \ No newline at end of file +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Anime { + pub id: i64, + pub title: String, + pub main_picture: Option, + pub mean: Option, + pub media_type: Option, + pub status: Option, + pub start_season: Option, + pub related_anime: Option>, + pub alternative_titles: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AlternateTitles { + pub en: Option, + pub ja: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct MainPicture { + pub medium: String, + pub large: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RelatedAnime { + pub node: Node, + pub relation_type: RelationType, + pub relation_type_formatted: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Node { + pub id: i64, + pub title: String, + pub main_picture: MainPicture, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Season { + pub year: i64, + pub season: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Edge { + pub source: i64, + pub target: i64, + pub relation_type: RelationType, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum RelationType { + sequel, + prequel, + alternative_setting, + alternative_version, + side_story, + parent_story, + summary, + full_story, + spin_off, + character, + other, +} + +pub struct File { + pub content: Vec, + pub content_type: String, + pub file_name: String, +} + +#[derive(Clone, Deserialize, Serialize, Debug)] +pub struct ReviewResponse { + pros: Vec, + cons: Vec, + verdict: String, +} + +#[derive(Clone, Deserialize, Serialize, Debug)] +pub struct ReviewItem { + title: String, + description: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GeminiReponse { + pub candidates: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Candidates { + pub content: Content, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Content { + pub parts: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Parts { + pub text: String, +} diff --git a/dal-rust-api/src/model_dto.rs b/dal-rust-api/src/model_dto.rs index 9bbfda3..0579a43 100644 --- a/dal-rust-api/src/model_dto.rs +++ b/dal-rust-api/src/model_dto.rs @@ -1,151 +1,151 @@ -use std::{ - collections::HashSet, - hash::{Hash, Hasher}, -}; - -use serde::{Deserialize, Serialize}; - -use crate::model::Anime; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ContentGraphDTO { - pub nodes: HashSet, - pub edges: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct ContentNodeDTO { - pub id: i64, - pub title: String, - pub main_picture: Option, - pub mean: Option, - pub media_type: Option, - pub status: Option, - pub start_season: Option, - pub alternative_titles: Option, -} - -impl Eq for ContentNodeDTO {} - -impl PartialEq for ContentNodeDTO { - fn eq(&self, other: &Self) -> bool { - self.id == other.id - } -} - -impl Hash for ContentNodeDTO { - fn hash(&self, state: &mut H) { - self.id.hash(state); - } -} - -impl From for ContentNodeDTO { - fn from(anime: Anime) -> Self { - ContentNodeDTO { - id: anime.id, - title: anime.title, - main_picture: anime.main_picture.map(|main_picture| main_picture.into()), - mean: anime.mean, - media_type: anime.media_type, - status: anime.status, - start_season: anime.start_season.map(|season| season.into()), - alternative_titles: anime.alternative_titles.map(|alternate_titles| alternate_titles.into()), - } - } -} - - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct AlternateTitlesDTO { - pub en: Option, - pub ja: Option, -} - -impl From for AlternateTitlesDTO { - fn from(alternate_titles: crate::model::AlternateTitles) -> Self { - AlternateTitlesDTO { - en: alternate_titles.en, - ja: alternate_titles.ja, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct EdgeDTO { - pub source: i64, - pub target: i64, - pub relation_type: RelationTypeDTO, -} - -impl From for EdgeDTO { - fn from(edge: crate::model::Edge) -> Self { - EdgeDTO { - source: edge.source, - target: edge.target, - relation_type: edge.relation_type.into(), - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, Default)] -pub struct MainPictureDTO { - pub medium: String, - pub large: String, -} - -impl From for MainPictureDTO { - fn from(main_picture: crate::model::MainPicture) -> Self { - MainPictureDTO { - medium: main_picture.medium, - large: main_picture.large, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SeasonDTO { - pub year: i64, - pub season: String, -} - -impl From for SeasonDTO { - fn from(season: crate::model::Season) -> Self { - SeasonDTO { - year: season.year, - season: season.season, - } - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum RelationTypeDTO { - sequel, - prequel, - alternative_setting, - alternative_version, - side_story, - parent_story, - summary, - full_story, - spin_off, - character, - other, -} - -impl From for RelationTypeDTO { - fn from(relation_type: crate::model::RelationType) -> Self { - match relation_type { - crate::model::RelationType::sequel => RelationTypeDTO::sequel, - crate::model::RelationType::prequel => RelationTypeDTO::prequel, - crate::model::RelationType::alternative_setting => RelationTypeDTO::alternative_setting, - crate::model::RelationType::alternative_version => RelationTypeDTO::alternative_version, - crate::model::RelationType::side_story => RelationTypeDTO::side_story, - crate::model::RelationType::parent_story => RelationTypeDTO::parent_story, - crate::model::RelationType::summary => RelationTypeDTO::summary, - crate::model::RelationType::full_story => RelationTypeDTO::full_story, - crate::model::RelationType::spin_off => RelationTypeDTO::spin_off, - crate::model::RelationType::character => RelationTypeDTO::character, - crate::model::RelationType::other => RelationTypeDTO::other, - } - } -} +use std::{ + collections::HashSet, + hash::{Hash, Hasher}, +}; + +use serde::{Deserialize, Serialize}; + +use crate::model::Anime; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ContentGraphDTO { + pub nodes: HashSet, + pub edges: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct ContentNodeDTO { + pub id: i64, + pub title: String, + pub main_picture: Option, + pub mean: Option, + pub media_type: Option, + pub status: Option, + pub start_season: Option, + pub alternative_titles: Option, +} + +impl Eq for ContentNodeDTO {} + +impl PartialEq for ContentNodeDTO { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Hash for ContentNodeDTO { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } +} + +impl From for ContentNodeDTO { + fn from(anime: Anime) -> Self { + ContentNodeDTO { + id: anime.id, + title: anime.title, + main_picture: anime.main_picture.map(|main_picture| main_picture.into()), + mean: anime.mean, + media_type: anime.media_type, + status: anime.status, + start_season: anime.start_season.map(|season| season.into()), + alternative_titles: anime.alternative_titles.map(|alternate_titles| alternate_titles.into()), + } + } +} + + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AlternateTitlesDTO { + pub en: Option, + pub ja: Option, +} + +impl From for AlternateTitlesDTO { + fn from(alternate_titles: crate::model::AlternateTitles) -> Self { + AlternateTitlesDTO { + en: alternate_titles.en, + ja: alternate_titles.ja, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EdgeDTO { + pub source: i64, + pub target: i64, + pub relation_type: RelationTypeDTO, +} + +impl From for EdgeDTO { + fn from(edge: crate::model::Edge) -> Self { + EdgeDTO { + source: edge.source, + target: edge.target, + relation_type: edge.relation_type.into(), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct MainPictureDTO { + pub medium: String, + pub large: String, +} + +impl From for MainPictureDTO { + fn from(main_picture: crate::model::MainPicture) -> Self { + MainPictureDTO { + medium: main_picture.medium, + large: main_picture.large, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SeasonDTO { + pub year: i64, + pub season: String, +} + +impl From for SeasonDTO { + fn from(season: crate::model::Season) -> Self { + SeasonDTO { + year: season.year, + season: season.season, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum RelationTypeDTO { + sequel, + prequel, + alternative_setting, + alternative_version, + side_story, + parent_story, + summary, + full_story, + spin_off, + character, + other, +} + +impl From for RelationTypeDTO { + fn from(relation_type: crate::model::RelationType) -> Self { + match relation_type { + crate::model::RelationType::sequel => RelationTypeDTO::sequel, + crate::model::RelationType::prequel => RelationTypeDTO::prequel, + crate::model::RelationType::alternative_setting => RelationTypeDTO::alternative_setting, + crate::model::RelationType::alternative_version => RelationTypeDTO::alternative_version, + crate::model::RelationType::side_story => RelationTypeDTO::side_story, + crate::model::RelationType::parent_story => RelationTypeDTO::parent_story, + crate::model::RelationType::summary => RelationTypeDTO::summary, + crate::model::RelationType::full_story => RelationTypeDTO::full_story, + crate::model::RelationType::spin_off => RelationTypeDTO::spin_off, + crate::model::RelationType::character => RelationTypeDTO::character, + crate::model::RelationType::other => RelationTypeDTO::other, + } + } +} diff --git a/dal-rust-api/src/routes.rs b/dal-rust-api/src/routes.rs index 34fe44f..0b4b9a2 100644 --- a/dal-rust-api/src/routes.rs +++ b/dal-rust-api/src/routes.rs @@ -35,6 +35,9 @@ pub async fn setup_app(config: Config) -> Router { config: config.clone(), mal_api, cache_service, + ai_service: crate::gemini_api::GeminiAPI { + config: config.clone(), + }, }, }); Router::new() @@ -51,6 +54,10 @@ pub async fn setup_app(config: Config) -> Router { "/types/:image_type/images/:image_id", delete(handlers::delete_image), ) + .route( + "/reviews", + post(handlers::get_review_summary), + ) .route_layer(middleware::from_fn_with_state(state.clone(), auth::auth)) .with_state(state) .layer(get_cors_layer()) diff --git a/lib/api/auth/auth.dart b/lib/api/auth/auth.dart index cda6209..cdc4bfa 100644 --- a/lib/api/auth/auth.dart +++ b/lib/api/auth/auth.dart @@ -11,8 +11,6 @@ import 'package:dailyanimelist/widgets/web/c_webview.dart'; import 'package:dal_commons/dal_commons.dart'; import 'package:flutter/cupertino.dart'; import 'package:http/http.dart' as http; -import 'package:uni_links/uni_links.dart'; -// import 'package:uni_links/uni_links.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../constant.dart'; diff --git a/lib/api/dalapi.dart b/lib/api/dalapi.dart index 5eb5803..b512874 100644 --- a/lib/api/dalapi.dart +++ b/lib/api/dalapi.dart @@ -17,6 +17,8 @@ import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:http_parser/src/media_type.dart'; +enum FeatureFlag { aireviews } + class DalApi { static DalApi _internal = DalApi._(); static DalApi i = _internal; @@ -39,6 +41,10 @@ class DalApi { _scheduleForMalIds = _getScheduleForMalIds(); } + void resetScheduleForMalIds() { + _scheduleForMalIds = _getScheduleForMalIds(fromCache: false); + } + Future _getDalConfigFuture() async { final refUrl = '${CredMal.appConfigUrl}/${CredMal.buildVariant}/serverConfigV3${_debugMode ? 'Dev' : ''}.json'; @@ -213,13 +219,16 @@ class DalApi { String type = 'all', SeasonType? season, int? year, + bool fromCache = true, }) async { return ListData.fromJson( - await httpGet('schedules?${buildQueryParams({ - 'type': type, - 'season': season?.name, - 'year': year - })}'), + await httpGet( + 'schedules?${buildQueryParams({ + 'type': type, + 'season': season?.name, + 'year': year + })}', + fromCache), (p0) => ScheduleData.fromJson(p0))?.data ?? []; } @@ -228,12 +237,13 @@ class DalApi { String type = 'all', SeasonType? season, int? year, + bool fromCache = true, }) async { - return HashMap.fromEntries( - (await getSchedules(season: season, type: type, year: year)) - .map((e) => MapEntry(PathUtils.getIdUrl(e.relatedLinks?.mal), e)) - .where((e) => e.key != null) - .map((e) => MapEntry(e.key!, e.value))); + return HashMap.fromEntries((await getSchedules( + season: season, type: type, year: year, fromCache: fromCache)) + .map((e) => MapEntry(PathUtils.getIdUrl(e.relatedLinks?.mal), e)) + .where((e) => e.key != null) + .map((e) => MapEntry(e.key!, e.value))); } Future searchInterestStacks({ @@ -403,13 +413,19 @@ class DalApi { Future _apiGET(String endpoint) async { final apiURL = await _getAPIBaseUrl(); - return MalConnect.getContent('$apiURL/$endpoint', - retryOnFail: false, - withNoHeaders: true, - includeNsfw: false, - headers: { - 'Authorization': 'Bearer ${CredMal.apiSecret}', - }); + return MalConnect.getContent( + '$apiURL/$endpoint', + retryOnFail: false, + withNoHeaders: true, + includeNsfw: false, + headers: _headers, + ); + } + + Map get _headers { + return { + 'Authorization': 'Bearer ${CredMal.apiSecret}', + }; } Future getAnimeGraph(int id) async { @@ -420,6 +436,29 @@ class DalApi { ); } + Future isFeatureEnabled(FeatureFlag flag) { + return _dalConfigFuture.then((value) { + return value?.featureFlags?[flag.name] ?? false; + }); + } + + Future getReviewsSummary(List reviews) async { + try { + final apiURL = '${await _getAPIBaseUrl()}/reviews'; + logDal('Sending reviews to $apiURL'); + final response = await http.post( + Uri.parse(apiURL), + headers: _headers, + body: jsonEncode(reviews), + ); + final body = response.body; + return ContentReviewSummary.fromJson(jsonDecode(body)); + } catch (e) { + logDal(e); + return null; + } + } + Future getSignedImageUrl(String type, String id) async { final response = await _apiGET('types/$type/images/$id'); return response['signedURL']; diff --git a/lib/api/malapi.dart b/lib/api/malapi.dart index c71c016..12514a5 100644 --- a/lib/api/malapi.dart +++ b/lib/api/malapi.dart @@ -1,6 +1,6 @@ import 'dart:collection'; -import 'package:connectivity/connectivity.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dailyanimelist/api/credmal.dart'; import 'package:dailyanimelist/api/dalapi.dart'; import 'package:dailyanimelist/api/malconnect.dart'; @@ -412,9 +412,7 @@ class MalApi { static Future _checkIfDeviceIsConnected() async { try { - final connectivityResult = await (Connectivity().checkConnectivity()); - if (connectivityResult == ConnectivityResult.mobile || - connectivityResult == ConnectivityResult.wifi) { + if (await hasConnection()) { await Dio().get(githubApiLink); return true; } diff --git a/lib/constant.dart b/lib/constant.dart index c8a1879..33cb80d 100644 --- a/lib/constant.dart +++ b/lib/constant.dart @@ -2,7 +2,7 @@ import 'dart:ui' as ui; import 'dart:ui'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:connectivity/connectivity.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dailyanimelist/api/auth/auth.dart'; import 'package:dailyanimelist/api/credmal.dart'; import 'package:dailyanimelist/enums.dart'; @@ -514,9 +514,8 @@ bool shouldUpdateContent( } Future hasConnection() async { - var connectivityResult = await Connectivity().checkConnectivity(); - return connectivityResult == ConnectivityResult.mobile || - connectivityResult == ConnectivityResult.wifi; + final connectivityResult = await Connectivity().checkConnectivity(); + return !connectivityResult.contains(ConnectivityResult.none); } Future showToast(String message, {Toast? toast}) async { @@ -631,7 +630,7 @@ void launchLogOutConfirmation({required BuildContext context}) async { if (result) { try { await MalAuth.signOut(); - restartApp(); + restartApp(context); } catch (e) { logDal(e); showToast(S.current.Couldnt_sign_out_now); diff --git a/lib/generated/intl/messages_ar_EG.dart b/lib/generated/intl/messages_ar_EG.dart index 182b605..539c978 100644 --- a/lib/generated/intl/messages_ar_EG.dart +++ b/lib/generated/intl/messages_ar_EG.dart @@ -210,6 +210,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "هل ترغب في السماح بالإشعارات لهذا التطبيق. يتضمن ذلك إشعارات عند بث حلقة من PTW (خطة المشاهدة) وقائمة المشاهدة."), "Confusing": MessageLookupByLibrary.simpleMessage("مربك"), + "Cons": MessageLookupByLibrary.simpleMessage("سلبيات"), "Content_type": MessageLookupByLibrary.simpleMessage("نوع المحتوى"), "Continue": MessageLookupByLibrary.simpleMessage("استمر؟"), "ContinueW": MessageLookupByLibrary.simpleMessage("يكمل"), @@ -414,6 +415,9 @@ class MessageLookup extends MessageLookupByLibrary { "Gender_desc": MessageLookupByLibrary.simpleMessage("لا تهتم افتراضيا"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("إنشاء مخططات أخرى"), + "Generating": MessageLookupByLibrary.simpleMessage("توليد"), + "Generating_Review_Summary": + MessageLookupByLibrary.simpleMessage("إنشاء ملخص المراجعة"), "Genre": MessageLookupByLibrary.simpleMessage("النوع"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("توزيع النوع"), @@ -818,6 +822,7 @@ class MessageLookup extends MessageLookupByLibrary { "Promo_Videos": MessageLookupByLibrary.simpleMessage("أشرطة الفيديو الترويجي"), "Promotional": MessageLookupByLibrary.simpleMessage("الترويجية"), + "Pros": MessageLookupByLibrary.simpleMessage("الايجابيات"), "Psychological": MessageLookupByLibrary.simpleMessage("نفسي"), "Published": MessageLookupByLibrary.simpleMessage("نشرت"), "Published_Manga": @@ -895,6 +900,9 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("إعادة النظر"), "Review_By": MessageLookupByLibrary.simpleMessage("روجع من قبل"), "Review_On": MessageLookupByLibrary.simpleMessage("مراجعة على"), + "Review_Summary": MessageLookupByLibrary.simpleMessage("ملخص المراجعة"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "يتم إنشاء حكم المراجعة بواسطة الذكاء الاصطناعي باستخدام المراجعات التالية وقد لا يكون دقيقًا. يرجى قراءة التقييمات لمزيد من المعلومات."), "Reviews": MessageLookupByLibrary.simpleMessage("تقييم"), "Rewatch": MessageLookupByLibrary.simpleMessage("إعادف"), "Rewatching": MessageLookupByLibrary.simpleMessage("إخراج"), @@ -1063,6 +1071,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("استوديوهات"), "Sub_Category": MessageLookupByLibrary.simpleMessage("تصنيف فرعي"), "Suggestions": MessageLookupByLibrary.simpleMessage("اقتراحات"), + "Summary": MessageLookupByLibrary.simpleMessage("ملخص"), "Summer_2007": MessageLookupByLibrary.simpleMessage("صيف 2007."), "Summer_2008": MessageLookupByLibrary.simpleMessage("صيف 2008."), "Summer_2009": MessageLookupByLibrary.simpleMessage("صيف 2009."), @@ -1198,6 +1207,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("المستخدمين"), "Value": MessageLookupByLibrary.simpleMessage("قيمة"), "Vampire": MessageLookupByLibrary.simpleMessage("مصاص دماء"), + "Verdict": MessageLookupByLibrary.simpleMessage("الحكم"), "Version": MessageLookupByLibrary.simpleMessage("إصدار"), "Very_Bad": MessageLookupByLibrary.simpleMessage("سيئ جدا"), "Very_Good": MessageLookupByLibrary.simpleMessage("جيد جدًا"), diff --git a/lib/generated/intl/messages_de_DE.dart b/lib/generated/intl/messages_de_DE.dart index 53c8159..4ac8e16 100644 --- a/lib/generated/intl/messages_de_DE.dart +++ b/lib/generated/intl/messages_de_DE.dart @@ -219,6 +219,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "Möchten Sie Benachrichtigungen für diese App zulassen? Dazu gehören Benachrichtigungen, wenn eine Episode von Ihrem PTW (Plan To Watch) und Ihrer Beobachtungsliste ausgestrahlt wird."), "Confusing": MessageLookupByLibrary.simpleMessage("Verwirrend"), + "Cons": MessageLookupByLibrary.simpleMessage("Nachteile"), "Content_type": MessageLookupByLibrary.simpleMessage("Inhaltstyp"), "Continue": MessageLookupByLibrary.simpleMessage("fortsetzen?"), "ContinueW": MessageLookupByLibrary.simpleMessage("Fortsetzen"), @@ -428,6 +429,9 @@ class MessageLookup extends MessageLookupByLibrary { "Kümmere mich nicht standardmäßig"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("Andere Diagramme generieren"), + "Generating": MessageLookupByLibrary.simpleMessage("Generieren"), + "Generating_Review_Summary": MessageLookupByLibrary.simpleMessage( + "Zusammenfassung der Rezension erstellen"), "Genre": MessageLookupByLibrary.simpleMessage("Genre"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Genreverteilung"), @@ -842,6 +846,7 @@ class MessageLookup extends MessageLookupByLibrary { "Der Profilhintergrund wurde erfolgreich festgelegt"), "Promo_Videos": MessageLookupByLibrary.simpleMessage("Promo-Videos."), "Promotional": MessageLookupByLibrary.simpleMessage("Werbeartikel"), + "Pros": MessageLookupByLibrary.simpleMessage("Vorteile"), "Psychological": MessageLookupByLibrary.simpleMessage("Psychologisch"), "Published": MessageLookupByLibrary.simpleMessage("Veröffentlicht"), "Published_Manga": @@ -920,6 +925,10 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("Rezension"), "Review_By": MessageLookupByLibrary.simpleMessage("Überprüfung durch"), "Review_On": MessageLookupByLibrary.simpleMessage("Rezension an"), + "Review_Summary": MessageLookupByLibrary.simpleMessage( + "Zusammenfassung der Rezension"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "Das Bewertungsurteil wird von KI anhand der folgenden Bewertungen erstellt und ist möglicherweise nicht korrekt. Für weitere Informationen lesen Sie bitte die Bewertungen."), "Reviews": MessageLookupByLibrary.simpleMessage("Rezensionen"), "Rewatch": MessageLookupByLibrary.simpleMessage("Wiederverwatschen"), "Rewatching": MessageLookupByLibrary.simpleMessage("Wiederverwinternd"), @@ -1100,6 +1109,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Studios"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Unterkategorie"), "Suggestions": MessageLookupByLibrary.simpleMessage("Vorschläge"), + "Summary": MessageLookupByLibrary.simpleMessage("Zusammenfassung"), "Summer_2007": MessageLookupByLibrary.simpleMessage("Sommer 2007."), "Summer_2008": MessageLookupByLibrary.simpleMessage("Sommer 2008."), "Summer_2009": MessageLookupByLibrary.simpleMessage("Sommer 2009."), @@ -1235,6 +1245,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("Benutzer"), "Value": MessageLookupByLibrary.simpleMessage("Wert"), "Vampire": MessageLookupByLibrary.simpleMessage("Vampir"), + "Verdict": MessageLookupByLibrary.simpleMessage("Urteil"), "Version": MessageLookupByLibrary.simpleMessage("Ausführung"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Sehr schlecht"), "Very_Good": MessageLookupByLibrary.simpleMessage("Sehr gut"), diff --git a/lib/generated/intl/messages_en_US.dart b/lib/generated/intl/messages_en_US.dart index 839552a..36f661d 100644 --- a/lib/generated/intl/messages_en_US.dart +++ b/lib/generated/intl/messages_en_US.dart @@ -211,6 +211,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "Do you wish to allow notifications for this app. This includes notifications when an episode is aired from your PTW (Plan To Watch) and Watching list."), "Confusing": MessageLookupByLibrary.simpleMessage("Confusing"), + "Cons": MessageLookupByLibrary.simpleMessage("Cons"), "Content_type": MessageLookupByLibrary.simpleMessage("Content type"), "Continue": MessageLookupByLibrary.simpleMessage("continue?"), "ContinueW": MessageLookupByLibrary.simpleMessage("Continue"), @@ -416,6 +417,9 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Don\'t care by default"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("Generate Other charts"), + "Generating": MessageLookupByLibrary.simpleMessage("Generating"), + "Generating_Review_Summary": + MessageLookupByLibrary.simpleMessage("Generating Review Summary"), "Genre": MessageLookupByLibrary.simpleMessage("Genre"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Genre Distribution"), @@ -810,6 +814,7 @@ class MessageLookup extends MessageLookupByLibrary { "Profile background is set successfully"), "Promo_Videos": MessageLookupByLibrary.simpleMessage("Promo Videos"), "Promotional": MessageLookupByLibrary.simpleMessage("Promotional"), + "Pros": MessageLookupByLibrary.simpleMessage("Pros"), "Psychological": MessageLookupByLibrary.simpleMessage("Psychological"), "Published": MessageLookupByLibrary.simpleMessage("Published"), "Published_Manga": @@ -887,6 +892,10 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("Review"), "Review_By": MessageLookupByLibrary.simpleMessage("Review by"), "Review_On": MessageLookupByLibrary.simpleMessage("Review on"), + "Review_Summary": + MessageLookupByLibrary.simpleMessage("Review Summary"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "Review Verdict is generated by AI using the following reviews and may not be accurate. Please read the reviews for more information."), "Reviews": MessageLookupByLibrary.simpleMessage("Reviews"), "Rewatch": MessageLookupByLibrary.simpleMessage("Rewatch"), "Rewatching": MessageLookupByLibrary.simpleMessage("Rewatching"), @@ -1062,6 +1071,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Studios"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Sub Category"), "Suggestions": MessageLookupByLibrary.simpleMessage("Suggestions"), + "Summary": MessageLookupByLibrary.simpleMessage("Summary"), "Summer_2007": MessageLookupByLibrary.simpleMessage("Summer 2007"), "Summer_2008": MessageLookupByLibrary.simpleMessage("Summer 2008"), "Summer_2009": MessageLookupByLibrary.simpleMessage("Summer 2009"), @@ -1192,6 +1202,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("users"), "Value": MessageLookupByLibrary.simpleMessage("Value"), "Vampire": MessageLookupByLibrary.simpleMessage("Vampire"), + "Verdict": MessageLookupByLibrary.simpleMessage("Verdict"), "Version": MessageLookupByLibrary.simpleMessage("Version"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Very Bad"), "Very_Good": MessageLookupByLibrary.simpleMessage("Very Good"), diff --git a/lib/generated/intl/messages_es_ES.dart b/lib/generated/intl/messages_es_ES.dart index dc4b425..114259e 100644 --- a/lib/generated/intl/messages_es_ES.dart +++ b/lib/generated/intl/messages_es_ES.dart @@ -222,6 +222,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "¿Desea permitir notificaciones para esta aplicación? Esto incluye notificaciones cuando se transmite un episodio desde su PTW (Plan para mirar) y su lista de observación."), "Confusing": MessageLookupByLibrary.simpleMessage("Confuso"), + "Cons": MessageLookupByLibrary.simpleMessage("Contras"), "Content_type": MessageLookupByLibrary.simpleMessage("Tipo de contenido"), "Continue": MessageLookupByLibrary.simpleMessage("¿Seguir?"), @@ -437,6 +438,9 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("No importa por defecto"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("Generar otros gráficos"), + "Generating": MessageLookupByLibrary.simpleMessage("generando"), + "Generating_Review_Summary": MessageLookupByLibrary.simpleMessage( + "Generando resumen de revisión"), "Genre": MessageLookupByLibrary.simpleMessage("Género"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Distribución de género"), @@ -856,6 +860,7 @@ class MessageLookup extends MessageLookupByLibrary { "Promo_Videos": MessageLookupByLibrary.simpleMessage("Videos de promoción"), "Promotional": MessageLookupByLibrary.simpleMessage("Promocional"), + "Pros": MessageLookupByLibrary.simpleMessage("Ventajas"), "Psychological": MessageLookupByLibrary.simpleMessage("Psychological"), "Published": MessageLookupByLibrary.simpleMessage("Published"), "Published_Manga": @@ -936,6 +941,10 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("Revisar"), "Review_By": MessageLookupByLibrary.simpleMessage("Revisado por"), "Review_On": MessageLookupByLibrary.simpleMessage("Revisar en"), + "Review_Summary": + MessageLookupByLibrary.simpleMessage("Resumen de revisión"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "El veredicto de revisión lo genera AI utilizando las siguientes revisiones y puede no ser exacto. Lea las reseñas para obtener más información."), "Reviews": MessageLookupByLibrary.simpleMessage("Comentarios"), "Rewatch": MessageLookupByLibrary.simpleMessage("Revestidor"), "Rewatching": MessageLookupByLibrary.simpleMessage("Revestimiento"), @@ -1124,6 +1133,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Estudios"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Subcategoría"), "Suggestions": MessageLookupByLibrary.simpleMessage("Sugerencias"), + "Summary": MessageLookupByLibrary.simpleMessage("Resumen"), "Summer_2007": MessageLookupByLibrary.simpleMessage("2007 de verano de 2007"), "Summer_2008": MessageLookupByLibrary.simpleMessage("Verano 2008"), @@ -1264,6 +1274,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("users"), "Value": MessageLookupByLibrary.simpleMessage("Valor"), "Vampire": MessageLookupByLibrary.simpleMessage("Vampire"), + "Verdict": MessageLookupByLibrary.simpleMessage("Veredicto"), "Version": MessageLookupByLibrary.simpleMessage("Versión"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Muy mal"), "Very_Good": MessageLookupByLibrary.simpleMessage("Muy bien"), diff --git a/lib/generated/intl/messages_fr_FR.dart b/lib/generated/intl/messages_fr_FR.dart index 43b870d..70af7d1 100644 --- a/lib/generated/intl/messages_fr_FR.dart +++ b/lib/generated/intl/messages_fr_FR.dart @@ -220,6 +220,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "Souhaitez-vous autoriser les notifications pour cette application. Cela inclut les notifications lorsqu\'un épisode est diffusé à partir de votre PTW (Plan To Watch) et de votre liste de surveillance."), "Confusing": MessageLookupByLibrary.simpleMessage("Déroutant"), + "Cons": MessageLookupByLibrary.simpleMessage("Inconvénients"), "Content_type": MessageLookupByLibrary.simpleMessage("Type de contenu"), "Continue": MessageLookupByLibrary.simpleMessage("Continuez?"), "ContinueW": MessageLookupByLibrary.simpleMessage("Continuer"), @@ -432,6 +433,9 @@ class MessageLookup extends MessageLookupByLibrary { "Ne vous souciez pas par défaut"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage( "Générer d\'autres graphiques"), + "Generating": MessageLookupByLibrary.simpleMessage("Générateur"), + "Generating_Review_Summary": MessageLookupByLibrary.simpleMessage( + "Génération du résumé des avis"), "Genre": MessageLookupByLibrary.simpleMessage("Genre"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Genre Distribution"), @@ -858,6 +862,7 @@ class MessageLookup extends MessageLookupByLibrary { "Promo_Videos": MessageLookupByLibrary.simpleMessage("Vidéos promotionnelles"), "Promotional": MessageLookupByLibrary.simpleMessage("Promotionnel"), + "Pros": MessageLookupByLibrary.simpleMessage("Avantages"), "Psychological": MessageLookupByLibrary.simpleMessage("Psychologique"), "Published": MessageLookupByLibrary.simpleMessage("Publié"), "Published_Manga": MessageLookupByLibrary.simpleMessage("Manga publié"), @@ -937,6 +942,10 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("Revoir"), "Review_By": MessageLookupByLibrary.simpleMessage("Revue par"), "Review_On": MessageLookupByLibrary.simpleMessage("Examen sur"), + "Review_Summary": + MessageLookupByLibrary.simpleMessage("Résumé de l\'examen"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "Le verdict des avis est généré par l\'IA à l\'aide des avis suivants et peut ne pas être exact. Veuillez lire les commentaires pour plus d\'informations."), "Reviews": MessageLookupByLibrary.simpleMessage("Commentaires"), "Rewatch": MessageLookupByLibrary.simpleMessage("Rewatch"), "Rewatching": MessageLookupByLibrary.simpleMessage("Rétablissement"), @@ -1116,6 +1125,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Studios"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Sous-catégorie"), "Suggestions": MessageLookupByLibrary.simpleMessage("Suggestions"), + "Summary": MessageLookupByLibrary.simpleMessage("Résumé"), "Summer_2007": MessageLookupByLibrary.simpleMessage("Été 2007"), "Summer_2008": MessageLookupByLibrary.simpleMessage("Été 2008"), "Summer_2009": MessageLookupByLibrary.simpleMessage("Été 2009"), @@ -1249,6 +1259,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("users"), "Value": MessageLookupByLibrary.simpleMessage("Valeur"), "Vampire": MessageLookupByLibrary.simpleMessage("Vampire"), + "Verdict": MessageLookupByLibrary.simpleMessage("Verdict"), "Version": MessageLookupByLibrary.simpleMessage("Version"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Très mauvais"), "Very_Good": MessageLookupByLibrary.simpleMessage("Très bien"), diff --git a/lib/generated/intl/messages_id_ID.dart b/lib/generated/intl/messages_id_ID.dart index 06eeef6..6754f93 100644 --- a/lib/generated/intl/messages_id_ID.dart +++ b/lib/generated/intl/messages_id_ID.dart @@ -216,6 +216,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "Apakah Anda ingin mengizinkan pemberitahuan untuk aplikasi ini. Ini termasuk pemberitahuan ketika sebuah episode ditayangkan dari PTW (Plan To Watch) dan daftar Tontonan Anda."), "Confusing": MessageLookupByLibrary.simpleMessage("Membingungkan"), + "Cons": MessageLookupByLibrary.simpleMessage("Kontra"), "Content_type": MessageLookupByLibrary.simpleMessage("Jenis konten"), "Continue": MessageLookupByLibrary.simpleMessage("melanjutkan?"), "ContinueW": MessageLookupByLibrary.simpleMessage("Melanjutkan"), @@ -424,6 +425,9 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Tidak peduli secara default"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("Hasilkan grafik lainnya"), + "Generating": MessageLookupByLibrary.simpleMessage("Menghasilkan"), + "Generating_Review_Summary": MessageLookupByLibrary.simpleMessage( + "Menghasilkan Ringkasan Tinjauan"), "Genre": MessageLookupByLibrary.simpleMessage("Genre"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Distribusi Genre"), @@ -832,6 +836,7 @@ class MessageLookup extends MessageLookupByLibrary { "Latar belakang profil berhasil disetel"), "Promo_Videos": MessageLookupByLibrary.simpleMessage("Video promo"), "Promotional": MessageLookupByLibrary.simpleMessage("Promosi"), + "Pros": MessageLookupByLibrary.simpleMessage("Kelebihan"), "Psychological": MessageLookupByLibrary.simpleMessage("Psikologis"), "Published": MessageLookupByLibrary.simpleMessage("Diterbitkan"), "Published_Manga": @@ -908,6 +913,10 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("Review"), "Review_By": MessageLookupByLibrary.simpleMessage("Tinjau oleh"), "Review_On": MessageLookupByLibrary.simpleMessage("Tinjau kembali"), + "Review_Summary": + MessageLookupByLibrary.simpleMessage("Ringkasan Tinjauan"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "Putusan Tinjauan dibuat oleh AI menggunakan ulasan berikut dan mungkin tidak akurat. Silakan baca ulasan untuk informasi lebih lanjut."), "Reviews": MessageLookupByLibrary.simpleMessage("Ulasan"), "Rewatch": MessageLookupByLibrary.simpleMessage("Rewatch."), "Rewatching": MessageLookupByLibrary.simpleMessage("Rewatching."), @@ -1083,6 +1092,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Studio"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Sub kategori."), "Suggestions": MessageLookupByLibrary.simpleMessage("Saran"), + "Summary": MessageLookupByLibrary.simpleMessage("Ringkasan"), "Summer_2007": MessageLookupByLibrary.simpleMessage("Musim panas 2007."), "Summer_2008": @@ -1233,6 +1243,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("pengguna."), "Value": MessageLookupByLibrary.simpleMessage("Nilai"), "Vampire": MessageLookupByLibrary.simpleMessage("Vampir."), + "Verdict": MessageLookupByLibrary.simpleMessage("Dakwaan"), "Version": MessageLookupByLibrary.simpleMessage("Versi: kapan"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Sangat buruk"), "Very_Good": MessageLookupByLibrary.simpleMessage("Sangat bagus"), diff --git a/lib/generated/intl/messages_ja.dart b/lib/generated/intl/messages_ja.dart index bc79a7f..703d8b7 100644 --- a/lib/generated/intl/messages_ja.dart +++ b/lib/generated/intl/messages_ja.dart @@ -190,6 +190,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "このアプリの通知を許可しますか?これには、PTW (視聴プラン) および視聴リストからエピソードが放送されたときの通知が含まれます。"), "Confusing": MessageLookupByLibrary.simpleMessage("紛らわしい"), + "Cons": MessageLookupByLibrary.simpleMessage("短所"), "Content_type": MessageLookupByLibrary.simpleMessage("コンテンツタイプ"), "Continue": MessageLookupByLibrary.simpleMessage("継続する?"), "ContinueW": MessageLookupByLibrary.simpleMessage("継続する"), @@ -378,6 +379,9 @@ class MessageLookup extends MessageLookupByLibrary { "Gender_desc": MessageLookupByLibrary.simpleMessage("デフォルトで気にしないでください"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("他のチャートを生成する"), + "Generating": MessageLookupByLibrary.simpleMessage("生成中"), + "Generating_Review_Summary": + MessageLookupByLibrary.simpleMessage("レビュー概要の生成"), "Genre": MessageLookupByLibrary.simpleMessage("ジャンル"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("ジャンル配信"), "Genre_Exclude": MessageLookupByLibrary.simpleMessage("ジャンル除外"), @@ -735,6 +739,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("プロフィールの背景が正常に設定されました"), "Promo_Videos": MessageLookupByLibrary.simpleMessage("プロモビデオ"), "Promotional": MessageLookupByLibrary.simpleMessage("プロモーション"), + "Pros": MessageLookupByLibrary.simpleMessage("長所"), "Psychological": MessageLookupByLibrary.simpleMessage("心理学"), "Published": MessageLookupByLibrary.simpleMessage("発行"), "Published_Manga": MessageLookupByLibrary.simpleMessage("公開されたマンガ"), @@ -803,6 +808,9 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("レビュー"), "Review_By": MessageLookupByLibrary.simpleMessage("審査員"), "Review_On": MessageLookupByLibrary.simpleMessage("レビュー"), + "Review_Summary": MessageLookupByLibrary.simpleMessage("レビューの概要"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "レビュー評決は以下のレビューを使用して AI によって生成されており、正確ではない可能性があります。詳細についてはレビューをお読みください。"), "Reviews": MessageLookupByLibrary.simpleMessage("レビュー"), "Rewatch": MessageLookupByLibrary.simpleMessage("再ウォッチ"), "Rewatching": MessageLookupByLibrary.simpleMessage("再ウォッチング"), @@ -962,6 +970,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("スタジオ"), "Sub_Category": MessageLookupByLibrary.simpleMessage("サブカテゴリ"), "Suggestions": MessageLookupByLibrary.simpleMessage("suggest suggest"), + "Summary": MessageLookupByLibrary.simpleMessage("まとめ"), "Summer_2007": MessageLookupByLibrary.simpleMessage("2007年夏"), "Summer_2008": MessageLookupByLibrary.simpleMessage("2008年夏"), "Summer_2009": MessageLookupByLibrary.simpleMessage("2009年夏"), @@ -1079,6 +1088,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("ユーザー"), "Value": MessageLookupByLibrary.simpleMessage("価値"), "Vampire": MessageLookupByLibrary.simpleMessage("吸血鬼"), + "Verdict": MessageLookupByLibrary.simpleMessage("評決"), "Version": MessageLookupByLibrary.simpleMessage("バージョン"), "Very_Bad": MessageLookupByLibrary.simpleMessage("ひどい"), "Very_Good": MessageLookupByLibrary.simpleMessage("とても良い"), diff --git a/lib/generated/intl/messages_ko_KR.dart b/lib/generated/intl/messages_ko_KR.dart index 389f03b..9f0dfaa 100644 --- a/lib/generated/intl/messages_ko_KR.dart +++ b/lib/generated/intl/messages_ko_KR.dart @@ -188,6 +188,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "이 앱에 대한 알림을 허용하시겠습니까? 여기에는 PTW(시청 계획) 및 시청 목록에서 에피소드가 방송될 때의 알림이 포함됩니다."), "Confusing": MessageLookupByLibrary.simpleMessage("혼란스러운"), + "Cons": MessageLookupByLibrary.simpleMessage("단점"), "Content_type": MessageLookupByLibrary.simpleMessage("Content type"), "Continue": MessageLookupByLibrary.simpleMessage("계속하다?"), "ContinueW": MessageLookupByLibrary.simpleMessage("계속하다"), @@ -379,6 +380,9 @@ class MessageLookup extends MessageLookupByLibrary { "Gender_desc": MessageLookupByLibrary.simpleMessage("기본적으로 상관 없어"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("다른 차트 생성"), + "Generating": MessageLookupByLibrary.simpleMessage("생성 중"), + "Generating_Review_Summary": + MessageLookupByLibrary.simpleMessage("리뷰 요약 생성 중"), "Genre": MessageLookupByLibrary.simpleMessage("장르"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("장르 분포"), "Genre_Exclude": MessageLookupByLibrary.simpleMessage("장르 제외"), @@ -739,6 +743,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("프로필 배경이 설정되었습니다."), "Promo_Videos": MessageLookupByLibrary.simpleMessage("프로모션 비디오"), "Promotional": MessageLookupByLibrary.simpleMessage("판촉"), + "Pros": MessageLookupByLibrary.simpleMessage("장점"), "Psychological": MessageLookupByLibrary.simpleMessage("심리학"), "Published": MessageLookupByLibrary.simpleMessage("게시"), "Published_Manga": MessageLookupByLibrary.simpleMessage("게시 된 만화"), @@ -807,6 +812,9 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("검토"), "Review_By": MessageLookupByLibrary.simpleMessage("리뷰 작성자"), "Review_On": MessageLookupByLibrary.simpleMessage("검토 날짜"), + "Review_Summary": MessageLookupByLibrary.simpleMessage("검토 요약"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "리뷰 평결은 다음 리뷰를 사용하여 AI에 의해 생성되므로 정확하지 않을 수 있습니다. 자세한 내용은 리뷰를 읽어보시기 바랍니다."), "Reviews": MessageLookupByLibrary.simpleMessage("리뷰"), "Rewatch": MessageLookupByLibrary.simpleMessage("다시 늘어납니다"), "Rewatching": MessageLookupByLibrary.simpleMessage("재구성"), @@ -966,6 +974,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("스튜디오"), "Sub_Category": MessageLookupByLibrary.simpleMessage("하위 카테고리"), "Suggestions": MessageLookupByLibrary.simpleMessage("제안"), + "Summary": MessageLookupByLibrary.simpleMessage("요약"), "Summer_2007": MessageLookupByLibrary.simpleMessage("2007 년 여름"), "Summer_2008": MessageLookupByLibrary.simpleMessage("2008 년 여름"), "Summer_2009": MessageLookupByLibrary.simpleMessage("2009 년 여름"), @@ -1083,6 +1092,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("사용자"), "Value": MessageLookupByLibrary.simpleMessage("값"), "Vampire": MessageLookupByLibrary.simpleMessage("흡혈귀"), + "Verdict": MessageLookupByLibrary.simpleMessage("평결"), "Version": MessageLookupByLibrary.simpleMessage("버전"), "Very_Bad": MessageLookupByLibrary.simpleMessage("아주 나쁜"), "Very_Good": MessageLookupByLibrary.simpleMessage("매우 좋은"), diff --git a/lib/generated/intl/messages_pt_BR.dart b/lib/generated/intl/messages_pt_BR.dart index 6244ebb..70ba013 100644 --- a/lib/generated/intl/messages_pt_BR.dart +++ b/lib/generated/intl/messages_pt_BR.dart @@ -218,6 +218,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "Deseja permitir notificações para este aplicativo. Isso inclui notificações quando um episódio é transmitido a partir de seu PTW (Plano para assistir) e lista de exibição."), "Confusing": MessageLookupByLibrary.simpleMessage("Confuso"), + "Cons": MessageLookupByLibrary.simpleMessage("Contras"), "Content_type": MessageLookupByLibrary.simpleMessage("Tipo de conteúdo"), "Continue": MessageLookupByLibrary.simpleMessage("Prosseguir?"), @@ -432,6 +433,9 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Não se importa por padrão"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("Gerar outros gráficos"), + "Generating": MessageLookupByLibrary.simpleMessage("Gerando"), + "Generating_Review_Summary": + MessageLookupByLibrary.simpleMessage("Gerando Resumo de Revisão"), "Genre": MessageLookupByLibrary.simpleMessage("Gênero"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Distribuição de gênero"), @@ -845,6 +849,7 @@ class MessageLookup extends MessageLookupByLibrary { "Promo_Videos": MessageLookupByLibrary.simpleMessage("Vídeos promocionais."), "Promotional": MessageLookupByLibrary.simpleMessage("Promocional"), + "Pros": MessageLookupByLibrary.simpleMessage("Prós"), "Psychological": MessageLookupByLibrary.simpleMessage("Psicológico"), "Published": MessageLookupByLibrary.simpleMessage("Published"), "Published_Manga": @@ -924,6 +929,10 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("Análise"), "Review_By": MessageLookupByLibrary.simpleMessage("Revisão por"), "Review_On": MessageLookupByLibrary.simpleMessage("Revisar em"), + "Review_Summary": + MessageLookupByLibrary.simpleMessage("Resumo da revisão"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "O veredicto da revisão é gerado pela IA usando as análises a seguir e pode não ser preciso. Por favor, leia os comentários para obter mais informações."), "Reviews": MessageLookupByLibrary.simpleMessage("Avaliações"), "Rewatch": MessageLookupByLibrary.simpleMessage("Rewatch"), "Rewatching": MessageLookupByLibrary.simpleMessage("Rewatching."), @@ -1110,6 +1119,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Estúdios"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Sub categoria"), "Suggestions": MessageLookupByLibrary.simpleMessage("Sugestões"), + "Summary": MessageLookupByLibrary.simpleMessage("Resumo"), "Summer_2007": MessageLookupByLibrary.simpleMessage("Verão 2007."), "Summer_2008": MessageLookupByLibrary.simpleMessage("Verão 2008."), "Summer_2009": MessageLookupByLibrary.simpleMessage("Verão 2009."), @@ -1247,6 +1257,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("Comercial"), "Value": MessageLookupByLibrary.simpleMessage("Valor"), "Vampire": MessageLookupByLibrary.simpleMessage("Vampire"), + "Verdict": MessageLookupByLibrary.simpleMessage("Veredicto"), "Version": MessageLookupByLibrary.simpleMessage("Versão"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Muito mal"), "Very_Good": MessageLookupByLibrary.simpleMessage("Muito bom"), diff --git a/lib/generated/intl/messages_ru_RU.dart b/lib/generated/intl/messages_ru_RU.dart index ef3e1ee..5649f00 100644 --- a/lib/generated/intl/messages_ru_RU.dart +++ b/lib/generated/intl/messages_ru_RU.dart @@ -215,6 +215,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "Хотите ли вы разрешить уведомления для этого приложения? Сюда входят уведомления о выходе эпизода из вашего PTW (план к просмотру) и списка просмотра."), "Confusing": MessageLookupByLibrary.simpleMessage("запутанный"), + "Cons": MessageLookupByLibrary.simpleMessage("Минусы"), "Content_type": MessageLookupByLibrary.simpleMessage("Тип содержимого"), "Continue": MessageLookupByLibrary.simpleMessage("продолжать?"), "ContinueW": MessageLookupByLibrary.simpleMessage("Продолжать"), @@ -425,6 +426,9 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Плевать по умолчанию"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("Создать другие диаграммы"), + "Generating": MessageLookupByLibrary.simpleMessage("Создание"), + "Generating_Review_Summary": + MessageLookupByLibrary.simpleMessage("Создание сводки обзора"), "Genre": MessageLookupByLibrary.simpleMessage("Жанр"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Жанровое распространение"), @@ -839,6 +843,7 @@ class MessageLookup extends MessageLookupByLibrary { "Фон профиля успешно установлен"), "Promo_Videos": MessageLookupByLibrary.simpleMessage("Промо-видео"), "Promotional": MessageLookupByLibrary.simpleMessage("Рекламный"), + "Pros": MessageLookupByLibrary.simpleMessage("Плюсы"), "Psychological": MessageLookupByLibrary.simpleMessage("Психологический"), "Published": MessageLookupByLibrary.simpleMessage("Опубликовано"), @@ -918,6 +923,9 @@ class MessageLookup extends MessageLookupByLibrary { "Review": MessageLookupByLibrary.simpleMessage("Обзор"), "Review_By": MessageLookupByLibrary.simpleMessage("Отзыв от"), "Review_On": MessageLookupByLibrary.simpleMessage("Обзор на"), + "Review_Summary": MessageLookupByLibrary.simpleMessage("Обзор обзора"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "Вердикт обзора генерируется искусственным интеллектом на основе следующих обзоров и может быть неточным. Пожалуйста, прочитайте отзывы для получения дополнительной информации."), "Reviews": MessageLookupByLibrary.simpleMessage("Отзывы"), "Rewatch": MessageLookupByLibrary.simpleMessage("Пересмотреть"), "Rewatching": MessageLookupByLibrary.simpleMessage("Пересматриваю"), @@ -1095,6 +1103,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Студии"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Подкатегория"), "Suggestions": MessageLookupByLibrary.simpleMessage("Предложения"), + "Summary": MessageLookupByLibrary.simpleMessage("Краткое содержание"), "Summer_2007": MessageLookupByLibrary.simpleMessage("Лето 2007 г."), "Summer_2008": MessageLookupByLibrary.simpleMessage("Лето 2008 г."), "Summer_2009": MessageLookupByLibrary.simpleMessage("Лето 2009 г."), @@ -1235,6 +1244,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("пользователи"), "Value": MessageLookupByLibrary.simpleMessage("Ценить"), "Vampire": MessageLookupByLibrary.simpleMessage("Вампир"), + "Verdict": MessageLookupByLibrary.simpleMessage("Вердикт"), "Version": MessageLookupByLibrary.simpleMessage("Версия"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Очень плохо"), "Very_Good": MessageLookupByLibrary.simpleMessage("Очень хороший"), diff --git a/lib/generated/intl/messages_tr_TR.dart b/lib/generated/intl/messages_tr_TR.dart index 80df915..6e16b59 100644 --- a/lib/generated/intl/messages_tr_TR.dart +++ b/lib/generated/intl/messages_tr_TR.dart @@ -214,6 +214,7 @@ class MessageLookup extends MessageLookupByLibrary { "ConfirmNotifPermDesc": MessageLookupByLibrary.simpleMessage( "Bu uygulama için bildirimlere izin vermek istiyor musunuz? Buna PTW (İzleme Planınız) ve İzleme listenizden bir bölüm yayınlandığında yapılan bildirimler de dahildir."), "Confusing": MessageLookupByLibrary.simpleMessage("Kafa karıştırıcı"), + "Cons": MessageLookupByLibrary.simpleMessage("Eksileri"), "Content_type": MessageLookupByLibrary.simpleMessage("İçerik türü"), "Continue": MessageLookupByLibrary.simpleMessage("devam etmek?"), "ContinueW": MessageLookupByLibrary.simpleMessage("Devam etmek"), @@ -420,6 +421,9 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Varsayılan olarak umursama"), "Generate_Other_charts": MessageLookupByLibrary.simpleMessage("Diğer grafikler oluştur"), + "Generating": MessageLookupByLibrary.simpleMessage("Üretiliyor"), + "Generating_Review_Summary": MessageLookupByLibrary.simpleMessage( + "İnceleme Özeti Oluşturuluyor"), "Genre": MessageLookupByLibrary.simpleMessage("Tür"), "Genre_Distribution": MessageLookupByLibrary.simpleMessage("Tür Dağılımı"), @@ -823,6 +827,7 @@ class MessageLookup extends MessageLookupByLibrary { "Promo_Videos": MessageLookupByLibrary.simpleMessage("Tanıtım Videoları"), "Promotional": MessageLookupByLibrary.simpleMessage("Promosyon"), + "Pros": MessageLookupByLibrary.simpleMessage("Artıları"), "Psychological": MessageLookupByLibrary.simpleMessage("Psikolojik"), "Published": MessageLookupByLibrary.simpleMessage("Yayınlanan"), "Published_Manga": @@ -902,6 +907,10 @@ class MessageLookup extends MessageLookupByLibrary { "Review_By": MessageLookupByLibrary.simpleMessage("Tarafından incelemek"), "Review_On": MessageLookupByLibrary.simpleMessage("İnceleme"), + "Review_Summary": + MessageLookupByLibrary.simpleMessage("İnceleme Özeti"), + "Review_Summary_Desc": MessageLookupByLibrary.simpleMessage( + "İnceleme Kararı yapay zeka tarafından aşağıdaki incelemeler kullanılarak oluşturulur ve doğru olmayabilir. Daha fazla bilgi için lütfen yorumları okuyun."), "Reviews": MessageLookupByLibrary.simpleMessage("Yorumlar"), "Rewatch": MessageLookupByLibrary.simpleMessage("Tekrar izle"), "Rewatching": MessageLookupByLibrary.simpleMessage("Tekrar izliyorum"), @@ -1083,6 +1092,7 @@ class MessageLookup extends MessageLookupByLibrary { "Studios": MessageLookupByLibrary.simpleMessage("Stüdyolar"), "Sub_Category": MessageLookupByLibrary.simpleMessage("Alt Kategori"), "Suggestions": MessageLookupByLibrary.simpleMessage("Öneriler"), + "Summary": MessageLookupByLibrary.simpleMessage("Özet"), "Summer_2007": MessageLookupByLibrary.simpleMessage("Yaz 2007"), "Summer_2008": MessageLookupByLibrary.simpleMessage("Yaz 2008"), "Summer_2009": MessageLookupByLibrary.simpleMessage("Yaz 2009"), @@ -1218,6 +1228,7 @@ class MessageLookup extends MessageLookupByLibrary { "Users": MessageLookupByLibrary.simpleMessage("kullanıcılar"), "Value": MessageLookupByLibrary.simpleMessage("Değer"), "Vampire": MessageLookupByLibrary.simpleMessage("Vampir"), + "Verdict": MessageLookupByLibrary.simpleMessage("Karar"), "Version": MessageLookupByLibrary.simpleMessage("Sürüm"), "Very_Bad": MessageLookupByLibrary.simpleMessage("Çok Kötü"), "Very_Good": MessageLookupByLibrary.simpleMessage("Çok güzel"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 8f7515b..12b2909 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -9470,6 +9470,86 @@ class S { ); } + /// `Review Summary` + String get Review_Summary { + return Intl.message( + 'Review Summary', + name: 'Review_Summary', + desc: '', + args: [], + ); + } + + /// `Summary` + String get Summary { + return Intl.message( + 'Summary', + name: 'Summary', + desc: '', + args: [], + ); + } + + /// `Pros` + String get Pros { + return Intl.message( + 'Pros', + name: 'Pros', + desc: '', + args: [], + ); + } + + /// `Cons` + String get Cons { + return Intl.message( + 'Cons', + name: 'Cons', + desc: '', + args: [], + ); + } + + /// `Verdict` + String get Verdict { + return Intl.message( + 'Verdict', + name: 'Verdict', + desc: '', + args: [], + ); + } + + /// `Review Verdict is generated by AI using the following reviews and may not be accurate. Please read the reviews for more information.` + String get Review_Summary_Desc { + return Intl.message( + 'Review Verdict is generated by AI using the following reviews and may not be accurate. Please read the reviews for more information.', + name: 'Review_Summary_Desc', + desc: '', + args: [], + ); + } + + /// `Generating Review Summary` + String get Generating_Review_Summary { + return Intl.message( + 'Generating Review Summary', + name: 'Generating_Review_Summary', + desc: '', + args: [], + ); + } + + /// `Generating` + String get Generating { + return Intl.message( + 'Generating', + name: 'Generating', + desc: '', + args: [], + ); + } + /// `Anime Calendar` String get AnimeCalendar { return Intl.message( diff --git a/lib/l10n/intl_ar_EG.arb b/lib/l10n/intl_ar_EG.arb index c4d7098..d62e10f 100644 --- a/lib/l10n/intl_ar_EG.arb +++ b/lib/l10n/intl_ar_EG.arb @@ -945,5 +945,13 @@ "NextShow": "\u0627\u0644\u0639\u0631\u0636 \u0627\u0644\u0642\u0627\u062f\u0645", "Show": "\u064a\u0639\u0631\u0636", "ReleaseStartDate": "\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0627\u0641\u0631\u0627\u062c \u0639\u0646\u0647", - "AnimeCalendar": "\u062a\u0642\u0648\u064a\u0645 \u0627\u0644\u0623\u0646\u0645\u064a" + "AnimeCalendar": "\u062a\u0642\u0648\u064a\u0645 \u0627\u0644\u0623\u0646\u0645\u064a", + "Review_Summary": "\u0645\u0644\u062e\u0635 \u0627\u0644\u0645\u0631\u0627\u062c\u0639\u0629", + "Summary": "\u0645\u0644\u062e\u0635", + "Pros": "\u0627\u0644\u0627\u064a\u062c\u0627\u0628\u064a\u0627\u062a", + "Cons": "\u0633\u0644\u0628\u064a\u0627\u062a", + "Verdict": "\u0627\u0644\u062d\u0643\u0645", + "Review_Summary_Desc": "\u064a\u062a\u0645 \u0625\u0646\u0634\u0627\u0621 \u062d\u0643\u0645 \u0627\u0644\u0645\u0631\u0627\u062c\u0639\u0629 \u0628\u0648\u0627\u0633\u0637\u0629 \u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0627\u0635\u0637\u0646\u0627\u0639\u064a \u0628\u0627\u0633\u062a\u062e\u062f\u0627\u0645 \u0627\u0644\u0645\u0631\u0627\u062c\u0639\u0627\u062a \u0627\u0644\u062a\u0627\u0644\u064a\u0629 \u0648\u0642\u062f \u0644\u0627 \u064a\u0643\u0648\u0646 \u062f\u0642\u064a\u0642\u064b\u0627. \u064a\u0631\u062c\u0649 \u0642\u0631\u0627\u0621\u0629 \u0627\u0644\u062a\u0642\u064a\u064a\u0645\u0627\u062a \u0644\u0645\u0632\u064a\u062f \u0645\u0646 \u0627\u0644\u0645\u0639\u0644\u0648\u0645\u0627\u062a.", + "Generating_Review_Summary": "\u0625\u0646\u0634\u0627\u0621 \u0645\u0644\u062e\u0635 \u0627\u0644\u0645\u0631\u0627\u062c\u0639\u0629", + "Generating": "\u062a\u0648\u0644\u064a\u062f" } \ No newline at end of file diff --git a/lib/l10n/intl_de_DE.arb b/lib/l10n/intl_de_DE.arb index 12831e8..e95c730 100644 --- a/lib/l10n/intl_de_DE.arb +++ b/lib/l10n/intl_de_DE.arb @@ -946,5 +946,13 @@ "NextShow": "N\u00e4chste Show", "Show": "Zeigen", "ReleaseStartDate": "Ver\u00f6ffentlichungsdatum", - "AnimeCalendar": "Anime-Kalender" + "AnimeCalendar": "Anime-Kalender", + "Review_Summary": "Zusammenfassung der Rezension", + "Summary": "Zusammenfassung", + "Pros": "Vorteile", + "Cons": "Nachteile", + "Verdict": "Urteil", + "Review_Summary_Desc": "Das Bewertungsurteil wird von KI anhand der folgenden Bewertungen erstellt und ist m\u00f6glicherweise nicht korrekt. F\u00fcr weitere Informationen lesen Sie bitte die Bewertungen.", + "Generating_Review_Summary": "Zusammenfassung der Rezension erstellen", + "Generating": "Generieren" } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index fac8ff0..2da64a4 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -942,5 +942,13 @@ "NextShow": "Next Show", "Show": "Show", "ReleaseStartDate": "Release Date", + "Review_Summary": "Review Summary", + "Summary": "Summary", + "Pros": "Pros", + "Cons": "Cons", + "Verdict": "Verdict", + "Review_Summary_Desc": "Review Verdict is generated by AI using the following reviews and may not be accurate. Please read the reviews for more information.", + "Generating_Review_Summary": "Generating Review Summary", + "Generating": "Generating", "AnimeCalendar": "Anime Calendar" } \ No newline at end of file diff --git a/lib/l10n/intl_es_ES.arb b/lib/l10n/intl_es_ES.arb index c42d2f1..957a0b7 100644 --- a/lib/l10n/intl_es_ES.arb +++ b/lib/l10n/intl_es_ES.arb @@ -946,5 +946,13 @@ "NextShow": "Pr\u00f3ximo espect\u00e1culo", "Show": "Espect\u00e1culo", "ReleaseStartDate": "Fecha de lanzamiento", - "AnimeCalendar": "Calendario animado" + "AnimeCalendar": "Calendario animado", + "Review_Summary": "Resumen de revisi\u00f3n", + "Summary": "Resumen", + "Pros": "Ventajas", + "Cons": "Contras", + "Verdict": "Veredicto", + "Review_Summary_Desc": "El veredicto de revisi\u00f3n lo genera AI utilizando las siguientes revisiones y puede no ser exacto. Lea las rese\u00f1as para obtener m\u00e1s informaci\u00f3n.", + "Generating_Review_Summary": "Generando resumen de revisi\u00f3n", + "Generating": "generando" } \ No newline at end of file diff --git a/lib/l10n/intl_fr_FR.arb b/lib/l10n/intl_fr_FR.arb index 5c6449a..5b866a6 100644 --- a/lib/l10n/intl_fr_FR.arb +++ b/lib/l10n/intl_fr_FR.arb @@ -945,5 +945,13 @@ "NextShow": "Spectacle suivant", "Show": "Montrer", "ReleaseStartDate": "Date de sortie", - "AnimeCalendar": "Calendrier des anim\u00e9s" + "AnimeCalendar": "Calendrier des anim\u00e9s", + "Review_Summary": "R\u00e9sum\u00e9 de l'examen", + "Summary": "R\u00e9sum\u00e9", + "Pros": "Avantages", + "Cons": "Inconv\u00e9nients", + "Verdict": "Verdict", + "Review_Summary_Desc": "Le verdict des avis est g\u00e9n\u00e9r\u00e9 par l'IA \u00e0 l'aide des avis suivants et peut ne pas \u00eatre exact. Veuillez lire les commentaires pour plus d'informations.", + "Generating_Review_Summary": "G\u00e9n\u00e9ration du r\u00e9sum\u00e9 des avis", + "Generating": "G\u00e9n\u00e9rateur" } \ No newline at end of file diff --git a/lib/l10n/intl_id_ID.arb b/lib/l10n/intl_id_ID.arb index 727d416..cbdb533 100644 --- a/lib/l10n/intl_id_ID.arb +++ b/lib/l10n/intl_id_ID.arb @@ -945,5 +945,13 @@ "NextShow": "Pertunjukan Berikutnya", "Show": "Menunjukkan", "ReleaseStartDate": "Tanggal Rilis", - "AnimeCalendar": "Kalender Anime" + "AnimeCalendar": "Kalender Anime", + "Review_Summary": "Ringkasan Tinjauan", + "Summary": "Ringkasan", + "Pros": "Kelebihan", + "Cons": "Kontra", + "Verdict": "Dakwaan", + "Review_Summary_Desc": "Putusan Tinjauan dibuat oleh AI menggunakan ulasan berikut dan mungkin tidak akurat. Silakan baca ulasan untuk informasi lebih lanjut.", + "Generating_Review_Summary": "Menghasilkan Ringkasan Tinjauan", + "Generating": "Menghasilkan" } \ No newline at end of file diff --git a/lib/l10n/intl_ja.arb b/lib/l10n/intl_ja.arb index be30f87..7fce79d 100644 --- a/lib/l10n/intl_ja.arb +++ b/lib/l10n/intl_ja.arb @@ -945,5 +945,13 @@ "NextShow": "\u6b21\u306e\u30b7\u30e7\u30fc", "Show": "\u898b\u305b\u308b", "ReleaseStartDate": "\u767a\u58f2\u65e5", - "AnimeCalendar": "\u30a2\u30cb\u30e1\u30ab\u30ec\u30f3\u30c0\u30fc" + "AnimeCalendar": "\u30a2\u30cb\u30e1\u30ab\u30ec\u30f3\u30c0\u30fc", + "Review_Summary": "\u30ec\u30d3\u30e5\u30fc\u306e\u6982\u8981", + "Summary": "\u307e\u3068\u3081", + "Pros": "\u9577\u6240", + "Cons": "\u77ed\u6240", + "Verdict": "\u8a55\u6c7a", + "Review_Summary_Desc": "\u30ec\u30d3\u30e5\u30fc\u8a55\u6c7a\u306f\u4ee5\u4e0b\u306e\u30ec\u30d3\u30e5\u30fc\u3092\u4f7f\u7528\u3057\u3066 AI \u306b\u3088\u3063\u3066\u751f\u6210\u3055\u308c\u3066\u304a\u308a\u3001\u6b63\u78ba\u3067\u306f\u306a\u3044\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\u8a73\u7d30\u306b\u3064\u3044\u3066\u306f\u30ec\u30d3\u30e5\u30fc\u3092\u304a\u8aad\u307f\u304f\u3060\u3055\u3044\u3002", + "Generating_Review_Summary": "\u30ec\u30d3\u30e5\u30fc\u6982\u8981\u306e\u751f\u6210", + "Generating": "\u751f\u6210\u4e2d" } \ No newline at end of file diff --git a/lib/l10n/intl_ko_KR.arb b/lib/l10n/intl_ko_KR.arb index 93be987..f67cd2f 100644 --- a/lib/l10n/intl_ko_KR.arb +++ b/lib/l10n/intl_ko_KR.arb @@ -945,5 +945,13 @@ "NextShow": "\ub2e4\uc74c \uc1fc", "Show": "\ubcf4\uc5ec\uc8fc\ub2e4", "ReleaseStartDate": "\ucd9c\uc2dc\uc77c", - "AnimeCalendar": "\uc560\ub2c8\uba54\uc774\uc158 \ub2ec\ub825" + "AnimeCalendar": "\uc560\ub2c8\uba54\uc774\uc158 \ub2ec\ub825", + "Review_Summary": "\uac80\ud1a0 \uc694\uc57d", + "Summary": "\uc694\uc57d", + "Pros": "\uc7a5\uc810", + "Cons": "\ub2e8\uc810", + "Verdict": "\ud3c9\uacb0", + "Review_Summary_Desc": "\ub9ac\ubdf0 \ud3c9\uacb0\uc740 \ub2e4\uc74c \ub9ac\ubdf0\ub97c \uc0ac\uc6a9\ud558\uc5ec AI\uc5d0 \uc758\ud574 \uc0dd\uc131\ub418\ubbc0\ub85c \uc815\ud655\ud558\uc9c0 \uc54a\uc744 \uc218 \uc788\uc2b5\ub2c8\ub2e4. \uc790\uc138\ud55c \ub0b4\uc6a9\uc740 \ub9ac\ubdf0\ub97c \uc77d\uc5b4\ubcf4\uc2dc\uae30 \ubc14\ub78d\ub2c8\ub2e4.", + "Generating_Review_Summary": "\ub9ac\ubdf0 \uc694\uc57d \uc0dd\uc131 \uc911", + "Generating": "\uc0dd\uc131 \uc911" } \ No newline at end of file diff --git a/lib/l10n/intl_pt_BR.arb b/lib/l10n/intl_pt_BR.arb index 38510e7..d28a340 100644 --- a/lib/l10n/intl_pt_BR.arb +++ b/lib/l10n/intl_pt_BR.arb @@ -946,5 +946,13 @@ "NextShow": "Pr\u00f3ximo programa", "Show": "Mostrar", "ReleaseStartDate": "Data de lan\u00e7amento", - "AnimeCalendar": "Calend\u00e1rio de animes" + "AnimeCalendar": "Calend\u00e1rio de animes", + "Review_Summary": "Resumo da revis\u00e3o", + "Summary": "Resumo", + "Pros": "Pr\u00f3s", + "Cons": "Contras", + "Verdict": "Veredicto", + "Review_Summary_Desc": "O veredicto da revis\u00e3o \u00e9 gerado pela IA usando as an\u00e1lises a seguir e pode n\u00e3o ser preciso. Por favor, leia os coment\u00e1rios para obter mais informa\u00e7\u00f5es.", + "Generating_Review_Summary": "Gerando Resumo de Revis\u00e3o", + "Generating": "Gerando" } \ No newline at end of file diff --git a/lib/l10n/intl_ru_RU.arb b/lib/l10n/intl_ru_RU.arb index 118c2cf..685a032 100644 --- a/lib/l10n/intl_ru_RU.arb +++ b/lib/l10n/intl_ru_RU.arb @@ -943,5 +943,13 @@ "NextShow": "\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 \u0448\u043e\u0443", "Show": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c", "ReleaseStartDate": "\u0414\u0430\u0442\u0430 \u0432\u044b\u043f\u0443\u0441\u043a\u0430", - "AnimeCalendar": "\u0410\u043d\u0438\u043c\u0435-\u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c" + "AnimeCalendar": "\u0410\u043d\u0438\u043c\u0435-\u043a\u0430\u043b\u0435\u043d\u0434\u0430\u0440\u044c", + "Review_Summary": "\u041e\u0431\u0437\u043e\u0440 \u043e\u0431\u0437\u043e\u0440\u0430", + "Summary": "\u041a\u0440\u0430\u0442\u043a\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435", + "Pros": "\u041f\u043b\u044e\u0441\u044b", + "Cons": "\u041c\u0438\u043d\u0443\u0441\u044b", + "Verdict": "\u0412\u0435\u0440\u0434\u0438\u043a\u0442", + "Review_Summary_Desc": "\u0412\u0435\u0440\u0434\u0438\u043a\u0442 \u043e\u0431\u0437\u043e\u0440\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0438\u0441\u043a\u0443\u0441\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0438\u043d\u0442\u0435\u043b\u043b\u0435\u043a\u0442\u043e\u043c \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u043e\u0431\u0437\u043e\u0440\u043e\u0432 \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043d\u0435\u0442\u043e\u0447\u043d\u044b\u043c. \u041f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u0439\u0442\u0435 \u043e\u0442\u0437\u044b\u0432\u044b \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438.", + "Generating_Review_Summary": "\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0441\u0432\u043e\u0434\u043a\u0438 \u043e\u0431\u0437\u043e\u0440\u0430", + "Generating": "\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435" } \ No newline at end of file diff --git a/lib/l10n/intl_tr_TR.arb b/lib/l10n/intl_tr_TR.arb index dbb06c1..d724097 100644 --- a/lib/l10n/intl_tr_TR.arb +++ b/lib/l10n/intl_tr_TR.arb @@ -943,5 +943,13 @@ "NextShow": "Sonraki G\u00f6steri", "Show": "G\u00f6stermek", "ReleaseStartDate": "Yay\u0131n tarihi", - "AnimeCalendar": "Anime Takvimi" + "AnimeCalendar": "Anime Takvimi", + "Review_Summary": "\u0130nceleme \u00d6zeti", + "Summary": "\u00d6zet", + "Pros": "Art\u0131lar\u0131", + "Cons": "Eksileri", + "Verdict": "Karar", + "Review_Summary_Desc": "\u0130nceleme Karar\u0131 yapay zeka taraf\u0131ndan a\u015fa\u011f\u0131daki incelemeler kullan\u0131larak olu\u015fturulur ve do\u011fru olmayabilir. Daha fazla bilgi i\u00e7in l\u00fctfen yorumlar\u0131 okuyun.", + "Generating_Review_Summary": "\u0130nceleme \u00d6zeti Olu\u015fturuluyor", + "Generating": "\u00dcretiliyor" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 340639f..2391797 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,7 +20,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'package:dynamic_color/dynamic_color.dart'; -import 'package:restart_app/restart_app.dart'; +import 'package:flutter_phoenix/flutter_phoenix.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; /// Global UserData @@ -55,11 +55,11 @@ void main() async { } catch (e) {} } - runApp(_buildProvider(node)); + runApp(Phoenix(child: _buildProvider(node))); } -void restartApp() { - Restart.restartApp(); +void restartApp(BuildContext context) { + Phoenix.rebirth(context); } MultiProvider _buildProvider(Node? node) { diff --git a/lib/notifservice.dart b/lib/notifservice.dart index 877ef18..109a5ca 100644 --- a/lib/notifservice.dart +++ b/lib/notifservice.dart @@ -15,8 +15,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart' as http; -import 'package:notification_permissions/notification_permissions.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/timezone.dart' as tz; @@ -321,31 +321,35 @@ class NotificationService { } } - await flutterLocalNotificationsPlugin.zonedSchedule( - serviceId * 100 + node.id!, - _replaceTags(title) ?? "DailyAnimeList - ${S.current.Episode_Reminder}", - _replaceTags(body) ?? - "${node.title} - Episode $episode ${S.current.just_got_aired}!!", - exactDate != null - ? tz.TZDateTime.from(exactDate, tz.local) - : tz.TZDateTime.now(tz.local).add(addTime), - NotificationDetails( - android: AndroidNotificationDetails( - channel.channelId, - channel.channelName, - channelDescription: channel.channelDescription, - priority: Priority.high, - styleInformation: styleInfo, - icon: 'ic_stat_name', - largeIcon: largeIconBitmap, - category: AndroidNotificationCategory.reminder, + try { + await flutterLocalNotificationsPlugin.zonedSchedule( + serviceId * 100 + node.id!, + _replaceTags(title) ?? "DailyAnimeList - ${S.current.Episode_Reminder}", + _replaceTags(body) ?? + "${node.title} - Episode $episode ${S.current.just_got_aired}!!", + exactDate != null + ? tz.TZDateTime.from(exactDate, tz.local) + : tz.TZDateTime.now(tz.local).add(addTime), + NotificationDetails( + android: AndroidNotificationDetails( + channel.channelId, + channel.channelName, + channelDescription: channel.channelDescription, + priority: Priority.high, + styleInformation: styleInfo, + icon: 'ic_stat_name', + largeIcon: largeIconBitmap, + category: AndroidNotificationCategory.reminder, + ), ), - ), - androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, - payload: jsonEncode(node.toJson()), - uiLocalNotificationDateInterpretation: - UILocalNotificationDateInterpretation.absoluteTime, - ); + androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, + payload: jsonEncode(node.toJson()), + uiLocalNotificationDateInterpretation: + UILocalNotificationDateInterpretation.absoluteTime, + ); + } catch (e) { + logDal(e); + } } String? _replaceTags(String? body) { @@ -374,9 +378,10 @@ class NotificationService { Future askForPermission() async { if (user.pref.notifPref.onPTWGoesToWatching || user.pref.notifPref.onWatchingListUpdated) { - final currStatus = - await NotificationPermissions.getNotificationPermissionStatus(); - if (currStatus == PermissionStatus.denied) { + final notifPerm = await Permission.notification.status; + final alamPerm = await Permission.scheduleExactAlarm.status; + if (notifPerm == PermissionStatus.denied || + alamPerm == PermissionStatus.denied) { bool allowed = await showConfirmationDialog( alertTitle: S.current.ConfirmNotifPerm, desc: S.current.ConfirmNotifPermDesc, @@ -384,8 +389,7 @@ class NotificationService { ); if (allowed) { allowed = await _askNotifPermissionUsingLocal(); - } - if (!allowed) { + } else { user.pref.notifPref.onPTWGoesToWatching = false; user.pref.notifPref.onWatchingListUpdated = false; user.setIntance(); @@ -397,11 +401,25 @@ class NotificationService { Future _askNotifPermissionUsingLocal() async { FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); - return (await flutterLocalNotificationsPlugin - .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.requestNotificationsPermission()) ?? - false; + return ((await _getAlarmPerm(flutterLocalNotificationsPlugin)) ?? false) && + ((await _getNotificationPerm(flutterLocalNotificationsPlugin)) ?? + false); + } + + Future _getAlarmPerm( + FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin) async { + return await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.requestExactAlarmsPermission(); + } + + Future _getNotificationPerm( + FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin) async { + return await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.requestNotificationsPermission(); } static void onDidReceiveBackgroundNotificationResponse( diff --git a/lib/pages/animedetailed/reviewpage.dart b/lib/pages/animedetailed/reviewpage.dart index fb23e64..ae797a8 100644 --- a/lib/pages/animedetailed/reviewpage.dart +++ b/lib/pages/animedetailed/reviewpage.dart @@ -1,5 +1,9 @@ +// ignore_for_file: unused_import + import 'package:cached_network_image/cached_network_image.dart'; import 'package:collection/collection.dart'; +import 'package:dailyanimelist/api/auth/auth.dart'; +import 'package:dailyanimelist/api/dalapi.dart'; import 'package:dailyanimelist/constant.dart'; import 'package:dailyanimelist/generated/l10n.dart'; import 'package:dailyanimelist/screens/contentdetailedscreen.dart'; @@ -7,13 +11,19 @@ import 'package:dailyanimelist/screens/plainscreen.dart'; import 'package:dailyanimelist/util/pathutils.dart'; import 'package:dailyanimelist/widgets/avatarwidget.dart'; import 'package:dailyanimelist/widgets/common/share_builder.dart'; +import 'package:dailyanimelist/widgets/customanimations.dart'; import 'package:dailyanimelist/widgets/customappbar.dart'; import 'package:dailyanimelist/widgets/custombutton.dart'; +import 'package:dailyanimelist/widgets/customfuture.dart'; +import 'package:dailyanimelist/widgets/fadingeffect.dart'; +import 'package:dailyanimelist/widgets/home/accordion.dart'; +import 'package:dailyanimelist/widgets/loading/shimmerwidget.dart'; import 'package:dailyanimelist/widgets/selectbottom.dart'; import 'package:dailyanimelist/widgets/slivers.dart'; import 'package:dailyanimelist/widgets/translator.dart'; import 'package:dal_commons/dal_commons.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:intl/intl.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; @@ -710,3 +720,187 @@ class ReviewWidget extends StatelessWidget { ); } } + +class ReviewGeneratedSummary extends StatefulWidget { + final List reviews; + final Future? reviewSummaryFuture; + const ReviewGeneratedSummary({ + super.key, + required this.reviews, + this.reviewSummaryFuture, + }); + + @override + State createState() => _ReviewGeneratedSummaryState(); +} + +class _ReviewGeneratedSummaryState extends State { + late Future reviewSummaryFuture; + bool _hasReviewSummary = false; + + List get reviewsText => + widget.reviews.map((e) => e.reviewText ?? '').toList(); + + @override + void initState() { + super.initState(); + if (widget.reviewSummaryFuture != null) { + _hasReviewSummary = true; + reviewSummaryFuture = widget.reviewSummaryFuture!; + } else { + _hasReviewSummary = false; + WidgetsBinding.instance + .addPostFrameCallback((_) => _handlePostFrameCallback()); + } + } + + void _handlePostFrameCallback() { + DalApi.i.isFeatureEnabled(FeatureFlag.aireviews).then((value) { + if (value) { + _hasReviewSummary = true; + reviewSummaryFuture = DalApi.i.getReviewsSummary(reviewsText); + if (mounted) { + setState(() {}); + } + } + }); + } + + @override + Widget build(BuildContext context) { + if (!_hasReviewSummary) return SB.z; + if (widget.reviewSummaryFuture == null) { + return _buildIcon(); + } + return StateFullFutureWidget( + done: (sp) => _buildSummary(sp.data), + loadingChild: _reviewAnimation(), + future: () => reviewSummaryFuture, + ); + } + + Widget _buildIcon() { + return AvatarWidget( + url: 'assets/images/gemini.png', + width: 30, + height: 30, + onLongPress: _onIconTap, + isNetworkImage: false, + onTap: _onIconTap, + ); + } + + void _onIconTap() { + showAdaptiveDialog( + context: context, + builder: (context) { + return AlertDialog( + title: Text(S.current.Review_Summary), + contentPadding: EdgeInsets.zero, + content: SingleChildScrollView( + child: ReviewGeneratedSummary( + reviews: widget.reviews, + reviewSummaryFuture: reviewSummaryFuture, + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text(S.current.Close), + ), + ], + ); + }); + } + + Widget _reviewAnimation() { + return SizedBox( + height: 300, + child: Stack( + children: [ + Center( + child: SizedBox( + height: 320, + width: 200, + child: InterlaceAnimation( + colorScheme: Theme.of(context).colorScheme), + ), + ), + Center( + child: Text( + S.current.Generating, + style: Theme.of(context).textTheme.titleLarge, + textAlign: TextAlign.center, + ), + ), + ], + ), + ); + } + + Widget _buildSummary(ContentReviewSummary? data) { + if (data == null || data.verdict.isEmpty) return SB.z; + return Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 15), + child: _summaryContent(data), + ); + } + + Widget _summaryContent(ContentReviewSummary data) { + final verdictWidget = _reviewItem( + ReviewItem(title: S.current.Verdict, description: data.verdict), + ToolTipButton( + child: Icon(Icons.info_outline), + message: S.current.Review_Summary_Desc), + true, + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SB.h10, + verdictWidget, + ...data.pros + .map((item) => _reviewItem(item, _additionalText(S.current.Pros))), + ...data.cons + .map((item) => _reviewItem(item, _additionalText(S.current.Cons))), + SB.h10, + ], + ); + } +} + +Widget _reviewItem( + ReviewItem item, + Widget additional, [ + bool defaultExpanded = false, +]) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: Accordion( + title: item.title, + atStartExpanded: defaultExpanded, + isOpen: defaultExpanded, + additional: [additional], + child: TranslaterWidget( + content: item.description, + done: (data) => Padding( + padding: const EdgeInsets.symmetric(vertical: 5), + child: Text(data ?? item.description, style: TextStyle(fontSize: 13)), + ), + ), + ), + ); +} + +SizedBox _additionalText(String additional) { + return SizedBox( + height: 30.0, + child: PlainButton( + padding: EdgeInsets.zero, + onPressed: () {}, + child: Text(additional), + ), + ); +} diff --git a/lib/pages/settings/backup_restore.dart b/lib/pages/settings/backup_restore.dart index 186be43..ee6526c 100644 --- a/lib/pages/settings/backup_restore.dart +++ b/lib/pages/settings/backup_restore.dart @@ -55,7 +55,7 @@ class _BackUpAndRestorePageState extends State { await user.refreshAuthStatus(); await StreamUtils.i.init(); await showToast(S.current.Restore_Sucess); - restartApp(); + restartApp(context); } else { showToast(S.current.Restore_fail); } diff --git a/lib/pages/settings/themesettings.dart b/lib/pages/settings/themesettings.dart index 944623d..9ef23c5 100644 --- a/lib/pages/settings/themesettings.dart +++ b/lib/pages/settings/themesettings.dart @@ -60,7 +60,7 @@ class _ThemeSettingsState extends State { user.pref.firstTimePref = FirstTimePref(bg: false, news: false); } await user.setIntance(); - restartApp(); + restartApp(context); } void reset() { diff --git a/lib/screens/contentdetailedscreen.dart b/lib/screens/contentdetailedscreen.dart index 82d7a5b..99c4261 100644 --- a/lib/screens/contentdetailedscreen.dart +++ b/lib/screens/contentdetailedscreen.dart @@ -61,6 +61,7 @@ class VisibleSection { final skipTitle; final bool isTab; final VoidCallback? onViewAll; + final Widget? additionalWidget; VisibleSection( this.title, @@ -68,6 +69,7 @@ class VisibleSection { this.isTab = true, this.onViewAll, this.skipTitle = false, + this.additionalWidget, }); } @@ -360,6 +362,8 @@ class _ContentDetailedScreenState extends State reviews: animeDetailedHtml!.animeReviewList!, horizPadding: horizPadding, ), + additionalWidget: ReviewGeneratedSummary( + reviews: animeDetailedHtml!.animeReviewList!), onViewAll: _reviewsShowAll, )), TabType.Recommendations => _nullIf( @@ -1080,8 +1084,13 @@ class _ContentDetailedScreenState extends State crossAxisAlignment: CrossAxisAlignment.center, children: [ text(section.title, fontSize: 22), - if (section.onViewAll != null) ...[ + if (section.onViewAll != null || + section.additionalWidget != null) ...[ Expanded(child: SB.z), + if (section.additionalWidget != null) ...[ + section.additionalWidget!, + SB.w15, + ], Padding( padding: const EdgeInsets.only(right: horizPadding), child: PlainButton( @@ -1704,4 +1713,3 @@ class NoScalingAnimation extends FloatingActionButtonAnimator { return Tween(begin: 1.0, end: 1.0).animate(parent); } } - diff --git a/lib/screens/forumposts.dart b/lib/screens/forumposts.dart index baddbbc..c7cfee2 100644 --- a/lib/screens/forumposts.dart +++ b/lib/screens/forumposts.dart @@ -17,7 +17,7 @@ import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; import 'package:dailyanimelist/generated/l10n.dart'; import 'package:scroll_to_index/scroll_to_index.dart'; -import 'package:share/share.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:shimmer/shimmer.dart'; import 'generalsearchscreen.dart'; diff --git a/lib/user/user.dart b/lib/user/user.dart index 88566d1..0e699d6 100644 --- a/lib/user/user.dart +++ b/lib/user/user.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:dailyanimelist/api/auth/auth.dart'; import 'package:dailyanimelist/api/auth/authresp.dart'; import 'package:dailyanimelist/generated/l10n.dart'; import 'package:dailyanimelist/theme/theme.dart'; diff --git a/lib/widgets/common/image_preview.dart b/lib/widgets/common/image_preview.dart index f5aa21e..127111f 100644 --- a/lib/widgets/common/image_preview.dart +++ b/lib/widgets/common/image_preview.dart @@ -11,7 +11,7 @@ import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view_gallery.dart'; -import 'package:share/share.dart'; +import 'package:share_plus/share_plus.dart'; import 'package:path/path.dart' as p; void zoomInImage(BuildContext context, String url, [bool showButtons = true]) { @@ -93,7 +93,8 @@ void saveImage(String url) async { /// download image from url and share it void shareImage(String url) async { var path = await downloadImage(url); - Share.shareFiles([path], text: 'Image from DailyAnimeList'); + XFile file = XFile(path); + Share.shareXFiles([file], text: 'Image from DailyAnimeList'); } Future downloadImage(String url) async { diff --git a/lib/widgets/common/share_builder.dart b/lib/widgets/common/share_builder.dart index 90cd12b..6e5d038 100644 --- a/lib/widgets/common/share_builder.dart +++ b/lib/widgets/common/share_builder.dart @@ -5,7 +5,7 @@ import 'package:dal_commons/commons.dart'; import 'package:flutter/material.dart'; import 'package:dailyanimelist/generated/l10n.dart'; import 'package:flutter/services.dart'; -import 'package:share/share.dart'; +import 'package:share_plus/share_plus.dart'; class ShareOutputProps { final bool includeTitle; diff --git a/lib/widgets/customanimations.dart b/lib/widgets/customanimations.dart new file mode 100644 index 0000000..d030046 --- /dev/null +++ b/lib/widgets/customanimations.dart @@ -0,0 +1,263 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class InterlaceAnimation extends StatefulWidget { + final ColorScheme colorScheme; + const InterlaceAnimation({super.key, required this.colorScheme}); + @override + _InterlaceAnimationState createState() => _InterlaceAnimationState(); +} + +class _InterlaceAnimationState extends State + with TickerProviderStateMixin { + static final _shiftTween = Tween(begin: -pi, end: pi); + static final _aTween = Tween(begin: -10.0, end: 10.0); + static final _bTween = Tween(begin: -70.0, end: 70.0); + static final _cTween = Tween(begin: -100.0, end: 100.0); + static final _dTween = Tween(begin: -200.0, end: 200.0); + static final _eTween = Tween(begin: -300.0, end: 300.0); + + late ColorTween _lightTween1; + late ColorTween _lightTween2; + late ColorTween _glowTween1; + late ColorTween _glowTween2; + + static final _glowInterval = CurveTween(curve: Interval(0.9, 1.0)); + + static final _curve = CurveTween(curve: Curves.easeInOut); + + late AnimationController _shiftController; + late AnimationController _aController; + late AnimationController _bController; + late AnimationController _cController; + late AnimationController _dController; + late AnimationController _eController; + + late AnimationController _glowController; + + @override + void initState() { + super.initState(); + + final init = (int duration, double value) { + return AnimationController( + vsync: this, + duration: Duration(milliseconds: duration), + value: value, + ); + }; + + _shiftController = init(8000, 0.0); + _aController = init(6000, 0.7); + _bController = init(4000, 0.3); + _cController = init(8000, 0.8); + _dController = init(8000, 0.0); + _eController = init(8000, 0.6); + _glowController = init(4000, 0.0); + + final scheme = widget.colorScheme; + + _lightTween1 = ColorTween( + begin: scheme.primary, + end: scheme.secondary, + ); + + _lightTween2 = ColorTween( + begin: scheme.secondary, + end: scheme.primary, + ); + + _glowTween1 = ColorTween( + begin: scheme.primary, + end: scheme.secondary, + ); + + _glowTween2 = ColorTween( + begin: scheme.secondary, + end: scheme.primary, + ); + + _shiftController.addStatusListener((status) { + if (status == AnimationStatus.completed) { + _shiftController.forward(from: 0.0); + } + }); + + [ + _aController, + _bController, + _cController, + _dController, + _eController, + _glowController, + ].forEach((c) { + c.addStatusListener((status) { + if (status == AnimationStatus.completed) { + c.reverse(); + } else if (status == AnimationStatus.dismissed) { + c.forward(); + } + }); + + c.forward(); + }); + + _shiftController.forward(); + } + + @override + void dispose() { + _shiftController.dispose(); + _aController.dispose(); + _bController.dispose(); + _cController.dispose(); + _dController.dispose(); + _eController.dispose(); + _glowController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: AnimatedBuilder( + animation: _shiftController, + builder: (context, _) { + return RepaintBoundary( + child: CustomPaint( + painter: CirclePainter( + shift: _shiftTween.evaluate(_shiftController), + a: _aTween.chain(_curve).evaluate(_aController), + b: _bTween.chain(_curve).evaluate(_bController), + c: _cTween.chain(_curve).evaluate(_cController), + d: _dTween.chain(_curve).evaluate(_dController), + e: _eTween.chain(_curve).evaluate(_eController), + light1: _lightTween1 + .chain(_glowInterval) + .chain(_curve) + .evaluate(_glowController)!, + light2: _lightTween2 + .chain(_glowInterval) + .chain(_curve) + .evaluate(_glowController)!, + glow1: _glowTween1 + .chain(_glowInterval) + .chain(_curve) + .evaluate(_glowController)!, + glow2: _glowTween2 + .chain(_glowInterval) + .chain(_curve) + .evaluate(_glowController)!, + ), + ), + ); + }, + ), + ), + ); + } +} + +class CirclePainter extends CustomPainter { + CirclePainter({ + required this.shift, + required this.a, + required this.b, + required this.c, + required this.d, + required this.e, + required this.light1, + required this.light2, + required this.glow1, + required this.glow2, + }); + + final double shift; + final double a; + final double b; + final double c; + final double d; + final double e; + + final Color light1; + final Color light2; + final Color glow1; + final Color glow2; + + double getHarmonic(double x, double shift, List components) { + double y = 0; + + final angle = sin(x + shift); + for (var i = 0; i < components.length; i++) { + y += components[i] * angle / (i + 1); + } + + return y; + } + + Offset polarToCartesian({required double distance, required double angle}) { + final x = distance * cos(angle); + final y = distance * sin(angle); + return Offset(x, y); + } + + @override + void paint(Canvas canvas, Size size) { + final path = Path(); + + var points = []; + for (var i = 0; i <= pi * 2 * 1000; i++) { + final point = polarToCartesian( + distance: 110.0 + getHarmonic(i / 1000, shift, [a, b, c, d, e]), + angle: i / 1000, + ); + + points.add(point); + } + + path.addPolygon(points, false); + + final glowShader = LinearGradient( + colors: [ + glow1, + glow2, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ).createShader(Rect.fromCenter( + center: Offset(size.width / 2, size.height / 2), + height: 110.0, + width: 110.0, + )); + + final strokeShader = LinearGradient( + colors: [light1, light2], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ).createShader(Rect.fromCenter( + center: Offset(size.width / 2, size.height / 2), + height: 110.0, + width: 110.0, + )); + + final glowPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 18.0 + ..shader = glowShader + ..maskFilter = MaskFilter.blur(BlurStyle.outer, 14.0); + + final strokePaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 8.0 + ..shader = strokeShader + ..maskFilter = MaskFilter.blur(BlurStyle.outer, 2.0); + + canvas.drawPath(path, glowPaint); + canvas.drawPath(path, strokePaint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} diff --git a/lib/widgets/customappbar.dart b/lib/widgets/customappbar.dart index ab7cc53..c2fba29 100644 --- a/lib/widgets/customappbar.dart +++ b/lib/widgets/customappbar.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:connectivity/connectivity.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dailyanimelist/api/maluser.dart'; import 'package:dailyanimelist/constant.dart'; import 'package:dailyanimelist/enums.dart'; diff --git a/lib/widgets/home/animecard.dart b/lib/widgets/home/animecard.dart index 6f47d0b..b34546d 100644 --- a/lib/widgets/home/animecard.dart +++ b/lib/widgets/home/animecard.dart @@ -203,13 +203,14 @@ class AnimeGridCard extends StatelessWidget { child: Text( nodeTitle, style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontSize: 13, + fontSize: 14, color: Colors.white, overflow: TextOverflow.fade, + fontWeight: FontWeight.w400, shadows: [ Shadow( color: Colors.black, - offset: Offset(.5, .5), + offset: Offset(1, 1), blurRadius: 2, ) ] @@ -394,7 +395,7 @@ class AnimeGridCard extends StatelessWidget { left: 0, right: 0, child: SizedBox( - height: 30, + height: 80, child: ClipRRect( borderRadius: BorderRadius.circular(borderRadius), child: CustomPaint( @@ -402,7 +403,7 @@ class AnimeGridCard extends StatelessWidget { color: Colors.black, start: 5, end: 255, - extend: 5, + extend: 10, ), child: SB.z), ), diff --git a/lib/widgets/home/notifications.dart b/lib/widgets/home/notifications.dart index a362d76..c43c9f9 100644 --- a/lib/widgets/home/notifications.dart +++ b/lib/widgets/home/notifications.dart @@ -72,6 +72,7 @@ class _AnimeCalendarWidgetState extends State { fromCache: fromCache, limit: 500, ); + DalApi.i.resetScheduleForMalIds(); } @override @@ -343,7 +344,7 @@ class __ScheduleCustomListState extends State<_ScheduleCustomList> { Widget _buildAnimeListTile(int index, _SchduledNode node, int dayIndex) { if (node.currentDay) { - final nextNode = _currentDayNodes(dayIndex).tryAt(index + 1); + final nextNode = _getNextClosestNode(node); return _buildCurrentDayTile(node, index, nextNode); } final timestamp = node.scheduleData.timestamp!; @@ -526,4 +527,11 @@ class __ScheduleCustomListState extends State<_ScheduleCustomList> { final timestamp = DateTime.fromMillisecondsSinceEpoch(stamp * 1000); return '${timestamp.hour.toString().padLeft(2, '0')}:${timestamp.minute.toString().padLeft(2, '0')}'; } + + _SchduledNode? _getNextClosestNode(_SchduledNode node) { + var list = widget.scheduleNodeData.values.flattened.where(_filterScheduleNode).toList(); + list.sort((a, b) => a.scheduleData.timestamp! - b.scheduleData.timestamp!); + final index = list.indexOf(node); + return list.tryAt(index + 1); + } } diff --git a/lib/widgets/user/contentbuilder.dart b/lib/widgets/user/contentbuilder.dart index 14aeeb6..2e3a5ee 100644 --- a/lib/widgets/user/contentbuilder.dart +++ b/lib/widgets/user/contentbuilder.dart @@ -270,6 +270,7 @@ class _UserContentBuilderState extends State expand: _enableSearch, child: SizedBox( width: 80.0, + height: 27.0, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 0.0), diff --git a/lib/widgets/user/signinpage.dart b/lib/widgets/user/signinpage.dart index 5de6ca6..8121cee 100644 --- a/lib/widgets/user/signinpage.dart +++ b/lib/widgets/user/signinpage.dart @@ -5,6 +5,7 @@ import 'package:dailyanimelist/user/user.dart'; import 'package:dailyanimelist/widgets/avatarwidget.dart'; import 'package:dailyanimelist/widgets/custombutton.dart'; import 'package:dal_commons/dal_commons.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../../constant.dart'; @@ -118,35 +119,12 @@ class _SigninWidgetState extends State { const SizedBox( height: 30, ), - // user.status == AuthStatus.INPROGRESS - // ? Container( - // child: Center( - // child: loadingCenter(), - // ), - // ) - // : user.status == AuthStatus.UNAUTHENTICATED - // ? Container( padding: EdgeInsets.symmetric(horizontal: 20), width: double.infinity, height: 45, child: PlainButton( - onPressed: () async { - MalAuth.handleSignIn(); - Future.delayed(Duration(seconds: 45)).then((value) { - if (user.status != AuthStatus.AUTHENTICATED) { - logDal("timed out"); - showToast(S.current.Setup_timed_out); - if (mounted) - setState(() { - user.status = AuthStatus.UNAUTHENTICATED; - }); - } else { - if (mounted) setState(() {}); - } - }); - }, - // elevation: 2, + onPressed: () => _handleSignIn(), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(13)), child: Text( @@ -155,8 +133,24 @@ class _SigninWidgetState extends State { style: TextStyle(fontSize: 15), ), ), - ) - // : SB.z + ), + if (kDebugMode) ...[ + SB.h30, + // Enter code manually + Container( + padding: EdgeInsets.symmetric(horizontal: 20), + width: 500, + child: TextFormField( + decoration: InputDecoration(hintText: "Enter code manually"), + onFieldSubmitted: (value) async { + var uri = Uri.tryParse(value); + if (uri == null || !(await MalAuth.checkIfSignIn(uri))) { + showToast("Couldn't handle Code"); + } + }, + ), + ), + ] ]; } @@ -173,6 +167,22 @@ class _SigninWidgetState extends State { ); } + void _handleSignIn() { + MalAuth.handleSignIn(); + Future.delayed(Duration(seconds: 45)).then((value) { + if (user.status != AuthStatus.AUTHENTICATED) { + logDal("timed out"); + showToast(S.current.Setup_timed_out); + if (mounted) + setState(() { + user.status = AuthStatus.UNAUTHENTICATED; + }); + } else { + if (mounted) setState(() {}); + } + }); + } + Widget textWidget(String text) { return Text( text, diff --git a/lib/widgets/web/c_webview.dart b/lib/widgets/web/c_webview.dart index de8e3d7..5cf735d 100644 --- a/lib/widgets/web/c_webview.dart +++ b/lib/widgets/web/c_webview.dart @@ -3,19 +3,46 @@ import 'dart:io'; +import 'package:dailyanimelist/constant.dart'; +import 'package:dailyanimelist/generated/l10n.dart'; +import 'package:dal_commons/dal_commons.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_web_browser/flutter_web_browser.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:flutter_custom_tabs/flutter_custom_tabs_lite.dart' as cc; +import 'package:flutter_custom_tabs/flutter_custom_tabs_lite.dart'; +import 'package:url_launcher/url_launcher.dart' as url_launcher; -Future launchWebView(String url) async { +Future launchWebView(String url, {BuildContext? context}) async { try { if (!kIsWeb && Platform.isAndroid) { - await FlutterWebBrowser.openWebPage(url: url); + await _openInWebView(context, url); } else { - await launchUrl(Uri.parse(url)); + await url_launcher.launchUrl(Uri.parse(url)); } } catch (e) { debugPrint(e.toString()); } } + +Future _openInWebView(BuildContext? context, String url) async { + Color? barColor; + Color? onBarColor; + if (context != null) { + final theme = Theme.of(context); + barColor = theme.colorScheme.surface; + onBarColor = theme.colorScheme.onSurface; + } + try { + await cc.launchUrl( + Uri.parse(url), + options: LaunchOptions( + barColor: barColor, + onBarColor: onBarColor, + barFixingEnabled: false, + ), + ); + } catch (e) { + logDal(e.toString()); + showToast(S.current.No_Connection); + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a6895d0..bd8d020 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,7 +5,7 @@ import FlutterMacOS import Foundation -import connectivity_macos +import connectivity_plus import device_info_plus import dynamic_color import file_selector_macos @@ -13,12 +13,13 @@ import flutter_local_notifications import flutter_secure_storage_macos import package_info_plus import path_provider_foundation +import share_plus import shared_preferences_foundation import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) @@ -26,6 +27,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) diff --git a/pubspec.lock b/pubspec.lock index bf8752b..946f525 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,23 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "67.0.0" + version: "72.0.0" + _macros: + dependency: transitive + description: dart + source: sdk + version: "0.3.2" analyzer: dependency: transitive description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "6.7.0" archive: dependency: transitive description: @@ -77,10 +82,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.2" build_resolvers: dependency: transitive description: @@ -93,18 +98,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + sha256: dd09dd4e2b078992f42aac7f1a622f01882a8492fef08486b27ddde929c19f04 url: "https://pub.dev" source: hosted - version: "2.4.9" + version: "2.4.12" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "7.3.2" built_collection: dependency: "direct main" description: @@ -125,26 +130,26 @@ packages: dependency: "direct main" description: name: cached_network_image - sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819" + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.4.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7 + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "4.1.1" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.3.1" carousel_slider: dependency: "direct main" description: @@ -193,35 +198,19 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" - connectivity: + connectivity_plus: dependency: "direct main" description: - name: connectivity - sha256: a8e91263cf3e25fb5cc95e19dfde4999e32a648ac3b9e8a558a28165731678f8 + name: connectivity_plus + sha256: "2056db5241f96cdc0126bd94459fc4cdc13876753768fc7a31c425e50a7177d0" url: "https://pub.dev" source: hosted - version: "3.0.6" - connectivity_for_web: + version: "6.0.5" + connectivity_plus_platform_interface: dependency: transitive description: - name: connectivity_for_web - sha256: "01a390c1d5adc2ed1fa1f52d120c07fe9fd01166a93f965a832fd6cfc0ea6482" - url: "https://pub.dev" - source: hosted - version: "0.4.0+1" - connectivity_macos: - dependency: transitive - description: - name: connectivity_macos - sha256: "51ae08d5162eca9669b9d8951ed83ce19c5355a81149f94e4dee2740beb93628" - url: "https://pub.dev" - source: hosted - version: "0.2.1+2" - connectivity_platform_interface: - dependency: transitive - description: - name: connectivity_platform_interface - sha256: "2d82e942df9d49f29a24bb07fb5ce085d4a53e47818c62364d2b6deb9e0d7a8e" + name: connectivity_plus_platform_interface + sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" url: "https://pub.dev" source: hosted version: "2.0.1" @@ -245,10 +234,10 @@ packages: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" csslib: dependency: transitive description: @@ -315,18 +304,18 @@ packages: dependency: "direct main" description: name: dio - sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 + sha256: "0dfb6b6a1979dac1c1245e17cef824d7b452ea29bd33d3467269f9bef3715fb0" url: "https://pub.dev" source: hosted - version: "5.5.0+1" + version: "5.6.0" dio_web_adapter: dependency: transitive description: name: dio_web_adapter - sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" + sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.0" dotted_border: dependency: "direct main" description: @@ -363,10 +352,10 @@ packages: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" file: dependency: transitive description: @@ -379,10 +368,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03 + sha256: "167bb619cdddaa10ef2907609feb8a79c16dfa479d3afaf960f8e223f754bf12" url: "https://pub.dev" source: hosted - version: "8.0.0+1" + version: "8.1.2" file_selector_linux: dependency: transitive description: @@ -435,18 +424,18 @@ packages: dependency: "direct main" description: name: flex_color_picker - sha256: "5c846437069fb7afdd7ade6bf37e628a71d2ab0787095ddcb1253bf9345d5f3a" + sha256: "809af4ec82ede3b140ed0219b97d548de99e47aa4b99b14a10f705a2dbbcba5e" url: "https://pub.dev" source: hosted - version: "3.4.1" + version: "3.5.1" flex_seed_scheme: dependency: transitive description: name: flex_seed_scheme - sha256: "4cee2f1d07259f77e8b36f4ec5f35499d19e74e17c7dce5b819554914082bc01" + sha256: cc08c81879ecfd2ab840664ce4770980da0b8a319e35f51bcf763849b7f7596b url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "3.1.2" flutter: dependency: "direct main" description: flutter @@ -456,10 +445,50 @@ packages: dependency: transitive description: name: flutter_cache_manager - sha256: a77f77806a790eb9ba0118a5a3a936e81c4fea2b61533033b2b0c3d50bbde5ea + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + flutter_custom_tabs: + dependency: "direct main" + description: + name: flutter_custom_tabs + sha256: "34167bd15fa3479855c011f868e0789c9569c12b64358ca7250accc5a24c3312" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + flutter_custom_tabs_android: + dependency: transitive + description: + name: flutter_custom_tabs_android + sha256: cf06fde8c002f326dc6cbf69ee3f97c3feead4436229da02d2e2aa39d5a5dbf4 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + flutter_custom_tabs_ios: + dependency: transitive + description: + name: flutter_custom_tabs_ios + sha256: ef2de533bc45fb84fefc3854bc8b1e43001671c6bc6bc55faa57942eecd3f70a + url: "https://pub.dev" + source: hosted + version: "2.1.0" + flutter_custom_tabs_platform_interface: + dependency: transitive + description: + name: flutter_custom_tabs_platform_interface + sha256: e18e9b08f92582123bdb84fb6e4c91804b0579700fed6f887d32fd9a1e96a5d5 url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "2.1.0" + flutter_custom_tabs_web: + dependency: transitive + description: + name: flutter_custom_tabs_web + sha256: "08ae322b11e1972a233d057542279873d0f9d1d5f8159c2c741457239d9d562c" + url: "https://pub.dev" + source: hosted + version: "2.1.0" flutter_dotenv: dependency: "direct main" description: @@ -505,14 +534,22 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_phoenix: + dependency: "direct main" + description: + name: flutter_phoenix + sha256: "39589dac934ea476d0e43fb60c1ddfba58f14960743640c8250dea11c4333378" + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de" url: "https://pub.dev" source: hosted - version: "2.0.19" + version: "2.0.21" flutter_secure_storage: dependency: "direct main" description: @@ -574,14 +611,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_web_browser: - dependency: "direct main" - description: - name: flutter_web_browser - sha256: a5564b736253f745e147b8c4eff86de436324d081974cc1f16bff881134a400f - url: "https://pub.dev" - source: hosted - version: "0.17.1" flutter_web_plugins: dependency: transitive description: flutter @@ -591,10 +620,10 @@ packages: dependency: "direct main" description: name: fluttertoast - sha256: "81b68579e23fcbcada2db3d50302813d2371664afe6165bc78148050ab94bf66" + sha256: "95f349437aeebe524ef7d6c9bde3e6b4772717cf46a0eb6a3ceaddc740b297cc" url: "https://pub.dev" source: hosted - version: "8.2.5" + version: "8.2.8" frontend_server_client: dependency: transitive description: @@ -615,10 +644,10 @@ packages: dependency: transitive description: name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" graphview: dependency: "direct main" description: @@ -679,18 +708,18 @@ packages: dependency: transitive description: name: image_picker_android - sha256: "0f57fee1e8bfadf8cc41818bbcd7f72e53bb768a54d9496355d5e8a5681a19f1" + sha256: "8c5abf0dcc24fe6e8e0b4a5c0b51a5cf30cefdf6407a3213dae61edc75a70f56" url: "https://pub.dev" source: hosted - version: "0.8.12+1" + version: "0.8.12+12" image_picker_for_web: dependency: transitive description: name: image_picker_for_web - sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 + sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.5" image_picker_ios: dependency: transitive description: @@ -743,10 +772,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" intl_utils: dependency: "direct dev" description: @@ -791,26 +820,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" line_icons: dependency: "direct main" description: @@ -835,6 +864,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" matcher: dependency: transitive description: @@ -847,18 +884,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.15.0" mime: dependency: transitive description: @@ -875,14 +912,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - notification_permissions: - dependency: "direct main" + nm: + dependency: transitive description: - name: notification_permissions - sha256: e62e7fbd4b64941764778fc957224567aff10cdc8b0227046bd0a4b9db7b47a6 + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.5.0" octo_image: dependency: transitive description: @@ -915,14 +952,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" - particle_field: - dependency: "direct main" - description: - name: particle_field - sha256: "9a5ebdde32751f82aba64198e9bbd46738e789e275bf9e7a88318ed460be560e" - url: "https://pub.dev" - source: hosted - version: "1.0.0" path: dependency: transitive description: @@ -959,10 +988,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.10" path_provider_foundation: dependency: transitive description: @@ -1007,10 +1036,10 @@ packages: dependency: transitive description: name: permission_handler_android - sha256: eaf2a1ec4472775451e88ca6a7b86559ef2f1d1ed903942ed135e38ea0097dca + sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa" url: "https://pub.dev" source: hosted - version: "12.0.8" + version: "12.0.12" permission_handler_apple: dependency: transitive description: @@ -1023,10 +1052,10 @@ packages: dependency: transitive description: name: permission_handler_html - sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" + sha256: d220eb8476b466d58b161e10b3001d93999010a26228a3fb89c4280db1249546 url: "https://pub.dev" source: hosted - version: "0.1.1" + version: "0.1.3+1" permission_handler_platform_interface: dependency: transitive description: @@ -1107,14 +1136,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - restart_app: - dependency: "direct main" - description: - name: restart_app - sha256: b37daeb1c02fcab30e19d9e30b6fdd215bd53577efd927042eb77cf6f09daadb - url: "https://pub.dev" - source: hosted - version: "1.2.1" retry: dependency: "direct main" description: @@ -1147,38 +1168,46 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.8" - share: + share_plus: dependency: "direct main" description: - name: share - sha256: "97e6403f564ed1051a01534c2fc919cb6e40ea55e60a18ec23cee6e0ce19f4be" + name: share_plus + sha256: "468c43f285207c84bcabf5737f33b914ceb8eb38398b91e5e3ad1698d1b72a52" + url: "https://pub.dev" + source: hosted + version: "10.0.2" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "6ababf341050edff57da8b6990f11f4e99eaba837865e2e6defe16d039619db5" url: "https://pub.dev" source: hosted - version: "2.0.4" + version: "5.0.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + sha256: a7e8467e9181cef109f601e3f65765685786c1a738a83d7fbbde377589c0d974 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.3.1" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "776786cff96324851b656777648f36ac772d88bc4c669acff97b7fce5de3c849" + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.5.2" shared_preferences_linux: dependency: transitive description: @@ -1199,10 +1228,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2" + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" shared_preferences_windows: dependency: transitive description: @@ -1223,10 +1252,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.0" shimmer: dependency: "direct main" description: @@ -1292,10 +1321,10 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" + sha256: "7b41b6c3507854a159e24ae90a8e3e9cc01eb26a477c118d6dca065b5f55453e" url: "https://pub.dev" source: hosted - version: "2.5.4" + version: "2.5.4+2" stack_trace: dependency: transitive description: @@ -1332,10 +1361,10 @@ packages: dependency: transitive description: name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + sha256: a824e842b8a054f91a728b783c177c1e4731f6b124f9192468457a8913371255 url: "https://pub.dev" source: hosted - version: "3.1.0+1" + version: "3.2.0" term_glyph: dependency: transitive description: @@ -1348,10 +1377,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.2" timezone: dependency: "direct main" description: @@ -1384,30 +1413,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" - uni_links: - dependency: "direct main" - description: - name: uni_links - sha256: "051098acfc9e26a9fde03b487bef5d3d228ca8f67693480c6f33fd4fbb8e2b6e" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - uni_links_platform_interface: - dependency: transitive - description: - name: uni_links_platform_interface - sha256: "929cf1a71b59e3b7c2d8a2605a9cf7e0b125b13bc858e55083d88c62722d4507" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - uni_links_web: - dependency: transitive - description: - name: uni_links_web - sha256: "7539db908e25f67de2438e33cc1020b30ab94e66720b5677ba6763b25f6394df" - url: "https://pub.dev" - source: hosted - version: "0.1.0" url_launcher: dependency: "direct main" description: @@ -1420,10 +1425,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9" + sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79 url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "6.3.9" url_launcher_ios: dependency: transitive description: @@ -1500,10 +1505,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.4" watcher: dependency: transitive description: @@ -1516,18 +1521,26 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.0.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" url: "https://pub.dev" source: hosted - version: "2.4.5" + version: "3.0.1" webview_flutter: dependency: transitive description: @@ -1540,10 +1553,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: "0d21cfc3bfdd2e30ab2ebeced66512b91134b39e72e97b43db2d47dda1c4e53a" + sha256: c66651fba15f9d7ddd31daec42da8d6bce46c85610a7127e3ebcb39a4395c3c9 url: "https://pub.dev" source: hosted - version: "3.16.3" + version: "3.16.6" webview_flutter_platform_interface: dependency: transitive description: @@ -1572,18 +1585,18 @@ packages: dependency: transitive description: name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a" url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.5.4" win32_registry: dependency: transitive description: name: win32_registry - sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" + sha256: "723b7f851e5724c55409bb3d5a32b203b3afe8587eaf5dafb93a5fed8ecda0d6" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.4" xdg_directories: dependency: transitive description: @@ -1609,5 +1622,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index d0e6c65..3d10644 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2024.2.2+89 +version: 2024.8.1+93 environment: sdk: '>=3.2.6 <4.0.0' @@ -25,18 +25,16 @@ dependencies: flutter: sdk: flutter - http: ^1.1.0 - provider: ^6.0.2 - shared_preferences: ^2.0.5 - - cached_network_image: ^3.2.0 - flutter_secure_storage: ^9.0.0 - url_launcher: ^6.0.3 - uni_links: ^0.5.1 + http: ^1.2.2 + provider: ^6.1.2 + shared_preferences: ^2.3.2 + cached_network_image: ^3.4.1 + flutter_secure_storage: ^9.2.2 + url_launcher: ^6.3.0 shimmer: ^3.0.0 - intl: ^0.18.1 + intl: ^0.19.0 fluttertoast: ^8.0.9 - connectivity: ^3.0.3 + connectivity_plus: ^6.0.5 carousel_slider: ^5.0.0 html: ^0.15.0 html_unescape: ^2.0.0 @@ -44,25 +42,24 @@ dependencies: fl_chart: ^0.68.0 flutter_html: ^3.0.0-beta.2 scroll_to_index: ^3.0.1 - share: ^2.0.1 + share_plus: ^10.0.2 built_collection: ^5.1.0 flutter_local_notifications: ^17.2.2 - timezone: ^0.9.2 - flutter_web_browser: ^0.17.0 - flex_color_picker: ^3.3.0 + timezone: ^0.9.4 + flutter_custom_tabs: ^2.1.0 + flex_color_picker: ^3.5.1 translator_plus: ^1.0.1 - image_picker: ^1.0.4 + image_picker: ^1.1.2 uuid: ^4.0.0 - dotted_border: ^2.0.0 - retry: ^3.1.0 + dotted_border: ^2.1.0 + retry: ^3.1.2 auto_size_text: ^3.0.0 visibility_detector: ^0.4.0+2 photo_view: ^0.15.0 - dio: ^5.4.0 - notification_permissions: ^0.6.1 + dio: ^5.6.0 infinite_scroll_pagination: ^4.0.0 line_icons: ^2.0.3 - dynamic_color: ^1.6.7 + dynamic_color: ^1.7.0 device_info_plus: ^10.1.2 flutter_dotenv: ^5.1.0 dal_api: @@ -75,14 +72,13 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.0 - restart_app: ^1.2.1 - collection: ^1.17.2 - file_picker: ^8.0.0+1 - permission_handler: ^11.0.1 - path_provider: ^2.1.1 + flutter_phoenix: ^1.1.1 + collection: ^1.18.0 + file_picker: ^8.1.2 + permission_handler: ^11.3.1 + path_provider: ^2.1.4 package_info_plus: ^8.0.2 - particle_field: ^1.0.0 - webviewtube: ^2.0.0 + webviewtube: ^2.1.1 graphview: ^1.2.0 dev_dependencies: @@ -108,6 +104,7 @@ flutter: - assets/images/winter.png - assets/images/fall.jpg + - assets/images/gemini.png - assets/images/star.png - assets/images/user_dal.png - assets/images/myanimelist-logo.jpg diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d207..903f489 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index bfe1ada..5e195a8 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,13 +6,17 @@ #include "generated_plugin_registrant.h" +#include #include #include #include #include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); FileSelectorWindowsRegisterWithRegistrar( @@ -21,6 +25,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index f03fc71..b8cc106 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,10 +3,12 @@ # list(APPEND FLUTTER_PLUGIN_LIST + connectivity_plus dynamic_color file_selector_windows flutter_secure_storage_windows permission_handler_windows + share_plus url_launcher_windows )