├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── extension-request.md │ └── feature_request.md ├── .gitignore ├── .metadata ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── io │ │ │ │ └── github │ │ │ │ └── sayuri192 │ │ │ │ └── wakaranai │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── favicon.png ├── build.yaml ├── docs ├── ChapterReader.png ├── ChapterReader2.png ├── ConcreteView1.png ├── ConcreteView2.png ├── ExplorePage.png ├── HistoryPage.png ├── ServiceViewer.png ├── ServiceViewerSearch.png ├── contribute │ └── readme.md ├── guides │ ├── capyscript_syntax.md │ ├── caypscript_modules.md │ ├── extension_templates │ │ └── manga │ │ │ ├── config.json │ │ │ └── main.capyscript │ ├── extensions.md │ └── readme.md ├── inapp_docs │ ├── 1.jpg │ ├── 2.jpg │ ├── 3.jpg │ └── external_extension_sources.md └── wakaranai.png ├── example.env ├── fonts ├── Ubuntu-B.ttf ├── Ubuntu-BI.ttf ├── Ubuntu-C.ttf ├── Ubuntu-L.ttf ├── Ubuntu-LI.ttf ├── Ubuntu-M.ttf ├── Ubuntu-MI.ttf ├── Ubuntu-R.ttf ├── Ubuntu-RI.ttf ├── Ubuntu-Th.ttf ├── UbuntuMono-B.ttf ├── UbuntuMono-BI.ttf ├── UbuntuMono-R.ttf └── UbuntuMono-RI.ttf ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── blocs │ ├── api_client_controller │ │ ├── api_client_controller_cubit.dart │ │ └── api_client_controller_state.dart │ ├── auth │ │ ├── authentication_cubit.dart │ │ └── authentication_state.dart │ ├── browser_interceptor │ │ ├── browser_interceptor_cubit.dart │ │ └── browser_interceptor_state.dart │ ├── latest_release_cubit │ │ ├── latest_release_cubit.dart │ │ └── latest_release_state.dart │ └── service_view │ │ ├── service_view_cubit.dart │ │ └── service_view_state.dart ├── data │ ├── domain │ │ ├── app_version.dart │ │ ├── base_domain.dart │ │ ├── database │ │ │ ├── anime_episode_activity_domain.dart │ │ │ ├── base_activity_domain.dart │ │ │ ├── base_extension.dart │ │ │ ├── chapter_activity_domain.dart │ │ │ ├── concrete_data_domain.dart │ │ │ ├── extension_domain.dart │ │ │ ├── extension_source_domain.dart │ │ │ └── extension_source_type.dart │ │ ├── github_url_data.dart │ │ ├── latest_release_data.dart │ │ └── ui │ │ │ └── activity_list_item.dart │ ├── entities │ │ ├── anime_episode_activity_table.dart │ │ ├── base_table.dart │ │ ├── chapter_activity_table.dart │ │ ├── concrete_data_table.dart │ │ ├── extension_source_table.dart │ │ └── extension_table.dart │ └── models │ │ ├── configs_repo │ │ └── configs_response │ │ │ ├── repo_configs_response.dart │ │ │ └── repo_configs_response.g.dart │ │ ├── configs_source_type │ │ └── configs_source_type.dart │ │ ├── github │ │ ├── github_response_model.dart │ │ ├── github_response_model.g.dart │ │ ├── payload │ │ │ ├── github_payload_model.dart │ │ │ ├── github_payload_model.g.dart │ │ │ ├── repo │ │ │ │ ├── github_repo_model.dart │ │ │ │ └── github_repo_model.g.dart │ │ │ └── tree │ │ │ │ ├── blob │ │ │ │ ├── github_blob_model.dart │ │ │ │ └── github_blob_model.g.dart │ │ │ │ ├── github_tree_model.dart │ │ │ │ ├── github_tree_model.g.dart │ │ │ │ └── item │ │ │ │ ├── github_tree_item_model.dart │ │ │ │ └── github_tree_item_model.g.dart │ │ └── release_response │ │ │ ├── github_release_response_model.dart │ │ │ └── github_release_response_model.g.dart │ │ ├── protector │ │ ├── protector_storage_item.dart │ │ └── protector_storage_item.g.dart │ │ ├── remote_config │ │ ├── remote_category.dart │ │ ├── remote_config.dart │ │ └── remote_config.g.dart │ │ ├── remote_script │ │ ├── remote_script.dart │ │ └── remote_script.g.dart │ │ └── web_browser_result │ │ ├── web_browser_result.dart │ │ └── web_browser_result.g.dart ├── database │ ├── wakaranai_database.dart │ └── wakaranai_database.g.dart ├── env.dart ├── generated │ ├── intl │ │ ├── messages_all.dart │ │ └── messages_en.dart │ └── l10n.dart ├── l10n │ └── intl_en.arb ├── main.dart ├── repositories │ ├── configs_repository │ │ ├── github │ │ │ ├── github_configs_repository.dart │ │ │ └── github_configs_repository.g.dart │ │ └── local │ │ │ ├── local_configs_repository.dart │ │ │ └── local_configs_repository.g.dart │ ├── database │ │ ├── anime_episode_activity_repository.dart │ │ ├── base_repository.dart │ │ ├── chapter_activity_repository.dart │ │ ├── concerete_data_repository.dart │ │ ├── extension_repository.dart │ │ ├── extension_source_repository.dart │ │ └── repository_providers.dart │ ├── releases_repository │ │ └── github │ │ │ ├── github_releases_repository.dart │ │ │ └── github_releases_repository.g.dart │ └── shared_pref │ │ ├── default_extension_source_repository │ │ └── default_extension_source_repository.dart │ │ └── default_manga_reader_mode_repository │ │ └── default_manga_reader_repository.dart ├── res.dart ├── services │ ├── configs_service │ │ ├── configs_service.dart │ │ ├── github_configs_service.dart │ │ └── repo_configs_service.dart │ ├── protector_storage │ │ └── protector_storage_service.dart │ ├── releases_service │ │ └── releases_service.dart │ └── settings_service │ │ └── settings_service.dart ├── ui │ ├── app_view.dart │ ├── common │ │ └── service_viewer │ │ │ └── service_viewer_loader.dart │ ├── gallery_view_card.dart │ ├── home │ │ ├── activity_history_page │ │ │ ├── acitvity_history_page.dart │ │ │ ├── cubit │ │ │ │ ├── activity_history_cubit_mixin.dart │ │ │ │ ├── anime_activity_history_cubit.dart │ │ │ │ ├── anime_activity_history_state.dart │ │ │ │ ├── manga_activity_history_cubit.dart │ │ │ │ └── manga_activity_history_state.dart │ │ │ └── widgets │ │ │ │ └── activity_history_long_tap_dialog.dart │ │ ├── api_controller_wrapper.dart │ │ ├── concrete_view_cubit_wrapper.dart │ │ ├── configs_page │ │ │ ├── bloc │ │ │ │ └── remote_configs │ │ │ │ │ ├── remote_configs_cubit.dart │ │ │ │ │ └── remote_configs_state.dart │ │ │ ├── config_card.dart │ │ │ ├── configs_group.dart │ │ │ ├── configs_page.dart │ │ │ └── extension_sources │ │ │ │ ├── add_extension_page │ │ │ │ ├── add_extension_page.dart │ │ │ │ ├── add_extension_page_arguments.dart │ │ │ │ └── add_extension_page_result.dart │ │ │ │ ├── cubit │ │ │ │ ├── extension_sources_cubit.dart │ │ │ │ └── extension_sources_state.dart │ │ │ │ ├── extension_sources_page_result.dart │ │ │ │ └── my_extension_sources_page.dart │ │ ├── cubit │ │ │ ├── home_page_cubit.dart │ │ │ └── home_page_state.dart │ │ ├── home_view.dart │ │ ├── service_view_cubit_wrapper.dart │ │ ├── service_viewer_app_bar.dart │ │ ├── settings_page │ │ │ ├── cubit │ │ │ │ └── settings │ │ │ │ │ ├── settings_cubit.dart │ │ │ │ │ └── settings_state.dart │ │ │ └── settings_page.dart │ │ ├── web_browser_page.dart │ │ ├── web_browser_wrapper.dart │ │ └── widgets │ │ │ ├── bottom_navigation_bar_container.dart │ │ │ └── bottom_navigation_bar_item_widget.dart │ ├── local_gallery_view_wrapper.dart │ ├── routes.dart │ ├── services │ │ ├── anime │ │ │ ├── anime_concrete_viewer │ │ │ │ ├── anime_concrete_viewer.dart │ │ │ │ └── anime_player_button.dart │ │ │ ├── anime_iframe_player │ │ │ │ ├── anime_iframe_player.dart │ │ │ │ └── cubit │ │ │ │ │ ├── anime_iframe_player_cubit.dart │ │ │ │ │ └── anime_iframe_player_state.dart │ │ │ └── anime_service_viewer │ │ │ │ ├── anime_service_viewer.dart │ │ │ │ └── anime_service_viewer_body.dart │ │ ├── concrete_viewer_mixin.dart │ │ ├── cubits │ │ │ ├── chapter_view │ │ │ │ ├── chapter_view_cubit.dart │ │ │ │ └── chapter_view_state.dart │ │ │ └── concrete_view │ │ │ │ ├── concrete_view_cubit.dart │ │ │ │ └── concrete_view_state.dart │ │ └── manga │ │ │ └── manga_service_viewer │ │ │ ├── concrete_viewer │ │ │ ├── chapter_viewer │ │ │ │ ├── bottom_modal_settings.dart │ │ │ │ ├── chapter_view_mode.dart │ │ │ │ ├── chapter_viewer.dart │ │ │ │ ├── pages_change_button.dart │ │ │ │ └── settings_overlay.dart │ │ │ ├── manga_concrete_viewer.dart │ │ │ └── manga_provider_button.dart │ │ │ ├── manga_service_viewer.dart │ │ │ └── manga_service_viewer_body.dart │ ├── splashscreen │ │ └── splashscreen_view.dart │ └── widgets │ │ ├── appbar.dart │ │ ├── change_order_icon_button.dart │ │ ├── confirmation_dialog │ │ └── confirmation_dialog.dart │ │ ├── elevated_appbar.dart │ │ ├── expandable_fab_widget.dart │ │ ├── image_widget.dart │ │ ├── infinite_rotation_animation.dart │ │ ├── outlined_text_form_field.dart │ │ ├── primary_button.dart │ │ ├── primary_input_widget.dart │ │ └── snackbars.dart └── utils │ ├── app_colors.dart │ ├── browser.dart │ ├── decoders.dart │ ├── enum_converters.dart │ ├── github_url_parser.dart │ ├── globals.dart │ ├── heroes.dart │ ├── images.dart │ └── text_styles.dart ├── pubspec.lock └── pubspec.yaml /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve the wakaranai app 4 | title: '' 5 | labels: bug 6 | assignees: Sayuri128 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Smartphone (please complete the following information):** 27 | - Device: [e.g. Google Pixel 6a] 28 | - OS: [e.g. Android 12] 29 | - Version [e.g. 0.1.0] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/extension-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Extension Request 3 | about: Request to add a new extension 4 | title: '' 5 | labels: extension request 6 | assignees: Sayuri128 7 | 8 | --- 9 | 10 | **Type** [e.g. Manga] 11 | **Language** [e.g. French] 12 | **Name** [e.g. Mangadex] 13 | **source** [e.g. https://mangadex.org/] 14 | 15 | **Additional context** 16 | Add any other context about the request here. 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for wakaranai app 4 | title: '' 5 | labels: enhancement 6 | assignees: Sayuri128 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | .env 49 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "17025dd88227cd9532c33fa78f5250d548d87e9a" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 17 | base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 18 | - platform: windows 19 | create_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 20 | base_revision: 17025dd88227cd9532c33fa78f5250d548d87e9a 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | 2 | include: package:flutter_lints/flutter.yaml 3 | 4 | linter: 5 | rules: 6 | avoid_print: true 7 | always_declare_return_types: true 8 | annotate_overrides: true 9 | use_build_context_synchronously: false 10 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | def keystoreProperties = new Properties() 26 | def keystorePropertiesFile = rootProject.file('key.properties') 27 | if (keystorePropertiesFile.exists()) { 28 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 29 | } 30 | 31 | android { 32 | compileSdkVersion 34 33 | namespace "io.github.sayuri128.wakaranai" 34 | 35 | compileOptions { 36 | sourceCompatibility JavaVersion.VERSION_1_8 37 | targetCompatibility JavaVersion.VERSION_1_8 38 | } 39 | 40 | kotlinOptions { 41 | jvmTarget = '1.8' 42 | } 43 | 44 | sourceSets { 45 | main.java.srcDirs += 'src/main/kotlin' 46 | } 47 | 48 | defaultConfig { 49 | applicationId "io.github.sayuri128.wakaranai" 50 | minSdkVersion 21 51 | targetSdkVersion 34 52 | versionCode flutterVersionCode.toInteger() 53 | versionName flutterVersionName 54 | } 55 | 56 | signingConfigs { 57 | release { 58 | keyAlias keystoreProperties['keyAlias'] 59 | keyPassword keystoreProperties['keyPassword'] 60 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 61 | storePassword keystoreProperties['storePassword'] 62 | 63 | } 64 | } 65 | 66 | buildTypes { 67 | release { 68 | signingConfig signingConfigs.release 69 | } 70 | debug { 71 | signingConfig signingConfigs.release 72 | } 73 | } 74 | } 75 | 76 | flutter { 77 | source '../..' 78 | } 79 | 80 | dependencies { 81 | 82 | } 83 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue 2 | -dontwarn com.google.errorprone.annotations.CheckReturnValue 3 | -dontwarn com.google.errorprone.annotations.Immutable 4 | -dontwarn com.google.errorprone.annotations.RestrictedApi 5 | -dontwarn javax.annotation.Nullable 6 | -dontwarn javax.annotation.concurrent.GuardedBy -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 23 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/io/github/sayuri192/wakaranai/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package io.github.sayuri128.wakaranai 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | rootProject.buildDir = '../build' 9 | subprojects { 10 | project.buildDir = "${rootProject.buildDir}/${project.name}" 11 | } 12 | subprojects { 13 | project.evaluationDependsOn(':app') 14 | } 15 | 16 | tasks.register("clean", Delete) { 17 | delete rootProject.buildDir 18 | } -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xms2048m -Xmx8196m 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.3.0" apply false 22 | id "org.jetbrains.kotlin.android" version "1.9.22" apply false 23 | } 24 | 25 | include ":app" -------------------------------------------------------------------------------- /assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/assets/favicon.png -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | any_map: true 7 | explicit_to_json: true 8 | sqfentity_gen|property_sqfentity: 9 | generate_for: 10 | - lib/model/*.dart 11 | - lib/model/test/*.dart 12 | -------------------------------------------------------------------------------- /docs/ChapterReader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/ChapterReader.png -------------------------------------------------------------------------------- /docs/ChapterReader2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/ChapterReader2.png -------------------------------------------------------------------------------- /docs/ConcreteView1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/ConcreteView1.png -------------------------------------------------------------------------------- /docs/ConcreteView2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/ConcreteView2.png -------------------------------------------------------------------------------- /docs/ExplorePage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/ExplorePage.png -------------------------------------------------------------------------------- /docs/HistoryPage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/HistoryPage.png -------------------------------------------------------------------------------- /docs/ServiceViewer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/ServiceViewer.png -------------------------------------------------------------------------------- /docs/ServiceViewerSearch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/ServiceViewerSearch.png -------------------------------------------------------------------------------- /docs/contribute/readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/contribute/readme.md -------------------------------------------------------------------------------- /docs/guides/capyscript_syntax.md: -------------------------------------------------------------------------------- 1 | # Capyscript syntax 2 | 3 | Capyscript is a language that is very similar to any other C-like language. It has a few differences 4 | though. 5 | There is no classes, only functions, though classes are planned for the future, so it's easier to 6 | structure extensions. 7 | 8 | ### Functions 9 | 10 | There is no return type for functions, and the return type is inferred from the return value. 11 | Functions are defined like this: 12 | 13 | ```capyscript 14 | 15 | function foo(bar) { 16 | return bar + 1; 17 | } 18 | 19 | function main() { 20 | return foo(1); // returns 2 21 | } 22 | 23 | ``` 24 | 25 | ### Functions as arguments 26 | 27 | Functions can be passed as arguments to other functions. This is useful for callbacks. 28 | 29 | ```capyscript 30 | 31 | function foo(fun) { 32 | fun(); 33 | } 34 | 35 | function bar() { 36 | print("Hello World!"); 37 | } 38 | 39 | function main() { 40 | foo(bar); // prints "Hello World!" 41 | } 42 | 43 | ``` 44 | 45 | ### Variables 46 | 47 | In Capyscript, variables are dynamically typed, meaning that you don't have to specify the type of a 48 | variable when you define it. 49 | 50 | ```capyscript 51 | 52 | foo = "Hello"; 53 | bar = "World!"; 54 | pi = 3.14; 55 | 56 | ``` 57 | 58 | ### if statements 59 | 60 | If statements are defined like this: 61 | 62 | ```capyscript 63 | 64 | var = null; 65 | 66 | if(var == null) { 67 | print("var is null"); 68 | } else { 69 | print("var is not null"); 70 | } 71 | 72 | ``` 73 | 74 | output: 75 | 76 | ``` 77 | var is null 78 | ``` 79 | 80 | ### Arrays 81 | 82 | It is possible to define arrays in Capyscript. Nested multidimensional arrays are also supported. 83 | 84 | Arrays are defined like this: 85 | 86 | ```capyscript 87 | arr = [1, 2, 3, 4, 5]; 88 | arr2 = [[0], [1], [2]]; 89 | ``` 90 | 91 | ### for loops 92 | 93 | For loops are defined like this: 94 | 95 | ```capyscript 96 | 97 | for (i = 0; i < 10; i++) { 98 | print(i); 99 | } 100 | 101 | 102 | function main() { 103 | arr = [[0], [1], [2]]; 104 | 105 | for (i = 0; i < 3; i++) { 106 | for (j = 0; j < 1; j++) { 107 | print(arr[i][j]); 108 | } 109 | } 110 | } 111 | 112 | ``` 113 | 114 | output: 115 | 116 | ``` 117 | 0 118 | 1 119 | 2 120 | ``` 121 | 122 | ### Maps 123 | 124 | Maps are defined like this: 125 | 126 | ```capyscript 127 | 128 | map = { 129 | "foo": "bar", 130 | "bar": "foo" 131 | }; 132 | 133 | print(map["foo"]); // prints "bar" 134 | print(map["bar"]); // prints "foo" 135 | 136 | ``` 137 | -------------------------------------------------------------------------------- /docs/guides/extension_templates/manga/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Name", 3 | "uid": "Generate a new UUIDv4", 4 | "logoUrl": "icon URL", 5 | "type": 0, 6 | "nsfw": false, 7 | "language": "English", 8 | "version": 1, 9 | "filters": [], 10 | "protectorConfig": { 11 | "pingUrl": "Url where the app will open in the web browser", 12 | "needToLogin": true, 13 | "inAppBrowserInterceptor": true 14 | }, 15 | "searchAvailable": true 16 | } 17 | -------------------------------------------------------------------------------- /docs/guides/extension_templates/manga/main.capyscript: -------------------------------------------------------------------------------- 1 | import "http"; 2 | import "io"; 3 | import "json"; 4 | import "date"; 5 | import "html"; 6 | import "manga_models"; 7 | import "converter"; 8 | 9 | 10 | 11 | function getGallery(page, query, filters) { 12 | 13 | } 14 | 15 | function getConcrete(uid, data) { 16 | 17 | } 18 | 19 | function getPages(uid, data) { 20 | 21 | } 22 | 23 | function getImageHeaders(uid) { 24 | return {}; 25 | } 26 | 27 | function passProtector(body, headers, cookies) { 28 | useHeaders({"headers": headers}); 29 | } 30 | 31 | function passWebBrowserInterceptorController(controller) { 32 | registerInterceptorController(controller); 33 | } 34 | -------------------------------------------------------------------------------- /docs/guides/readme.md: -------------------------------------------------------------------------------- 1 | TODO 2 | -------------------------------------------------------------------------------- /docs/inapp_docs/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/inapp_docs/1.jpg -------------------------------------------------------------------------------- /docs/inapp_docs/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/inapp_docs/2.jpg -------------------------------------------------------------------------------- /docs/inapp_docs/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/inapp_docs/3.jpg -------------------------------------------------------------------------------- /docs/inapp_docs/external_extension_sources.md: -------------------------------------------------------------------------------- 1 | # External Extension Sources 2 | 3 | You can add external repositories to Wakaranai. 4 | 5 | You just need to add a URL to repository that contains the extensions in the following structure: 6 | 7 | ``` 8 | ├── manga 9 | │ ├── extension1 10 | │ │ ├── config.json 11 | │ │ └── main.capyscript 12 | │ ├── extension2 13 | │ │ ├── config.json 14 | │ │ └── main.capyscript 15 | ├── anime 16 | │ ├── extension1 17 | │ │ ├── config.json 18 | │ │ └── main.capyscript 19 | │ ├── extension2 20 | │ │ ├── config.json 21 | │ │ └── main.capyscript 22 | ``` 23 | 24 | 25 | 26 | 29 | 32 | 35 | 36 |
27 | Explore Page 28 | 30 | Service Viewer 31 | 33 | Chapter Reader 1 34 |
37 | 38 | See guide for creating extensions [here](../guides/extensions.md) 39 | -------------------------------------------------------------------------------- /docs/wakaranai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/docs/wakaranai.png -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | LOCAL_REPOSITORY_URL= 2 | 3 | OFFICIAL_GITHUB_CONFIGS_SOURCE_ORG=Sayuri128 4 | OFFICIAL_GITHUB_CONFIGS_SOURCE_REPOSITORY=wakaranai_configs 5 | 6 | OFFICIAL_GITHUB_REPO_ORG=Sayuri128 7 | OFFICIAL_GITHUB_REPO_NAME=wakaranai 8 | 9 | CURRENT_APP_VERSION=0.1.0 10 | -------------------------------------------------------------------------------- /fonts/Ubuntu-B.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-B.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-BI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-BI.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-C.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-C.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-L.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-L.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-LI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-LI.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-M.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-M.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-MI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-MI.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-R.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-R.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-RI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-RI.ttf -------------------------------------------------------------------------------- /fonts/Ubuntu-Th.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/Ubuntu-Th.ttf -------------------------------------------------------------------------------- /fonts/UbuntuMono-B.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/UbuntuMono-B.ttf -------------------------------------------------------------------------------- /fonts/UbuntuMono-BI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/UbuntuMono-BI.ttf -------------------------------------------------------------------------------- /fonts/UbuntuMono-R.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/UbuntuMono-R.ttf -------------------------------------------------------------------------------- /fonts/UbuntuMono-RI.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/fonts/UbuntuMono-RI.ttf -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayuri128/wakaranai/7a13e9618c41c8d114f7f0b36cd7a06ad31abbbb/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Wakaranai 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/blocs/api_client_controller/api_client_controller_state.dart: -------------------------------------------------------------------------------- 1 | part of 'api_client_controller_cubit.dart'; 2 | 3 | class ApiClientControllerState { 4 | const ApiClientControllerState(); 5 | } 6 | 7 | class ApiClientControllerInitialized 8 | extends ApiClientControllerState { 9 | final T apiClient; 10 | final ConfigInfo configInfo; 11 | 12 | ApiClientControllerInitialized( 13 | {required this.apiClient, required this.configInfo}); 14 | } 15 | 16 | class ApiClientControllerError extends ApiClientControllerState { 17 | final String message; 18 | 19 | ApiClientControllerError({required this.message}); 20 | } 21 | -------------------------------------------------------------------------------- /lib/blocs/auth/authentication_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:equatable/equatable.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | 5 | part 'authentication_state.dart'; 6 | 7 | class AuthenticationCubit extends Cubit { 8 | AuthenticationCubit() : super(AuthenticationInitial()); 9 | 10 | void authorize(String email, String password) => 11 | emit(AuthenticationAuthenticated.create(email, password)); 12 | } 13 | -------------------------------------------------------------------------------- /lib/blocs/auth/authentication_state.dart: -------------------------------------------------------------------------------- 1 | part of 'authentication_cubit.dart'; 2 | 3 | @immutable 4 | abstract class AuthenticationState extends Equatable {} 5 | 6 | class AuthenticationInitial extends AuthenticationState { 7 | @override 8 | List get props => []; 9 | } 10 | 11 | class AuthenticationUnauthenticated extends AuthenticationState { 12 | @override 13 | List get props => []; 14 | } 15 | 16 | class AuthenticationAuthenticated extends AuthenticationState { 17 | final String email; 18 | final String password; 19 | 20 | AuthenticationAuthenticated.create(this.email, this.password); 21 | 22 | @override 23 | List get props => [email, password]; 24 | } 25 | -------------------------------------------------------------------------------- /lib/blocs/browser_interceptor/browser_interceptor_state.dart: -------------------------------------------------------------------------------- 1 | part of 'browser_interceptor_cubit.dart'; 2 | 3 | @immutable 4 | abstract class BrowserInterceptorState { 5 | const BrowserInterceptorState(); 6 | } 7 | 8 | class BrowserInterceptorInitial extends BrowserInterceptorState {} 9 | 10 | class BrowserInterceptorInitialized extends BrowserInterceptorState {} 11 | 12 | class BrowserInterceptorLoadingPage extends BrowserInterceptorState { 13 | final String url; 14 | final String? method; 15 | final Map? headers; 16 | final String? body; 17 | final Completer onLoaded; 18 | 19 | const BrowserInterceptorLoadingPage( 20 | {required this.url, 21 | required this.onLoaded, 22 | this.method, 23 | this.headers, 24 | required this.body}); 25 | } 26 | 27 | class BrowserInterceptorPageLoaded extends BrowserInterceptorState { 28 | final String body; 29 | final Map data; 30 | 31 | const BrowserInterceptorPageLoaded({required this.body, required this.data}); 32 | } 33 | -------------------------------------------------------------------------------- /lib/blocs/latest_release_cubit/latest_release_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:wakaranai/data/domain/latest_release_data.dart'; 4 | import 'package:wakaranai/services/releases_service/releases_service.dart'; 5 | 6 | part 'latest_release_state.dart'; 7 | 8 | class LatestReleaseCubit extends Cubit { 9 | LatestReleaseCubit() : super(LatestReleaseInitial()); 10 | 11 | final ReleasesService _releasesService = ReleasesService(); 12 | 13 | Future init() async { 14 | final LatestReleaseData? latestReleaseData = 15 | await _releasesService.getLatestReleaseDownloadUrl(); 16 | 17 | if (latestReleaseData == null) { 18 | emit(const LatestReleaseError( 19 | message: 'Failed to get latest release data')); 20 | } else { 21 | emit(LatestReleaseLoaded( 22 | releaseData: latestReleaseData, 23 | )); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/blocs/latest_release_cubit/latest_release_state.dart: -------------------------------------------------------------------------------- 1 | part of 'latest_release_cubit.dart'; 2 | 3 | @immutable 4 | abstract class LatestReleaseState { 5 | const LatestReleaseState(); 6 | } 7 | 8 | class LatestReleaseInitial extends LatestReleaseState {} 9 | 10 | class LatestReleaseLoading extends LatestReleaseState {} 11 | 12 | class LatestReleaseLoaded extends LatestReleaseState { 13 | final LatestReleaseData releaseData; 14 | 15 | const LatestReleaseLoaded({ 16 | required this.releaseData, 17 | }); 18 | 19 | LatestReleaseLoaded copyWith({ 20 | LatestReleaseData? releaseData, 21 | }) { 22 | return LatestReleaseLoaded( 23 | releaseData: releaseData ?? this.releaseData, 24 | ); 25 | } 26 | } 27 | 28 | class LatestReleaseError extends LatestReleaseState { 29 | final String message; 30 | 31 | const LatestReleaseError({ 32 | required this.message, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /lib/blocs/service_view/service_view_state.dart: -------------------------------------------------------------------------------- 1 | part of 'service_view_cubit.dart'; 2 | 3 | @immutable 4 | abstract class ServiceViewState { 5 | final T client; 6 | 7 | const ServiceViewState({ 8 | required this.client, 9 | }); 10 | } 11 | 12 | class ServiceViewInitial 13 | extends ServiceViewState { 14 | const ServiceViewInitial({ 15 | required super.client, 16 | }); 17 | } 18 | 19 | class ServiceViewLoading 20 | extends ServiceViewState { 21 | const ServiceViewLoading({ 22 | required super.client, 23 | }); 24 | } 25 | 26 | class ServiceViewError 27 | extends ServiceViewState { 28 | final String message; 29 | final void Function() retry; 30 | 31 | const ServiceViewError( 32 | {required this.message, required super.client, required this.retry}); 33 | } 34 | 35 | class ServiceViewInitialized 36 | extends ServiceViewState { 37 | final String searchQuery; 38 | final ConfigInfo configInfo; 39 | final List galleryViews; 40 | final Map> galleryViewImagesHeaders; 41 | final int currentPage; 42 | final bool loading; 43 | 44 | final Map selectedFilters; 45 | 46 | const ServiceViewInitialized( 47 | {required super.client, 48 | required this.searchQuery, 49 | required this.configInfo, 50 | required this.galleryViews, 51 | required this.galleryViewImagesHeaders, 52 | required this.currentPage, 53 | required this.selectedFilters, 54 | required this.loading}); 55 | 56 | ServiceViewInitialized copyWith( 57 | {String? searchQuery, 58 | T? client, 59 | ConfigInfo? configInfo, 60 | List? galleryViews, 61 | Map>? galleryViewImagesHeaders, 62 | int? currentPage, 63 | Map? selectedFilters, 64 | bool? loading}) { 65 | return ServiceViewInitialized( 66 | searchQuery: searchQuery ?? this.searchQuery, 67 | client: client ?? this.client, 68 | configInfo: configInfo ?? this.configInfo, 69 | galleryViews: galleryViews ?? this.galleryViews, 70 | galleryViewImagesHeaders: 71 | galleryViewImagesHeaders ?? this.galleryViewImagesHeaders, 72 | currentPage: currentPage ?? this.currentPage, 73 | selectedFilters: selectedFilters ?? this.selectedFilters, 74 | loading: loading ?? this.loading); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/data/domain/app_version.dart: -------------------------------------------------------------------------------- 1 | class AppVersion { 2 | final int major; 3 | final int minor; 4 | final int patch; 5 | 6 | const AppVersion({ 7 | required this.major, 8 | required this.minor, 9 | required this.patch, 10 | }); 11 | 12 | static final RegExp regex = RegExp(r"\d+\.\d+\.\d+"); 13 | 14 | factory AppVersion.fromString(String version) { 15 | if (!regex.hasMatch(version)) { 16 | throw Exception("Invalid version string: $version"); 17 | } 18 | final RegExpMatch? versionResult = regex.firstMatch(version); 19 | if (versionResult == null) { 20 | throw Exception("Invalid version string: $version"); 21 | } 22 | final String versionStr = versionResult.group(0)!; 23 | 24 | final List parts = versionStr.split("."); 25 | if (parts.length != 3) { 26 | throw Exception("Invalid version string: $version"); 27 | } 28 | 29 | final int major = int.parse(parts[0]); 30 | final int minor = int.parse(parts[1]); 31 | final int patch = int.parse(parts[2]); 32 | 33 | return AppVersion( 34 | major: major, 35 | minor: minor, 36 | patch: patch, 37 | ); 38 | } 39 | 40 | bool operator >(AppVersion other) => 41 | major > other.major || 42 | (major == other.major && minor > other.minor) || 43 | (major == other.major && minor == other.minor && patch > other.patch); 44 | 45 | bool operator <(AppVersion other) => 46 | major < other.major || 47 | (major == other.major && minor < other.minor) || 48 | (major == other.major && minor == other.minor && patch < other.patch); 49 | 50 | @override 51 | bool operator ==(Object other) => 52 | identical(this, other) || 53 | other is AppVersion && 54 | runtimeType == other.runtimeType && 55 | major == other.major && 56 | minor == other.minor && 57 | patch == other.patch; 58 | 59 | @override 60 | int get hashCode => major.hashCode ^ minor.hashCode ^ patch.hashCode; 61 | 62 | @override 63 | String toString() { 64 | return "$major.$minor.$patch"; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/data/domain/base_domain.dart: -------------------------------------------------------------------------------- 1 | abstract class BaseDomain { 2 | int id; 3 | final DateTime createdAt; 4 | final DateTime? updatedAt; 5 | 6 | BaseDomain({ 7 | required this.id, 8 | required this.createdAt, 9 | this.updatedAt, 10 | }); 11 | 12 | TTableCompanion toDrift({ 13 | bool update = false, 14 | bool create = false, 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /lib/data/domain/database/anime_episode_activity_domain.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:convert'; 3 | 4 | import 'package:drift/drift.dart'; 5 | import 'package:wakaranai/data/domain/base_domain.dart'; 6 | import 'package:wakaranai/data/domain/database/base_activity_domain.dart'; 7 | import 'package:wakaranai/database/wakaranai_database.dart'; 8 | 9 | class AnimeEpisodeActivityDomain extends BaseActivityDomain { 10 | 11 | final String? timestamp; 12 | final String? data; 13 | final int? watchedTime; 14 | final int? totalTime; 15 | 16 | Map get dataJson => data != null ? jsonDecode(data!) : {}; 17 | 18 | AnimeEpisodeActivityDomain({ 19 | required super.id, 20 | required super.uid, 21 | required super.title, 22 | this.timestamp, 23 | this.data, 24 | required super.concreteId, 25 | this.watchedTime, 26 | this.totalTime, 27 | required super.createdAt, 28 | super.updatedAt, 29 | }); 30 | 31 | factory AnimeEpisodeActivityDomain.fromDrift(AnimeEpisodeActivityTableData data) => 32 | AnimeEpisodeActivityDomain( 33 | id: data.id, 34 | uid: data.uid, 35 | title: data.title, 36 | timestamp: data.timestamp, 37 | data: data.data, 38 | concreteId: data.concreteId, 39 | watchedTime: data.watchedTime, 40 | totalTime: data.totalTime, 41 | createdAt: data.createdAt, 42 | updatedAt: data.updatedAt, 43 | ); 44 | 45 | @override 46 | AnimeEpisodeActivityTableCompanion toDrift({bool update = false, bool 47 | create = false}) { 48 | if (create) { 49 | return AnimeEpisodeActivityTableCompanion( 50 | id: const Value.absent(), 51 | uid: Value(uid), 52 | title: Value(title), 53 | timestamp: Value(timestamp), 54 | data: Value(data), 55 | concreteId: Value(concreteId), 56 | watchedTime: Value(watchedTime), 57 | totalTime: Value(totalTime), 58 | createdAt: Value(createdAt), 59 | ); 60 | } 61 | 62 | return AnimeEpisodeActivityTableCompanion( 63 | id: Value(id), 64 | uid: Value(uid), 65 | title: Value(title), 66 | timestamp: Value(timestamp), 67 | data: Value(data), 68 | concreteId: Value(concreteId), 69 | watchedTime: Value(watchedTime), 70 | totalTime: Value(totalTime), 71 | createdAt: Value(createdAt), 72 | updatedAt: Value(update ? DateTime.now() : updatedAt), 73 | ); 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /lib/data/domain/database/base_activity_domain.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/base_domain.dart'; 2 | 3 | abstract class BaseActivityDomain 4 | extends BaseDomain { 5 | final String title; 6 | final String uid; 7 | final int concreteId; 8 | 9 | BaseActivityDomain({ 10 | required this.title, 11 | required this.uid, 12 | required this.concreteId, 13 | required super.id, 14 | required super.createdAt, 15 | super.updatedAt, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /lib/data/domain/database/base_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 2 | 3 | mixin BaseExtension { 4 | abstract final ConfigInfo config; 5 | } 6 | -------------------------------------------------------------------------------- /lib/data/domain/database/chapter_activity_domain.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:drift/drift.dart'; 4 | import 'package:wakaranai/data/domain/base_domain.dart'; 5 | import 'package:wakaranai/data/domain/database/base_activity_domain.dart'; 6 | import 'package:wakaranai/database/wakaranai_database.dart'; 7 | 8 | class ChapterActivityDomain extends BaseActivityDomain { 9 | final String? timestamp; 10 | final String? data; 11 | final int readPages; 12 | final int totalPages; 13 | 14 | bool get isCompleted => readPages >= totalPages; 15 | Map get dataJson => data != null ? jsonDecode(data!) : {}; 16 | 17 | factory ChapterActivityDomain.fromDrift(ChapterActivityTableData data) => 18 | ChapterActivityDomain( 19 | id: data.id, 20 | uid: data.uid, 21 | title: data.title, 22 | timestamp: data.timestamp, 23 | data: data.data, 24 | concreteId: data.concreteId, 25 | readPages: data.readPages, 26 | totalPages: data.totalPages, 27 | createdAt: data.createdAt, 28 | updatedAt: data.updatedAt, 29 | ); 30 | 31 | @override 32 | ChapterActivityTableCompanion toDrift( 33 | {bool update = false, bool create = false}) { 34 | if (create) { 35 | return ChapterActivityTableCompanion( 36 | id: const Value.absent(), 37 | uid: Value(uid), 38 | title: Value(title), 39 | timestamp: Value(timestamp), 40 | data: Value(data), 41 | concreteId: Value(concreteId), 42 | readPages: Value(readPages), 43 | totalPages: Value(totalPages), 44 | createdAt: Value(createdAt), 45 | ); 46 | } 47 | 48 | return ChapterActivityTableCompanion( 49 | id: Value(id), 50 | uid: Value(uid), 51 | title: Value(title), 52 | timestamp: Value(timestamp), 53 | data: Value(data), 54 | concreteId: Value(concreteId), 55 | readPages: Value(readPages), 56 | totalPages: Value(totalPages), 57 | createdAt: Value(createdAt), 58 | updatedAt: Value(update ? DateTime.now() : updatedAt), 59 | ); 60 | } 61 | 62 | ChapterActivityDomain({ 63 | required super.uid, 64 | required super.title, 65 | required this.timestamp, 66 | required this.data, 67 | required super.concreteId, 68 | required this.readPages, 69 | required this.totalPages, 70 | required super.id, 71 | required super.createdAt, 72 | super.updatedAt, 73 | }); 74 | 75 | ChapterActivityDomain copyWith({ 76 | int? id, 77 | String? title, 78 | String? uid, 79 | String? timestamp, 80 | String? data, 81 | int? concreteId, 82 | int? readPages, 83 | int? totalPages, 84 | }) { 85 | return ChapterActivityDomain( 86 | id: id ?? this.id, 87 | title: title ?? this.title, 88 | uid: uid ?? this.uid, 89 | timestamp: timestamp ?? this.timestamp, 90 | data: data ?? this.data, 91 | concreteId: concreteId ?? this.concreteId, 92 | readPages: readPages ?? this.readPages, 93 | totalPages: totalPages ?? this.totalPages, 94 | createdAt: createdAt, 95 | updatedAt: updatedAt, 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/data/domain/database/concrete_data_domain.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:capyscript/modules/waka_models/models/common/concrete_view.dart'; 4 | import 'package:drift/drift.dart'; 5 | import 'package:wakaranai/data/domain/base_domain.dart'; 6 | import 'package:wakaranai/database/wakaranai_database.dart'; 7 | 8 | class ConcreteDataDomain extends BaseDomain { 9 | final String extensionUid; 10 | final String uid; 11 | final String title; 12 | final String? cover; 13 | final String? data; 14 | 15 | Map get dataJson => data != null ? jsonDecode(data!) : {}; 16 | 17 | factory ConcreteDataDomain.fromDrift(ConcreteDataTableData data) => 18 | ConcreteDataDomain( 19 | id: data.id, 20 | uid: data.uid, 21 | extensionUid: data.extensionUid, 22 | title: data.title, 23 | cover: data.cover, 24 | data: data.data, 25 | createdAt: data.createdAt, 26 | updatedAt: data.updatedAt, 27 | ); 28 | 29 | @override 30 | ConcreteDataTableCompanion toDrift( 31 | {bool update = false, bool create = false}) { 32 | if (create) { 33 | return ConcreteDataTableCompanion( 34 | id: const Value.absent(), 35 | uid: Value(uid), 36 | extensionUid: Value(extensionUid), 37 | title: Value(title), 38 | cover: Value(cover), 39 | data: Value(data), 40 | createdAt: Value(createdAt), 41 | ); 42 | } 43 | 44 | return ConcreteDataTableCompanion( 45 | id: Value(id), 46 | uid: Value(uid), 47 | extensionUid: Value(extensionUid), 48 | title: Value(title), 49 | cover: Value(cover), 50 | data: Value(data), 51 | createdAt: Value(createdAt), 52 | updatedAt: Value(update ? DateTime.now() : updatedAt), 53 | ); 54 | } 55 | 56 | ConcreteDataDomain copyWith({ 57 | int? id, 58 | String? uid, 59 | String? extensionUid, 60 | String? title, 61 | String? cover, 62 | String? data, 63 | }) { 64 | return ConcreteDataDomain( 65 | id: id ?? this.id, 66 | uid: uid ?? this.uid, 67 | extensionUid: extensionUid ?? this.extensionUid, 68 | title: title ?? this.title, 69 | cover: cover ?? this.cover, 70 | data: data ?? this.data, 71 | createdAt: createdAt, 72 | updatedAt: updatedAt, 73 | ); 74 | } 75 | 76 | ConcreteDataDomain({ 77 | required super.id, 78 | required this.uid, 79 | required this.extensionUid, 80 | required this.title, 81 | this.cover, 82 | this.data, 83 | required super.createdAt, 84 | super.updatedAt, 85 | }); 86 | } 87 | 88 | extension ConcreteViewExtension on ConcreteView { 89 | ConcreteDataDomain toConcreteDataDomain({ 90 | required Map data, 91 | required String extensionUid, 92 | }) => 93 | ConcreteDataDomain( 94 | id: 0, 95 | uid: uid, 96 | extensionUid: extensionUid, 97 | title: title, 98 | cover: cover, 99 | data: jsonEncode(data), 100 | createdAt: DateTime.now(), 101 | ); 102 | } 103 | -------------------------------------------------------------------------------- /lib/data/domain/database/extension_source_domain.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/domain/base_domain.dart'; 3 | import 'package:wakaranai/data/domain/database/extension_source_type.dart'; 4 | import 'package:wakaranai/database/wakaranai_database.dart'; 5 | import 'package:wakaranai/utils/enum_converters.dart'; 6 | 7 | class ExtensionSourceDomain extends BaseDomain { 8 | final String name; 9 | final String url; 10 | final ExtensionSourceType type; 11 | 12 | ExtensionSourceDomain({ 13 | required super.id, 14 | required this.name, 15 | required this.url, 16 | required this.type, 17 | required super.createdAt, 18 | required super.updatedAt, 19 | }); 20 | 21 | factory ExtensionSourceDomain.fromDrift(ExtensionSourceTableData data) => 22 | ExtensionSourceDomain( 23 | id: data.id, 24 | name: data.name, 25 | url: data.url, 26 | type: decodeEnum(ExtensionSourceType.values, data.type)!, 27 | createdAt: data.createdAt, 28 | updatedAt: data.updatedAt, 29 | ); 30 | 31 | @override 32 | ExtensionSourceTableCompanion toDrift({ 33 | bool update = false, 34 | bool create = false, 35 | }) { 36 | if (create) { 37 | return ExtensionSourceTableCompanion( 38 | name: Value(name), 39 | url: Value(url), 40 | type: Value(type.toString()), 41 | createdAt: Value(createdAt), 42 | ); 43 | } 44 | 45 | return ExtensionSourceTableCompanion( 46 | id: Value(id), 47 | name: Value(name), 48 | url: Value(url), 49 | type: Value(encodeEnum(type)), 50 | createdAt: Value(createdAt), 51 | updatedAt: Value(update ? DateTime.now() : updatedAt), 52 | ); 53 | } 54 | 55 | ExtensionSourceDomain copyWith({ 56 | String? name, 57 | String? url, 58 | ExtensionSourceType? type, 59 | }) { 60 | return ExtensionSourceDomain( 61 | id: id, 62 | name: name ?? this.name, 63 | url: url ?? this.url, 64 | type: type ?? this.type, 65 | createdAt: createdAt, 66 | updatedAt: updatedAt, 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/data/domain/database/extension_source_type.dart: -------------------------------------------------------------------------------- 1 | enum ExtensionSourceType { 2 | github, 3 | } 4 | -------------------------------------------------------------------------------- /lib/data/domain/github_url_data.dart: -------------------------------------------------------------------------------- 1 | class GithubUrlData { 2 | final String org; 3 | final String repo; 4 | 5 | const GithubUrlData({ 6 | required this.org, 7 | required this.repo, 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/data/domain/latest_release_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/app_version.dart'; 2 | 3 | class LatestReleaseData { 4 | final String url; 5 | final AppVersion latestVersion; 6 | final AppVersion currentVersion; 7 | 8 | bool get needsUpdate => latestVersion > currentVersion; 9 | 10 | const LatestReleaseData({ 11 | required this.url, 12 | required this.latestVersion, 13 | required this.currentVersion, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /lib/data/domain/ui/activity_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/database/base_activity_domain.dart'; 2 | import 'package:wakaranai/data/domain/database/concrete_data_domain.dart'; 3 | 4 | class ActivityListItem { 5 | final DateTime day; 6 | final List> listItems; 7 | 8 | const ActivityListItem({ 9 | required this.day, 10 | required this.listItems, 11 | }); 12 | 13 | void removeItem(String uid) { 14 | listItems.removeWhere((element) => element.activity.uid == uid); 15 | } 16 | 17 | ActivityListItem copyWith({ 18 | DateTime? day, 19 | List>? listItems, 20 | }) { 21 | return ActivityListItem( 22 | day: day ?? this.day, 23 | listItems: listItems ?? this.listItems, 24 | ); 25 | } 26 | } 27 | 28 | class DayActivityListItem { 29 | final ConcreteDataDomain data; 30 | final T activity; 31 | 32 | const DayActivityListItem({ 33 | required this.data, 34 | required this.activity, 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /lib/data/entities/anime_episode_activity_table.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:drift/drift.dart'; 3 | import 'package:wakaranai/data/entities/base_table.dart'; 4 | import 'package:wakaranai/data/entities/concrete_data_table.dart'; 5 | 6 | class AnimeEpisodeActivityTable extends BaseTable { 7 | 8 | TextColumn get uid => text()(); 9 | 10 | TextColumn get title => text()(); 11 | 12 | TextColumn get timestamp => text().nullable()(); 13 | 14 | TextColumn get data => text().nullable()(); 15 | 16 | IntColumn get concreteId => integer().references(ConcreteDataTable, #id)(); 17 | 18 | IntColumn get watchedTime => integer().nullable()(); 19 | 20 | IntColumn get totalTime => integer().nullable()(); 21 | 22 | } -------------------------------------------------------------------------------- /lib/data/entities/base_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | 3 | class BaseTable extends Table { 4 | IntColumn get id => integer().autoIncrement()(); 5 | 6 | DateTimeColumn get createdAt => 7 | dateTime().clientDefault(() => DateTime.now())(); 8 | 9 | DateTimeColumn get updatedAt => dateTime().nullable()(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/data/entities/chapter_activity_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/entities/base_table.dart'; 3 | import 'package:wakaranai/data/entities/concrete_data_table.dart'; 4 | 5 | class ChapterActivityTable extends BaseTable { 6 | TextColumn get uid => text()(); 7 | 8 | TextColumn get title => text()(); 9 | 10 | TextColumn get timestamp => text().nullable()(); 11 | 12 | TextColumn get data => text().nullable()(); 13 | 14 | IntColumn get concreteId => integer().references(ConcreteDataTable, #id)(); 15 | 16 | IntColumn get readPages => integer()(); 17 | 18 | IntColumn get totalPages => integer()(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/entities/concrete_data_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/entities/base_table.dart'; 3 | import 'package:wakaranai/data/entities/extension_table.dart'; 4 | 5 | class ConcreteDataTable extends BaseTable { 6 | 7 | TextColumn get uid => text()(); 8 | 9 | TextColumn get extensionUid => text().references(ExtensionTable, #uid)(); 10 | 11 | TextColumn get title => text()(); 12 | 13 | TextColumn get cover => text().nullable()(); 14 | 15 | TextColumn get data => text().nullable()(); 16 | } 17 | -------------------------------------------------------------------------------- /lib/data/entities/extension_source_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/entities/base_table.dart'; 3 | 4 | class ExtensionSourceTable extends BaseTable { 5 | TextColumn get type => text()(); 6 | 7 | TextColumn get name => text()(); 8 | 9 | TextColumn get url => text()(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/data/entities/extension_table.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/entities/base_table.dart'; 3 | 4 | class ExtensionTable extends BaseTable { 5 | TextColumn get uid => text().unique()(); 6 | 7 | TextColumn get name => text()(); 8 | 9 | TextColumn get type => text()(); 10 | 11 | IntColumn get version => integer()(); 12 | 13 | TextColumn get logoUrl => text()(); 14 | 15 | TextColumn get language => text()(); 16 | 17 | BoolColumn get nsfw => boolean()(); 18 | 19 | TextColumn get sourceCode => text()(); 20 | 21 | BoolColumn get searchAvailable => boolean()(); 22 | 23 | TextColumn get protectorConfig => text().nullable()(); 24 | } 25 | -------------------------------------------------------------------------------- /lib/data/models/configs_repo/configs_response/repo_configs_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:wakaranai/data/models/remote_config/remote_config.dart'; 3 | 4 | part 'repo_configs_response.g.dart'; 5 | 6 | @JsonSerializable() 7 | class RepoConfigsResponse { 8 | final int status; 9 | final List availableCategories; 10 | final List configs; 11 | 12 | factory RepoConfigsResponse.fromJson(Map json) => 13 | _$RepoConfigsResponseFromJson(json); 14 | 15 | Map toJson() => _$RepoConfigsResponseToJson(this); 16 | 17 | const RepoConfigsResponse({ 18 | required this.status, 19 | required this.availableCategories, 20 | required this.configs, 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /lib/data/models/configs_repo/configs_response/repo_configs_response.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'repo_configs_response.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | RepoConfigsResponse _$RepoConfigsResponseFromJson(Map json) => 10 | RepoConfigsResponse( 11 | status: (json['status'] as num).toInt(), 12 | availableCategories: (json['availableCategories'] as List) 13 | .map((e) => e as String) 14 | .toList(), 15 | configs: (json['configs'] as List) 16 | .map( 17 | (e) => RemoteConfig.fromJson(Map.from(e as Map))) 18 | .toList(), 19 | ); 20 | 21 | Map _$RepoConfigsResponseToJson( 22 | RepoConfigsResponse instance) => 23 | { 24 | 'status': instance.status, 25 | 'availableCategories': instance.availableCategories, 26 | 'configs': instance.configs.map((e) => e.toJson()).toList(), 27 | }; 28 | -------------------------------------------------------------------------------- /lib/data/models/configs_source_type/configs_source_type.dart: -------------------------------------------------------------------------------- 1 | // TODO: add local configs (that are stored locally on user's device) 2 | enum ConfigsSourceType { github, rest } 3 | 4 | // String configsSourceTypeToString(ConfigsSourceType type) { 5 | // switch (type) { 6 | // case ConfigsSourceType.GIT_HUB: 7 | // return S.current.github_configs_source_type; 8 | // case ConfigsSourceType.REST: 9 | // return S.current.rest_configs_source_type; 10 | // } 11 | // } 12 | -------------------------------------------------------------------------------- /lib/data/models/github/github_response_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:wakaranai/data/models/github/payload/github_payload_model.dart'; 3 | 4 | part 'github_response_model.g.dart'; 5 | 6 | @JsonSerializable(explicitToJson: true) 7 | class GithubResponseModel { 8 | factory GithubResponseModel.fromJson(Map json) => 9 | _$GithubResponseModelFromJson(json); 10 | 11 | Map toJson() => _$GithubResponseModelToJson(this); 12 | 13 | final GithubPayloadModel payload; 14 | 15 | const GithubResponseModel({ 16 | required this.payload, 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/models/github/github_response_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_response_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubResponseModel _$GithubResponseModelFromJson(Map json) => 10 | GithubResponseModel( 11 | payload: GithubPayloadModel.fromJson( 12 | Map.from(json['payload'] as Map)), 13 | ); 14 | 15 | Map _$GithubResponseModelToJson( 16 | GithubResponseModel instance) => 17 | { 18 | 'payload': instance.payload.toJson(), 19 | }; 20 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/github_payload_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:wakaranai/data/models/github/payload/repo/github_repo_model.dart'; 3 | import 'package:wakaranai/data/models/github/payload/tree/blob/github_blob_model.dart'; 4 | import 'package:wakaranai/data/models/github/payload/tree/github_tree_model.dart'; 5 | 6 | part 'github_payload_model.g.dart'; 7 | 8 | @JsonSerializable(explicitToJson: true) 9 | class GithubPayloadModel { 10 | factory GithubPayloadModel.fromJson(Map json) => 11 | _$GithubPayloadModelFromJson(json); 12 | 13 | Map toJson() => _$GithubPayloadModelToJson(this); 14 | 15 | final String path; 16 | final GithubRepoModel repo; 17 | final GithubTreeModel? tree; 18 | final GithubBlobModel? blob; 19 | final String? title; 20 | 21 | const GithubPayloadModel({ 22 | required this.path, 23 | required this.repo, 24 | required this.tree, 25 | this.blob, 26 | this.title, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/github_payload_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_payload_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubPayloadModel _$GithubPayloadModelFromJson(Map json) => GithubPayloadModel( 10 | path: json['path'] as String, 11 | repo: GithubRepoModel.fromJson( 12 | Map.from(json['repo'] as Map)), 13 | tree: json['tree'] == null 14 | ? null 15 | : GithubTreeModel.fromJson( 16 | Map.from(json['tree'] as Map)), 17 | blob: json['blob'] == null 18 | ? null 19 | : GithubBlobModel.fromJson( 20 | Map.from(json['blob'] as Map)), 21 | title: json['title'] as String?, 22 | ); 23 | 24 | Map _$GithubPayloadModelToJson(GithubPayloadModel instance) => 25 | { 26 | 'path': instance.path, 27 | 'repo': instance.repo.toJson(), 28 | 'tree': instance.tree?.toJson(), 29 | 'blob': instance.blob?.toJson(), 30 | 'title': instance.title, 31 | }; 32 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/repo/github_repo_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'github_repo_model.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class GithubRepoModel { 7 | factory GithubRepoModel.fromJson(Map json) => 8 | _$GithubRepoModelFromJson(json); 9 | 10 | Map toJson() => _$GithubRepoModelToJson(this); 11 | 12 | final int id; 13 | final String defaultBranch; 14 | final String name; 15 | final String ownerLogin; 16 | final bool currentUserCanPush; 17 | final bool isFork; 18 | final bool isEmpty; 19 | final String createdAt; 20 | final String ownerAvatar; 21 | final bool public; 22 | final bool private; 23 | final bool isOrgOwned; 24 | 25 | const GithubRepoModel({ 26 | required this.id, 27 | required this.defaultBranch, 28 | required this.name, 29 | required this.ownerLogin, 30 | required this.currentUserCanPush, 31 | required this.isFork, 32 | required this.isEmpty, 33 | required this.createdAt, 34 | required this.ownerAvatar, 35 | required this.public, 36 | required this.private, 37 | required this.isOrgOwned, 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/repo/github_repo_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_repo_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubRepoModel _$GithubRepoModelFromJson(Map json) => GithubRepoModel( 10 | id: (json['id'] as num).toInt(), 11 | defaultBranch: json['defaultBranch'] as String, 12 | name: json['name'] as String, 13 | ownerLogin: json['ownerLogin'] as String, 14 | currentUserCanPush: json['currentUserCanPush'] as bool, 15 | isFork: json['isFork'] as bool, 16 | isEmpty: json['isEmpty'] as bool, 17 | createdAt: json['createdAt'] as String, 18 | ownerAvatar: json['ownerAvatar'] as String, 19 | public: json['public'] as bool, 20 | private: json['private'] as bool, 21 | isOrgOwned: json['isOrgOwned'] as bool, 22 | ); 23 | 24 | Map _$GithubRepoModelToJson(GithubRepoModel instance) => 25 | { 26 | 'id': instance.id, 27 | 'defaultBranch': instance.defaultBranch, 28 | 'name': instance.name, 29 | 'ownerLogin': instance.ownerLogin, 30 | 'currentUserCanPush': instance.currentUserCanPush, 31 | 'isFork': instance.isFork, 32 | 'isEmpty': instance.isEmpty, 33 | 'createdAt': instance.createdAt, 34 | 'ownerAvatar': instance.ownerAvatar, 35 | 'public': instance.public, 36 | 'private': instance.private, 37 | 'isOrgOwned': instance.isOrgOwned, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/tree/blob/github_blob_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'github_blob_model.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class GithubBlobModel { 7 | factory GithubBlobModel.fromJson(Map json) => 8 | _$GithubBlobModelFromJson(json); 9 | 10 | Map toJson() => _$GithubBlobModelToJson(this); 11 | 12 | final List rawLines; 13 | final String displayName; 14 | final String displayUrl; 15 | 16 | const GithubBlobModel({ 17 | required this.rawLines, 18 | required this.displayName, 19 | required this.displayUrl, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/tree/blob/github_blob_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_blob_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubBlobModel _$GithubBlobModelFromJson(Map json) => GithubBlobModel( 10 | rawLines: 11 | (json['rawLines'] as List).map((e) => e as String).toList(), 12 | displayName: json['displayName'] as String, 13 | displayUrl: json['displayUrl'] as String, 14 | ); 15 | 16 | Map _$GithubBlobModelToJson(GithubBlobModel instance) => 17 | { 18 | 'rawLines': instance.rawLines, 19 | 'displayName': instance.displayName, 20 | 'displayUrl': instance.displayUrl, 21 | }; 22 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/tree/github_tree_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:wakaranai/data/models/github/payload/tree/item/github_tree_item_model.dart'; 3 | 4 | part 'github_tree_model.g.dart'; 5 | 6 | @JsonSerializable(explicitToJson: true) 7 | class GithubTreeModel { 8 | factory GithubTreeModel.fromJson(Map json) => 9 | _$GithubTreeModelFromJson(json); 10 | 11 | Map toJson() => _$GithubTreeModelToJson(this); 12 | 13 | final List items; 14 | 15 | const GithubTreeModel({ 16 | required this.items, 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/tree/github_tree_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_tree_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubTreeModel _$GithubTreeModelFromJson(Map json) => GithubTreeModel( 10 | items: (json['items'] as List) 11 | .map((e) => 12 | GithubTreeItemModel.fromJson(Map.from(e as Map))) 13 | .toList(), 14 | ); 15 | 16 | Map _$GithubTreeModelToJson(GithubTreeModel instance) => 17 | { 18 | 'items': instance.items.map((e) => e.toJson()).toList(), 19 | }; 20 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/tree/item/github_tree_item_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'github_tree_item_model.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class GithubTreeItemModel { 7 | factory GithubTreeItemModel.fromJson(Map json) => 8 | _$GithubTreeItemModelFromJson(json); 9 | 10 | Map toJson() => _$GithubTreeItemModelToJson(this); 11 | final String name; 12 | final String path; 13 | final String contentType; 14 | 15 | const GithubTreeItemModel({ 16 | required this.name, 17 | required this.path, 18 | required this.contentType, 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /lib/data/models/github/payload/tree/item/github_tree_item_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_tree_item_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubTreeItemModel _$GithubTreeItemModelFromJson(Map json) => 10 | GithubTreeItemModel( 11 | name: json['name'] as String, 12 | path: json['path'] as String, 13 | contentType: json['contentType'] as String, 14 | ); 15 | 16 | Map _$GithubTreeItemModelToJson( 17 | GithubTreeItemModel instance) => 18 | { 19 | 'name': instance.name, 20 | 'path': instance.path, 21 | 'contentType': instance.contentType, 22 | }; 23 | -------------------------------------------------------------------------------- /lib/data/models/github/release_response/github_release_response_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'github_release_response_model.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class GithubReleaseResponseModel { 7 | factory GithubReleaseResponseModel.fromJson(Map json) => 8 | _$GithubReleaseResponseModelFromJson(json); 9 | 10 | Map toJson() => _$GithubReleaseResponseModelToJson(this); 11 | 12 | final String url; 13 | 14 | @JsonKey(name: "html_url") 15 | final String htmlUrl; 16 | 17 | @JsonKey(name: "assets_url") 18 | final String assetsUrl; 19 | 20 | @JsonKey(name: "upload_url") 21 | final String uploadUrl; 22 | 23 | @JsonKey(name: "tarball_url") 24 | final String tarballUrl; 25 | 26 | @JsonKey(name: "zipball_url") 27 | final String zipballUrl; 28 | 29 | final int id; 30 | 31 | @JsonKey(name: "node_id") 32 | final String nodeId; 33 | 34 | @JsonKey(name: "tag_name") 35 | final String tagName; 36 | 37 | @JsonKey(name: "target_commitish") 38 | final String targetCommitish; 39 | 40 | final String name; 41 | 42 | const GithubReleaseResponseModel({ 43 | required this.url, 44 | required this.htmlUrl, 45 | required this.assetsUrl, 46 | required this.uploadUrl, 47 | required this.tarballUrl, 48 | required this.zipballUrl, 49 | required this.id, 50 | required this.nodeId, 51 | required this.tagName, 52 | required this.targetCommitish, 53 | required this.name, 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /lib/data/models/github/release_response/github_release_response_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_release_response_model.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | GithubReleaseResponseModel _$GithubReleaseResponseModelFromJson(Map json) => 10 | GithubReleaseResponseModel( 11 | url: json['url'] as String, 12 | htmlUrl: json['html_url'] as String, 13 | assetsUrl: json['assets_url'] as String, 14 | uploadUrl: json['upload_url'] as String, 15 | tarballUrl: json['tarball_url'] as String, 16 | zipballUrl: json['zipball_url'] as String, 17 | id: (json['id'] as num).toInt(), 18 | nodeId: json['node_id'] as String, 19 | tagName: json['tag_name'] as String, 20 | targetCommitish: json['target_commitish'] as String, 21 | name: json['name'] as String, 22 | ); 23 | 24 | Map _$GithubReleaseResponseModelToJson( 25 | GithubReleaseResponseModel instance) => 26 | { 27 | 'url': instance.url, 28 | 'html_url': instance.htmlUrl, 29 | 'assets_url': instance.assetsUrl, 30 | 'upload_url': instance.uploadUrl, 31 | 'tarball_url': instance.tarballUrl, 32 | 'zipball_url': instance.zipballUrl, 33 | 'id': instance.id, 34 | 'node_id': instance.nodeId, 35 | 'tag_name': instance.tagName, 36 | 'target_commitish': instance.targetCommitish, 37 | 'name': instance.name, 38 | }; 39 | -------------------------------------------------------------------------------- /lib/data/models/protector/protector_storage_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | import 'package:wakaranai/data/models/web_browser_result/web_browser_result.dart'; 3 | 4 | part 'protector_storage_item.g.dart'; 5 | 6 | @JsonSerializable(explicitToJson: true) 7 | class ProtectorStorageItem { 8 | factory ProtectorStorageItem.fromJson(Map json) => 9 | _$ProtectorStorageItemFromJson(json); 10 | Map toJson() => _$ProtectorStorageItemToJson(this); 11 | 12 | final String uid; 13 | final WebBrowserPageResult data; 14 | 15 | const ProtectorStorageItem({ 16 | required this.uid, 17 | required this.data, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /lib/data/models/protector/protector_storage_item.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'protector_storage_item.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | ProtectorStorageItem _$ProtectorStorageItemFromJson(Map json) => 10 | ProtectorStorageItem( 11 | uid: json['uid'] as String, 12 | data: WebBrowserPageResult.fromJson( 13 | Map.from(json['data'] as Map)), 14 | ); 15 | 16 | Map _$ProtectorStorageItemToJson( 17 | ProtectorStorageItem instance) => 18 | { 19 | 'uid': instance.uid, 20 | 'data': instance.data.toJson(), 21 | }; 22 | -------------------------------------------------------------------------------- /lib/data/models/remote_config/remote_category.dart: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/data/models/remote_config/remote_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | import 'package:wakaranai/data/domain/database/base_extension.dart'; 4 | 5 | part 'remote_config.g.dart'; 6 | 7 | @JsonSerializable() 8 | class RemoteConfig with BaseExtension { 9 | factory RemoteConfig.fromJson(Map json) => 10 | _$RemoteConfigFromJson(json); 11 | 12 | Map toJson() => _$RemoteConfigToJson(this); 13 | 14 | final String path; 15 | @override 16 | final ConfigInfo config; 17 | const RemoteConfig({ 18 | required this.path, 19 | required this.config, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/models/remote_config/remote_config.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'remote_config.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | RemoteConfig _$RemoteConfigFromJson(Map json) => RemoteConfig( 10 | path: json['path'] as String, 11 | config: 12 | ConfigInfo.fromJson(Map.from(json['config'] as Map)), 13 | ); 14 | 15 | Map _$RemoteConfigToJson(RemoteConfig instance) => 16 | { 17 | 'path': instance.path, 18 | 'config': instance.config.toJson(), 19 | }; 20 | -------------------------------------------------------------------------------- /lib/data/models/remote_script/remote_script.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'remote_script.g.dart'; 4 | 5 | @JsonSerializable() 6 | class RemoteScript { 7 | factory RemoteScript.fromJson(Map json) => 8 | _$RemoteScriptFromJson(json); 9 | Map toJson() => _$RemoteScriptToJson(this); 10 | 11 | final String path; 12 | final String script; 13 | 14 | const RemoteScript({ 15 | required this.path, 16 | required this.script, 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /lib/data/models/remote_script/remote_script.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'remote_script.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | RemoteScript _$RemoteScriptFromJson(Map json) => RemoteScript( 10 | path: json['path'] as String, 11 | script: json['script'] as String, 12 | ); 13 | 14 | Map _$RemoteScriptToJson(RemoteScript instance) => 15 | { 16 | 'path': instance.path, 17 | 'script': instance.script, 18 | }; 19 | -------------------------------------------------------------------------------- /lib/data/models/web_browser_result/web_browser_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'web_browser_result.g.dart'; 4 | 5 | @JsonSerializable(explicitToJson: true) 6 | class WebBrowserPageResult { 7 | factory WebBrowserPageResult.fromJson(Map json) => 8 | _$WebBrowserPageResultFromJson(json); 9 | 10 | Map toJson() => _$WebBrowserPageResultToJson(this); 11 | 12 | final Map headers; 13 | final Map cookies; 14 | final String body; 15 | 16 | const WebBrowserPageResult({ 17 | required this.headers, 18 | required this.cookies, 19 | required this.body, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /lib/data/models/web_browser_result/web_browser_result.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'web_browser_result.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | WebBrowserPageResult _$WebBrowserPageResultFromJson(Map json) => 10 | WebBrowserPageResult( 11 | headers: Map.from(json['headers'] as Map), 12 | cookies: Map.from(json['cookies'] as Map), 13 | body: json['body'] as String, 14 | ); 15 | 16 | Map _$WebBrowserPageResultToJson( 17 | WebBrowserPageResult instance) => 18 | { 19 | 'headers': instance.headers, 20 | 'cookies': instance.cookies, 21 | 'body': instance.body, 22 | }; 23 | -------------------------------------------------------------------------------- /lib/database/wakaranai_database.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:drift/drift.dart'; 4 | import 'package:drift/native.dart'; 5 | import 'package:path/path.dart' as path; 6 | import 'package:path_provider/path_provider.dart'; 7 | import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart'; 8 | import 'package:wakaranai/data/entities/anime_episode_activity_table.dart'; 9 | import 'package:wakaranai/data/entities/chapter_activity_table.dart'; 10 | import 'package:wakaranai/data/entities/concrete_data_table.dart'; 11 | import 'package:wakaranai/data/entities/extension_source_table.dart'; 12 | import 'package:wakaranai/data/entities/extension_table.dart'; 13 | 14 | part 'wakaranai_database.g.dart'; 15 | 16 | @DriftDatabase(tables: [ 17 | ExtensionSourceTable, 18 | ExtensionTable, 19 | ConcreteDataTable, 20 | ChapterActivityTable, 21 | AnimeEpisodeActivityTable, 22 | ]) 23 | class WakaranaiDatabase extends _$WakaranaiDatabase { 24 | WakaranaiDatabase() : super(_openConnection()); 25 | 26 | @override 27 | MigrationStrategy get migration => MigrationStrategy( 28 | beforeOpen: (details) async { 29 | }, 30 | onCreate: (m) async { 31 | await m.createAll(); 32 | }, 33 | onUpgrade: (m, from, to) async { 34 | if (from < 2) { 35 | await m.createTable(extensionTable); 36 | } 37 | if (from < 3) { 38 | await m.createTable(concreteDataTable); 39 | await m.createTable(chapterActivityTable); 40 | await m.createTable(animeEpisodeActivityTable); 41 | 42 | } 43 | }, 44 | ); 45 | 46 | @override 47 | int get schemaVersion => 3; 48 | } 49 | 50 | LazyDatabase _openConnection() { 51 | return LazyDatabase(() async { 52 | final dbFolder = await getApplicationDocumentsDirectory(); 53 | final file = File(path.join(dbFolder.path, "smt.sqlite")); 54 | 55 | if (Platform.isAndroid) { 56 | await applyWorkaroundToOpenSqlite3OnOldAndroidVersions(); 57 | } 58 | 59 | return NativeDatabase.createInBackground(file); 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /lib/env.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_dotenv/flutter_dotenv.dart'; 2 | 3 | class Env { 4 | static final String localRepoUrl = dotenv.env['LOCAL_REPOSITORY_URL']!; 5 | static final String configsSourceOrg = 6 | dotenv.env['OFFICIAL_GITHUB_CONFIGS_SOURCE_ORG']!; 7 | 8 | static final String configsSourceRepo = 9 | dotenv.env['OFFICIAL_GITHUB_CONFIGS_SOURCE_REPOSITORY']!; 10 | 11 | static final String sqfliteDmBName = dotenv.env['SQFLITE_DB_NAME']!; 12 | 13 | static final String appRepoOrg = dotenv.env['OFFICIAL_GITHUB_REPO_ORG']!; 14 | static final String appRepoName = dotenv.env['OFFICIAL_GITHUB_REPO_NAME']!; 15 | 16 | static final String currentAppVersion = dotenv.env['CURRENT_APP_VERSION']!; 17 | } 18 | -------------------------------------------------------------------------------- /lib/generated/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:flutter/foundation.dart'; 15 | import 'package:intl/intl.dart'; 16 | import 'package:intl/message_lookup_by_library.dart'; 17 | import 'package:intl/src/intl_helpers.dart'; 18 | 19 | import 'messages_en.dart' as messages_en; 20 | 21 | typedef Future LibraryLoader(); 22 | Map _deferredLibraries = { 23 | 'en': () => new SynchronousFuture(null), 24 | }; 25 | 26 | MessageLookupByLibrary? _findExact(String localeName) { 27 | switch (localeName) { 28 | case 'en': 29 | return messages_en.messages; 30 | default: 31 | return null; 32 | } 33 | } 34 | 35 | /// User programs should call this before using [localeName] for messages. 36 | Future initializeMessages(String localeName) { 37 | var availableLocale = Intl.verifiedLocale( 38 | localeName, (locale) => _deferredLibraries[locale] != null, 39 | onFailure: (_) => null); 40 | if (availableLocale == null) { 41 | return new SynchronousFuture(false); 42 | } 43 | var lib = _deferredLibraries[availableLocale]; 44 | lib == null ? new SynchronousFuture(false) : lib(); 45 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 46 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 47 | return new SynchronousFuture(true); 48 | } 49 | 50 | bool _messagesExistFor(String locale) { 51 | try { 52 | return _findExact(locale) != null; 53 | } catch (e) { 54 | return false; 55 | } 56 | } 57 | 58 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 59 | var actualLocale = 60 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); 61 | if (actualLocale == null) return null; 62 | return _findExact(actualLocale); 63 | } 64 | -------------------------------------------------------------------------------- /lib/repositories/configs_repository/github/github_configs_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:retrofit/retrofit.dart'; 3 | import 'package:wakaranai/data/models/github/github_response_model.dart'; 4 | 5 | part 'github_configs_repository.g.dart'; 6 | 7 | @RestApi(baseUrl: "https://github.com/") 8 | abstract class GithubConfigsRepository { 9 | static const String mangaDirectory = 'manga'; 10 | static const String animeDirectory = 'anime'; 11 | 12 | factory GithubConfigsRepository(Dio dio) = _GithubConfigsRepository; 13 | 14 | @GET('/{org}/{repo}/tree/{branch}/{directory}') 15 | Future getDirectories( 16 | @Path() String org, 17 | @Path() String repo, { 18 | @Path() required String directory, 19 | @Path() String branch = 'main', 20 | @Header("max-age") int maxAge = 300, 21 | @Header("accept") String accept = "application/json", 22 | }); 23 | 24 | @GET("/{org}/{repo}/raw/{branch}/{concrete}") 25 | Future getConcreteContent({ 26 | @Path() required String org, 27 | @Path() required String repo, 28 | @Path() required String concrete, 29 | @Path() String branch = 'main', 30 | @Header("max-age") int maxAge = 300, 31 | @Header("accept") String accept = "application/json", 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /lib/repositories/configs_repository/local/local_configs_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:retrofit/http.dart'; 3 | import 'package:retrofit/retrofit.dart'; 4 | import 'package:wakaranai/data/models/configs_repo/configs_response/repo_configs_response.dart'; 5 | import 'package:wakaranai/data/models/remote_script/remote_script.dart'; 6 | 7 | part 'local_configs_repository.g.dart'; 8 | 9 | @RestApi(baseUrl: String.fromEnvironment('LOCAL_REPOSITORY_URL')) 10 | abstract class LocalConfigsRepository { 11 | factory LocalConfigsRepository(Dio dio, {String baseUrl}) = 12 | _LocalConfigsRepository; 13 | 14 | @GET('/configs') 15 | Future getConfigs(@Query("category") String category); 16 | 17 | @GET("/script") 18 | Future getScript(@Query("path") String path); 19 | } 20 | -------------------------------------------------------------------------------- /lib/repositories/database/anime_episode_activity_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/src/runtime/query_builder/query_builder.dart'; 2 | import 'package:wakaranai/data/domain/database/anime_episode_activity_domain.dart'; 3 | import 'package:wakaranai/database/wakaranai_database.dart'; 4 | import 'package:wakaranai/repositories/database/base_repository.dart'; 5 | import 'package:wakaranai/repositories/database/concerete_data_repository.dart'; 6 | 7 | class AnimeEpisodeActivityRepository extends BaseRepository< 8 | AnimeEpisodeActivityDomain, 9 | AnimeEpisodeActivityTableCompanion, 10 | AnimeEpisodeActivityTableData> { 11 | AnimeEpisodeActivityRepository({required super.database}) { 12 | _concreteDataRepository = ConcreteDataRepository(database: database); 13 | } 14 | 15 | late final ConcreteDataRepository _concreteDataRepository; 16 | 17 | Future?> getAllActivitiesByConcreteId( 18 | String uid) async { 19 | final concrete = await _concreteDataRepository 20 | .getBy<$ConcreteDataTableTable>(uid, where: (tbl) => tbl.uid); 21 | 22 | if (concrete == null) { 23 | return null; 24 | } else { 25 | return getAllBy<$AnimeEpisodeActivityTableTable>(concrete.id, 26 | where: (tbl) => tbl.concreteId); 27 | } 28 | } 29 | 30 | @override 31 | DeleteStatement deleteStatement() { 32 | return database.animeEpisodeActivityTable.delete(); 33 | } 34 | 35 | @override 36 | AnimeEpisodeActivityDomain fromDrift(AnimeEpisodeActivityTableData data) { 37 | return AnimeEpisodeActivityDomain( 38 | id: data.id, 39 | uid: data.uid, 40 | title: data.title, 41 | timestamp: data.timestamp, 42 | data: data.data, 43 | concreteId: data.concreteId, 44 | watchedTime: data.watchedTime, 45 | totalTime: data.totalTime, 46 | createdAt: data.createdAt, 47 | updatedAt: data.updatedAt, 48 | ); 49 | } 50 | 51 | @override 52 | InsertStatement insertStatement() { 53 | return database.animeEpisodeActivityTable.insert(); 54 | } 55 | 56 | @override 57 | SimpleSelectStatement selectStatement({bool distinct = false}) { 58 | return database.animeEpisodeActivityTable.select(); 59 | } 60 | 61 | @override 62 | AnimeEpisodeActivityTableCompanion toDrift(AnimeEpisodeActivityDomain domain, 63 | {bool update = false, bool create = false}) { 64 | return domain.toDrift(update: update, create: create); 65 | } 66 | 67 | @override 68 | UpdateStatement updateStatement() { 69 | return database.animeEpisodeActivityTable.update(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/repositories/database/chapter_activity_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/src/runtime/query_builder/query_builder.dart'; 2 | import 'package:wakaranai/data/domain/database/chapter_activity_domain.dart'; 3 | import 'package:wakaranai/database/wakaranai_database.dart'; 4 | import 'package:wakaranai/repositories/database/base_repository.dart'; 5 | import 'package:wakaranai/repositories/database/concerete_data_repository.dart'; 6 | 7 | class ChapterActivityRepository extends BaseRepository { 9 | ChapterActivityRepository({required super.database}) { 10 | _concreteDataRepository = ConcreteDataRepository(database: database); 11 | } 12 | 13 | late final ConcreteDataRepository _concreteDataRepository; 14 | 15 | Future?> getAllActivitiesByConcreteId( 16 | String uid) async { 17 | final concrete = await _concreteDataRepository 18 | .getBy<$ConcreteDataTableTable>(uid, where: (tbl) => tbl.uid); 19 | 20 | if (concrete == null) { 21 | return null; 22 | } else { 23 | return getAllBy<$ChapterActivityTableTable>(concrete.id, 24 | where: (tbl) => tbl.concreteId); 25 | } 26 | } 27 | 28 | @override 29 | DeleteStatement deleteStatement() { 30 | return database.chapterActivityTable.delete(); 31 | } 32 | 33 | @override 34 | ChapterActivityDomain fromDrift(ChapterActivityTableData data) { 35 | return ChapterActivityDomain.fromDrift(data); 36 | } 37 | 38 | @override 39 | InsertStatement insertStatement() { 40 | return database.chapterActivityTable.insert(); 41 | } 42 | 43 | @override 44 | SimpleSelectStatement selectStatement({bool distinct = false}) { 45 | return database.chapterActivityTable.select(); 46 | } 47 | 48 | @override 49 | ChapterActivityTableCompanion toDrift(ChapterActivityDomain domain, 50 | {bool update = false, bool create = false}) { 51 | return domain.toDrift(update: update, create: create); 52 | } 53 | 54 | @override 55 | UpdateStatement updateStatement() { 56 | return database.chapterActivityTable.update(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/repositories/database/concerete_data_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/domain/database/concrete_data_domain.dart'; 3 | import 'package:wakaranai/database/wakaranai_database.dart'; 4 | import 'package:wakaranai/repositories/database/base_repository.dart'; 5 | 6 | class ConcreteDataRepository extends BaseRepository { 8 | ConcreteDataRepository({required super.database}); 9 | 10 | Future getByUid(String uid) async { 11 | return getBy<$ConcreteDataTableTable>(uid, where: (tbl) => tbl.uid); 12 | } 13 | 14 | @override 15 | DeleteStatement deleteStatement() { 16 | return database.concreteDataTable.delete(); 17 | } 18 | 19 | @override 20 | ConcreteDataDomain fromDrift(ConcreteDataTableData data) { 21 | return ConcreteDataDomain.fromDrift(data); 22 | } 23 | 24 | @override 25 | InsertStatement insertStatement() { 26 | return database.concreteDataTable.insert(); 27 | } 28 | 29 | @override 30 | SimpleSelectStatement selectStatement({bool distinct = false}) { 31 | return database.concreteDataTable.select(); 32 | } 33 | 34 | @override 35 | ConcreteDataTableCompanion toDrift(ConcreteDataDomain domain, 36 | {bool update = false, bool create = false}) { 37 | return domain.toDrift(update: update, create: create); 38 | } 39 | 40 | @override 41 | UpdateStatement updateStatement() { 42 | return database.concreteDataTable.update(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/repositories/database/extension_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/domain/database/extension_domain.dart'; 3 | import 'package:wakaranai/database/wakaranai_database.dart'; 4 | import 'package:wakaranai/repositories/database/base_repository.dart'; 5 | 6 | class ExtensionRepository extends BaseRepository { 8 | ExtensionRepository({ 9 | required super.database, 10 | }); 11 | 12 | Future getByUid(String uid) async { 13 | return getBy<$ExtensionTableTable>( 14 | uid, 15 | where: (tbl) => tbl.uid, 16 | ); 17 | } 18 | 19 | Future updateByUid(ExtensionDomain domain) async { 20 | return updateBy<$ExtensionTableTable, String>( 21 | domain, 22 | by: (domain) => domain.config.uid, 23 | where: (tbl) => tbl.uid, 24 | ); 25 | } 26 | 27 | Future createUpdateByUid(ExtensionDomain domain) async { 28 | return createUpdateBy<$ExtensionTableTable, String>( 29 | domain, 30 | by: (domain) => domain.config.uid, 31 | where: (tbl) => tbl.uid, 32 | ); 33 | } 34 | 35 | @override 36 | DeleteStatement deleteStatement() { 37 | return database.extensionTable.delete(); 38 | } 39 | 40 | @override 41 | ExtensionDomain fromDrift(ExtensionTableData data) { 42 | return ExtensionDomain.fromDrift(data); 43 | } 44 | 45 | @override 46 | InsertStatement insertStatement() { 47 | return database.extensionTable.insert(); 48 | } 49 | 50 | @override 51 | SimpleSelectStatement selectStatement({bool distinct = false}) { 52 | return database.extensionTable.select(); 53 | } 54 | 55 | @override 56 | ExtensionTableCompanion toDrift(ExtensionDomain domain, 57 | {bool update = false, bool create = false}) { 58 | return domain.toDrift(update: update, create: create); 59 | } 60 | 61 | @override 62 | UpdateStatement updateStatement() { 63 | return database.extensionTable.update(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/repositories/database/extension_source_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/domain/database/extension_source_domain.dart'; 3 | import 'package:wakaranai/database/wakaranai_database.dart'; 4 | import 'package:wakaranai/repositories/database/base_repository.dart'; 5 | 6 | class ExtensionSourceRepository extends BaseRepository { 8 | ExtensionSourceRepository({required super.database}); 9 | 10 | @override 11 | DeleteStatement deleteStatement() { 12 | return database.extensionSourceTable.delete(); 13 | } 14 | 15 | @override 16 | ExtensionSourceDomain fromDrift(data) { 17 | return ExtensionSourceDomain.fromDrift(data); 18 | } 19 | 20 | @override 21 | InsertStatement insertStatement() { 22 | return database.extensionSourceTable.insert(); 23 | } 24 | 25 | @override 26 | SimpleSelectStatement selectStatement({bool distinct = false}) { 27 | return database.extensionSourceTable.select(); 28 | } 29 | 30 | @override 31 | ExtensionSourceTableCompanion toDrift(domain, 32 | {bool update = false, bool create = false}) { 33 | return domain.toDrift(update: update, create: create); 34 | } 35 | 36 | @override 37 | UpdateStatement updateStatement() { 38 | return database.extensionSourceTable.update(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/repositories/database/repository_providers.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:wakaranai/database/wakaranai_database.dart'; 4 | import 'package:wakaranai/repositories/database/anime_episode_activity_repository.dart'; 5 | import 'package:wakaranai/repositories/database/chapter_activity_repository.dart'; 6 | import 'package:wakaranai/repositories/database/concerete_data_repository.dart'; 7 | import 'package:wakaranai/repositories/database/extension_repository.dart'; 8 | import 'package:wakaranai/repositories/database/extension_source_repository.dart'; 9 | 10 | Widget repositoryProviders(BuildContext context, {required Widget child}) { 11 | return MultiRepositoryProvider( 12 | providers: [ 13 | RepositoryProvider( 14 | create: (context) { 15 | final db = WakaranaiDatabase(); 16 | 17 | // Use migrator here to modify tables 18 | // without changing the version 19 | // when working in debug mode 20 | 21 | // final migrator = Migrator(db); 22 | // migrator.createTable(db.animeEpisodeActivityTable); 23 | 24 | return db; 25 | }, 26 | lazy: false, 27 | ), 28 | RepositoryProvider( 29 | create: (context) => ChapterActivityRepository( 30 | database: context.read(), 31 | ), 32 | ), 33 | RepositoryProvider( 34 | create: (context) => ExtensionRepository( 35 | database: context.read(), 36 | ), 37 | ), 38 | RepositoryProvider( 39 | create: (context) => ExtensionSourceRepository( 40 | database: context.read(), 41 | ), 42 | ), 43 | RepositoryProvider( 44 | create: (context) => ConcreteDataRepository( 45 | database: context.read(), 46 | ), 47 | ), 48 | RepositoryProvider( 49 | create: (context) => AnimeEpisodeActivityRepository( 50 | database: context.read(), 51 | ), 52 | ) 53 | ], 54 | child: child, 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /lib/repositories/releases_repository/github/github_releases_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:retrofit/retrofit.dart'; 3 | import 'package:wakaranai/data/models/github/release_response/github_release_response_model.dart'; 4 | 5 | part 'github_releases_repository.g.dart'; 6 | 7 | @RestApi(baseUrl: "https://api.github.com/") 8 | abstract class GithubReleasesRepository { 9 | factory GithubReleasesRepository(Dio dio) = _GithubReleasesRepository; 10 | 11 | @GET("repos/{org}/{repo}/releases/latest") 12 | Future getLatestRelease({ 13 | @Path() required String org, 14 | @Path() required String repo, 15 | @Header("max-age") int maxAge = 300, 16 | @Header("accept") String accept = "application/json", 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /lib/repositories/releases_repository/github/github_releases_repository.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'github_releases_repository.dart'; 4 | 5 | // ************************************************************************** 6 | // RetrofitGenerator 7 | // ************************************************************************** 8 | 9 | // ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations 10 | 11 | class _GithubReleasesRepository implements GithubReleasesRepository { 12 | _GithubReleasesRepository( 13 | this._dio, { 14 | this.baseUrl, 15 | this.errorLogger, 16 | }) { 17 | baseUrl ??= 'https://api.github.com/'; 18 | } 19 | 20 | final Dio _dio; 21 | 22 | String? baseUrl; 23 | 24 | final ParseErrorLogger? errorLogger; 25 | 26 | @override 27 | Future getLatestRelease({ 28 | required String org, 29 | required String repo, 30 | int maxAge = 300, 31 | String accept = "application/json", 32 | }) async { 33 | final _extra = {}; 34 | final queryParameters = {}; 35 | final _headers = { 36 | r'max-age': maxAge, 37 | r'accept': accept, 38 | }; 39 | _headers.removeWhere((k, v) => v == null); 40 | const Map? _data = null; 41 | final _options = _setStreamType(Options( 42 | method: 'GET', 43 | headers: _headers, 44 | extra: _extra, 45 | ) 46 | .compose( 47 | _dio.options, 48 | 'repos/${org}/${repo}/releases/latest', 49 | queryParameters: queryParameters, 50 | data: _data, 51 | ) 52 | .copyWith( 53 | baseUrl: _combineBaseUrls( 54 | _dio.options.baseUrl, 55 | baseUrl, 56 | ))); 57 | final _result = await _dio.fetch>(_options); 58 | late GithubReleaseResponseModel _value; 59 | try { 60 | _value = GithubReleaseResponseModel.fromJson(_result.data!); 61 | } on Object catch (e, s) { 62 | errorLogger?.logError(e, s, _options); 63 | rethrow; 64 | } 65 | return _value; 66 | } 67 | 68 | RequestOptions _setStreamType(RequestOptions requestOptions) { 69 | if (T != dynamic && 70 | !(requestOptions.responseType == ResponseType.bytes || 71 | requestOptions.responseType == ResponseType.stream)) { 72 | if (T == String) { 73 | requestOptions.responseType = ResponseType.plain; 74 | } else { 75 | requestOptions.responseType = ResponseType.json; 76 | } 77 | } 78 | return requestOptions; 79 | } 80 | 81 | String _combineBaseUrls( 82 | String dioBaseUrl, 83 | String? baseUrl, 84 | ) { 85 | if (baseUrl == null || baseUrl.trim().isEmpty) { 86 | return dioBaseUrl; 87 | } 88 | 89 | final url = Uri.parse(baseUrl); 90 | 91 | if (url.isAbsolute) { 92 | return url.toString(); 93 | } 94 | 95 | return Uri.parse(dioBaseUrl).resolveUri(url).toString(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/repositories/shared_pref/default_extension_source_repository/default_extension_source_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class DefaultExtensionRepository { 4 | SharedPreferences? _prefs; 5 | 6 | static const String prefix = 'default_extension_'; 7 | 8 | Future init() async { 9 | _prefs = await SharedPreferences.getInstance(); 10 | } 11 | 12 | Future setDefaultExtensionId(int? extensionId) async { 13 | if (extensionId == null) { 14 | await _prefs!.remove('${prefix}id'); 15 | } else { 16 | await _prefs!.setInt('${prefix}id', extensionId); 17 | } 18 | } 19 | 20 | Future getDefaultExtensionId() async { 21 | return _prefs!.getInt('${prefix}id'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/repositories/shared_pref/default_manga_reader_mode_repository/default_manga_reader_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | import 'package:wakaranai/ui/services/manga/manga_service_viewer/concrete_viewer/chapter_viewer/chapter_view_mode.dart'; 3 | import 'package:wakaranai/utils/enum_converters.dart'; 4 | 5 | class DefaultMangaReaderRepository { 6 | late final SharedPreferences sharedPreferences; 7 | 8 | String _getKey(String serviceUid, String mangaUid) => 9 | "default_manga_reader_mode_${serviceUid}_$mangaUid"; 10 | 11 | Future init() async { 12 | sharedPreferences = await SharedPreferences.getInstance(); 13 | } 14 | 15 | Future setDefaultMangaReaderMode( 16 | ChapterViewMode mode, 17 | String serviceUid, 18 | String mangaUid, 19 | ) async { 20 | await sharedPreferences.setString( 21 | _getKey(serviceUid, mangaUid), 22 | encodeEnum(mode), 23 | ); 24 | } 25 | 26 | Future getDefaultMangaReaderMode( 27 | String serviceUid, 28 | String mangaUid, 29 | ) async { 30 | final mode = sharedPreferences.getString(_getKey(serviceUid, mangaUid)); 31 | if (mode == null) return null; 32 | return decodeEnum(ChapterViewMode.values, mode); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/res.dart: -------------------------------------------------------------------------------- 1 | /// Generated by AssetsRefGenerator on 2022/04/02 2 | class Res { 3 | static const String fast_html_dom_parser = 4 | "assets/js-libs/fast-html-dom-parser.js"; 5 | } 6 | -------------------------------------------------------------------------------- /lib/services/configs_service/configs_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/database/base_extension.dart'; 2 | import 'package:wakaranai/data/models/remote_script/remote_script.dart'; 3 | 4 | abstract class ConfigsService { 5 | Future> getMangaConfigs(); 6 | Future> getAnimeConfigs(); 7 | 8 | Future getRemoteScript(String path); 9 | } 10 | -------------------------------------------------------------------------------- /lib/services/configs_service/repo_configs_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 2 | import 'package:dio/dio.dart'; 3 | import 'package:wakaranai/data/models/remote_config/remote_config.dart'; 4 | import 'package:wakaranai/data/models/remote_script/remote_script.dart'; 5 | import 'package:wakaranai/env.dart'; 6 | import 'package:wakaranai/repositories/configs_repository/local/local_configs_repository.dart'; 7 | import 'package:wakaranai/services/configs_service/configs_service.dart'; 8 | 9 | class RepoConfigsService implements ConfigsService { 10 | late final LocalConfigsRepository _localRepository; 11 | 12 | RepoConfigsService({String? url}) { 13 | _localRepository = 14 | LocalConfigsRepository(Dio(), baseUrl: url ?? Env.localRepoUrl); 15 | } 16 | 17 | @override 18 | Future> getMangaConfigs() async { 19 | return (await _localRepository.getConfigs("manga")) 20 | .configs 21 | .where((RemoteConfig element) => 22 | element.config.type == ConfigInfoType.MANGA) 23 | .toList(); 24 | } 25 | 26 | @override 27 | Future> getAnimeConfigs() async { 28 | return (await _localRepository.getConfigs("anime")) 29 | .configs 30 | .where((RemoteConfig element) => 31 | element.config.type == ConfigInfoType.ANIME) 32 | .toList(); 33 | } 34 | 35 | @override 36 | Future getRemoteScript(String path) async { 37 | return _localRepository.getScript(path); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/services/protector_storage/protector_storage_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter_secure_storage/flutter_secure_storage.dart'; 4 | import 'package:wakaranai/data/models/protector/protector_storage_item.dart'; 5 | 6 | class ProtectorStorageService { 7 | final FlutterSecureStorage _secureStorage = const FlutterSecureStorage(); 8 | 9 | Future getItem({required String uid}) async { 10 | try { 11 | final String? possibleItem = await _secureStorage.read(key: uid); 12 | 13 | if (possibleItem == null) { 14 | return null; 15 | } 16 | 17 | return ProtectorStorageItem.fromJson(jsonDecode(possibleItem)); 18 | } catch (e) { 19 | return null; 20 | } 21 | } 22 | 23 | Future saveItem({required ProtectorStorageItem item}) async { 24 | await _secureStorage.write(key: item.uid, value: jsonEncode(item.toJson())); 25 | } 26 | 27 | Future clear() async { 28 | await _secureStorage.deleteAll(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/services/releases_service/releases_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:dio/dio.dart'; 2 | import 'package:wakaranai/data/domain/app_version.dart'; 3 | import 'package:wakaranai/data/domain/latest_release_data.dart'; 4 | import 'package:wakaranai/data/models/github/release_response/github_release_response_model.dart'; 5 | import 'package:wakaranai/env.dart'; 6 | import 'package:wakaranai/main.dart'; 7 | import 'package:wakaranai/repositories/releases_repository/github/github_releases_repository.dart'; 8 | 9 | class ReleasesService { 10 | final GithubReleasesRepository _githubReleasesRepository = 11 | GithubReleasesRepository(Dio()); 12 | 13 | Future getLatestReleaseDownloadUrl() async { 14 | try { 15 | final GithubReleaseResponseModel response = 16 | await _githubReleasesRepository.getLatestRelease( 17 | org: Env.appRepoOrg, 18 | repo: Env.appRepoName, 19 | ); 20 | 21 | final AppVersion latestReleaseVersion = 22 | AppVersion.fromString(response.tagName); 23 | final AppVersion currentVersion = 24 | AppVersion.fromString(Env.currentAppVersion); 25 | 26 | return LatestReleaseData( 27 | url: response.htmlUrl, 28 | latestVersion: latestReleaseVersion, 29 | currentVersion: currentVersion, 30 | ); 31 | } catch (e, s) { 32 | logger.e(e); 33 | logger.e(s); 34 | return null; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/services/settings_service/settings_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | import 'package:wakaranai/ui/services/manga/manga_service_viewer/concrete_viewer/chapter_viewer/chapter_view_mode.dart'; 4 | 5 | class SettingsService { 6 | static const String defaultReaderModePrefsKey = 'DEFAULT_CHAPTER_READER_MODE'; 7 | static const String defaultConfigsSourceIdKey = 'DEFAULT_CONFIGS_SOURCE_ID'; 8 | 9 | SharedPreferences? _prefs; 10 | 11 | Future getDefaultReaderMode() async { 12 | _prefs ??= await SharedPreferences.getInstance(); 13 | 14 | final String? defaultModeStr = _prefs!.getString(defaultReaderModePrefsKey); 15 | 16 | late ChapterViewMode defaultMode; 17 | 18 | if (defaultModeStr != null) { 19 | try { 20 | return ChapterViewMode.values.firstWhereOrNull( 21 | (ChapterViewMode element) => element.toString() == defaultModeStr)!; 22 | } catch (_) { 23 | // ignore 24 | } 25 | } 26 | 27 | defaultMode = ChapterViewMode.rightToLeft; 28 | _prefs!.setString( 29 | defaultReaderModePrefsKey, ChapterViewMode.rightToLeft.toString()); 30 | 31 | return defaultMode; 32 | } 33 | 34 | Future setDefaultReaderMode(ChapterViewMode mode) async { 35 | _prefs ??= await SharedPreferences.getInstance(); 36 | 37 | _prefs!.setString(defaultReaderModePrefsKey, mode.toString()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/ui/common/service_viewer/service_viewer_loader.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:wakaranai/blocs/service_view/service_view_cubit.dart'; 4 | import 'package:wakaranai/utils/app_colors.dart'; 5 | 6 | class ServiceViewerLoader extends StatelessWidget { 7 | const ServiceViewerLoader({super.key, required this.cubit}); 8 | 9 | final ServiceViewCubit cubit; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return BlocBuilder( 14 | bloc: cubit, 15 | builder: (BuildContext context, Object? state) { 16 | if (state is ServiceViewInitialized && state.loading) { 17 | return const Column( 18 | children: [ 19 | SizedBox( 20 | height: 24, 21 | ), 22 | CircularProgressIndicator(color: AppColors.primary), 23 | SizedBox( 24 | height: 24, 25 | ), 26 | ], 27 | ); 28 | } 29 | 30 | return const SizedBox(); 31 | }, 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/ui/gallery_view_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | import 'package:wakaranai/utils/heroes.dart'; 4 | import 'package:wakaranai/utils/images.dart'; 5 | import 'package:wakaranai/utils/text_styles.dart'; 6 | 7 | class GalleryViewCard extends StatelessWidget { 8 | const GalleryViewCard( 9 | {super.key, 10 | this.onTap, 11 | this.onLongPress, 12 | required this.uid, 13 | required this.headers, 14 | required this.cover, 15 | required this.title}); 16 | 17 | final VoidCallback? onTap; 18 | final VoidCallback? onLongPress; 19 | 20 | final Map headers; 21 | final String uid; 22 | final String cover; 23 | final String title; 24 | 25 | static double aspectRatio(double width) => width <= 200 ? 9 / 16 : 6 / 9; 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Hero( 30 | tag: Heroes.galleryViewToConcreteView(uid), 31 | child: ClipRRect( 32 | borderRadius: BorderRadius.circular( 33 | 4.0, 34 | ), 35 | child: Material( 36 | child: Ink.image( 37 | fit: BoxFit.cover, 38 | width: MediaQuery.of(context).size.width, 39 | image: getImageProvider(cover, headers: headers), 40 | child: InkWell( 41 | splashColor: AppColors.mediumLight.withOpacity(0.3), 42 | onTap: onTap, 43 | onLongPress: onLongPress, 44 | child: Stack( 45 | fit: StackFit.expand, 46 | children: [ 47 | Container( 48 | width: double.maxFinite, 49 | decoration: BoxDecoration( 50 | gradient: LinearGradient( 51 | begin: const Alignment(-1, 0), 52 | end: const Alignment(-1, 1), 53 | colors: [ 54 | AppColors.mainBlack.withOpacity(0.0), 55 | AppColors.mainBlack.withOpacity(.8), 56 | ])), 57 | ), 58 | Align( 59 | alignment: Alignment.bottomLeft, 60 | child: Padding( 61 | padding: const EdgeInsets.all(8.0), 62 | child: Text( 63 | title, 64 | maxLines: 3, 65 | style: medium(), 66 | overflow: TextOverflow.ellipsis, 67 | ), 68 | ), 69 | ) 70 | ], 71 | ), 72 | )), 73 | ), 74 | ), 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/ui/home/activity_history_page/cubit/activity_history_cubit_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:drift/drift.dart'; 2 | import 'package:wakaranai/data/domain/base_domain.dart'; 3 | import 'package:wakaranai/data/domain/database/base_activity_domain.dart'; 4 | import 'package:wakaranai/data/domain/database/concrete_data_domain.dart'; 5 | import 'package:wakaranai/data/domain/ui/activity_list_item.dart'; 6 | import 'package:wakaranai/repositories/database/base_repository.dart'; 7 | import 'package:wakaranai/repositories/database/concerete_data_repository.dart'; 8 | 9 | mixin ActivityHistoryCubitMixin { 10 | List getOrderingTerms() { 11 | return [ 12 | (t) => OrderingTerm( 13 | expression: t.createdAt, 14 | mode: OrderingMode.desc, 15 | ), 16 | (t) => OrderingTerm( 17 | expression: t.updatedAt, 18 | mode: OrderingMode.desc, 19 | ), 20 | ]; 21 | } 22 | 23 | Future> fetchConcretes( 24 | {required Set concreteIds, 25 | required ConcreteDataRepository concreteDataRepository}) async { 26 | final Map activities = {}; 27 | 28 | for (final id in concreteIds) { 29 | final concreteData = await concreteDataRepository.get(id); 30 | if (concreteData != null) { 31 | activities[id] = concreteData; 32 | } 33 | } 34 | 35 | return activities; 36 | } 37 | 38 | List> createActivityListItems({ 39 | required List data, 40 | required Map activities, 41 | required int Function(TDomain data) getConcreteId, 42 | }) { 43 | final List> items = []; 44 | 45 | DateTime? lastDay; 46 | 47 | for (final activity in data) { 48 | final concreteData = activities[getConcreteId(activity)]; 49 | lastDay ??= activity.updatedAt ?? activity.createdAt; 50 | final lastDayDay = lastDay.day; 51 | final activityDay = (activity.updatedAt ?? activity.createdAt).day; 52 | if (lastDayDay != activityDay || items.isEmpty) { 53 | items.add( 54 | ActivityListItem( 55 | day: activity.updatedAt ?? activity.createdAt, 56 | listItems: [], 57 | ), 58 | ); 59 | lastDay = activity.updatedAt ?? activity.createdAt; 60 | } 61 | 62 | items.last.listItems.add( 63 | DayActivityListItem( 64 | data: concreteData!, 65 | activity: activity, 66 | ), 67 | ); 68 | } 69 | 70 | return items; 71 | } 72 | 73 | List> removeActivityListItem( 74 | List> items, String uid) { 75 | for (var i = 0; i < items.length; i++) { 76 | final item = items[i]; 77 | final index = item.listItems.indexWhere((element) => element.activity.uid == uid); 78 | if (index != -1) { 79 | item.listItems.removeAt(index); 80 | if (item.listItems.isEmpty) { 81 | items.removeAt(i); 82 | } 83 | return items; 84 | } 85 | } 86 | return items; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /lib/ui/home/activity_history_page/cubit/anime_activity_history_state.dart: -------------------------------------------------------------------------------- 1 | part of 'anime_activity_history_cubit.dart'; 2 | 3 | @immutable 4 | sealed class AnimeActivityHistoryState { 5 | const AnimeActivityHistoryState(); 6 | } 7 | 8 | final class AnimeActivityHistoryInitial extends AnimeActivityHistoryState {} 9 | 10 | class AnimeActivityHistoryLoading extends AnimeActivityHistoryState {} 11 | 12 | class AnimeActivityHistoryLoaded extends AnimeActivityHistoryState { 13 | final List> animeActivities; 14 | 15 | const AnimeActivityHistoryLoaded({ 16 | required this.animeActivities, 17 | }); 18 | } 19 | 20 | class AnimeActivityHistoryError extends AnimeActivityHistoryState { 21 | final String message; 22 | 23 | const AnimeActivityHistoryError(this.message); 24 | } 25 | -------------------------------------------------------------------------------- /lib/ui/home/activity_history_page/cubit/manga_activity_history_state.dart: -------------------------------------------------------------------------------- 1 | part of 'manga_activity_history_cubit.dart'; 2 | 3 | @immutable 4 | abstract class ActivityHistoryState { 5 | const ActivityHistoryState(); 6 | } 7 | 8 | final class ActivityHistoryInitial extends ActivityHistoryState {} 9 | 10 | class ActivityHistoryLoading extends ActivityHistoryState {} 11 | 12 | class ActivityHistoryLoaded extends ActivityHistoryState { 13 | final List> mangaActivities; 14 | 15 | const ActivityHistoryLoaded({ 16 | required this.mangaActivities, 17 | }); 18 | 19 | } 20 | 21 | class ActivityHistoryError extends ActivityHistoryState { 22 | final String message; 23 | 24 | const ActivityHistoryError(this.message); 25 | } 26 | -------------------------------------------------------------------------------- /lib/ui/home/activity_history_page/widgets/activity_history_long_tap_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/data/domain/base_domain.dart'; 3 | import 'package:wakaranai/data/domain/database/base_activity_domain.dart'; 4 | import 'package:wakaranai/data/domain/ui/activity_list_item.dart'; 5 | import 'package:wakaranai/generated/l10n.dart'; 6 | 7 | class ActivityHistoryLongTapDialog 8 | extends StatelessWidget { 9 | const ActivityHistoryLongTapDialog({ 10 | super.key, 11 | required this.domain, 12 | required this.onDelete, 13 | }); 14 | 15 | final BaseActivityDomain domain; 16 | final VoidCallback onDelete; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return AlertDialog( 21 | title: Text(domain.title), 22 | content: Column( 23 | mainAxisSize: MainAxisSize.min, 24 | children: [ 25 | ListTile( 26 | title: Text(S.current.activity_history_long_tap_dialog_delete_button), 27 | onTap: () { 28 | onDelete(); 29 | Navigator.of(context).pop(); 30 | }, 31 | ), 32 | ], 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/ui/home/api_controller_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/api_clients/api_client.dart'; 2 | import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:wakaranai/blocs/api_client_controller/api_client_controller_cubit.dart'; 6 | import 'package:wakaranai/data/domain/database/base_extension.dart'; 7 | import 'package:wakaranai/repositories/database/extension_repository.dart'; 8 | import 'package:wakaranai/ui/home/configs_page/bloc/remote_configs/remote_configs_cubit.dart'; 9 | import 'package:wakaranai/utils/app_colors.dart'; 10 | 11 | class ApiControllerWrapper extends StatelessWidget { 12 | const ApiControllerWrapper( 13 | {super.key, required this.remoteConfig, required this.builder}); 14 | 15 | final BaseExtension? remoteConfig; 16 | final Widget Function(T apiClient, ConfigInfo) builder; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return BlocProvider( 21 | create: (BuildContext context) => ApiClientControllerCubit( 22 | remoteConfig: remoteConfig, 23 | remoteConfigsCubit: context.read(), 24 | extensionRepository: context.read(), 25 | )..buildApiClient(), 26 | child: BlocBuilder( 27 | builder: (BuildContext context, ApiClientControllerState state) { 28 | if (state is ApiClientControllerInitialized) { 29 | return builder(state.apiClient, state.configInfo); 30 | } 31 | return const Material( 32 | color: AppColors.backgroundColor, 33 | child: Center( 34 | child: CircularProgressIndicator(color: AppColors.primary), 35 | ), 36 | ); 37 | }, 38 | ), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/ui/home/concrete_view_cubit_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/api_clients/api_client.dart'; 2 | import 'package:capyscript/modules/waka_models/models/common/concrete_view.dart'; 3 | import 'package:capyscript/modules/waka_models/models/common/gallery_view.dart'; 4 | import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_bloc/flutter_bloc.dart'; 7 | import 'package:wakaranai/repositories/database/anime_episode_activity_repository.dart'; 8 | import 'package:wakaranai/repositories/database/chapter_activity_repository.dart'; 9 | import 'package:wakaranai/repositories/database/concerete_data_repository.dart'; 10 | import 'package:wakaranai/ui/services/cubits/concrete_view/concrete_view_cubit.dart'; 11 | 12 | class ConcreteViewCubitWrapper extends StatelessWidget { 14 | const ConcreteViewCubitWrapper({ 15 | super.key, 16 | required this.client, 17 | required this.configInfo, 18 | required this.builder, 19 | this.init, 20 | }); 21 | 22 | final void Function(ConcreteViewCubit)? init; 23 | final T client; 24 | final ConfigInfo configInfo; 25 | final Widget Function(BuildContext context, ConcreteViewState state) 26 | builder; 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return BlocProvider>( 31 | create: (BuildContext context) { 32 | final ConcreteViewCubit cubit = ConcreteViewCubit( 33 | ConcreteViewState( 34 | apiClient: client, 35 | configInfo: configInfo, 36 | ), 37 | concreteDataRepository: context.read(), 38 | chapterActivityRepository: context.read(), 39 | animeEpisodeActivityRepository: context.read(), 40 | ); 41 | 42 | if (init != null) { 43 | init!(cubit); 44 | } 45 | 46 | return cubit; 47 | }, 48 | child: 49 | BlocBuilder, ConcreteViewState>( 50 | builder: (BuildContext context, ConcreteViewState state) => 51 | builder(context, state), 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/ui/home/configs_page/bloc/remote_configs/remote_configs_state.dart: -------------------------------------------------------------------------------- 1 | part of 'remote_configs_cubit.dart'; 2 | 3 | @immutable 4 | abstract class RemoteConfigsState {} 5 | 6 | class RemoteConfigsLoading extends RemoteConfigsState {} 7 | 8 | class RemoteConfigsLoaded extends RemoteConfigsState { 9 | final String sourceName; 10 | final List mangaRemoteConfigs; 11 | final List animeRemoteConfigs; 12 | 13 | RemoteConfigsLoaded({ 14 | required this.mangaRemoteConfigs, 15 | required this.animeRemoteConfigs, 16 | required this.sourceName, 17 | }); 18 | } 19 | 20 | class RemoteConfigsError extends RemoteConfigsState { 21 | final String message; 22 | 23 | RemoteConfigsError({ 24 | required this.message, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /lib/ui/home/configs_page/configs_group.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:wakaranai/data/domain/database/base_extension.dart'; 5 | import 'package:wakaranai/ui/home/activity_history_page/cubit/anime_activity_history_cubit.dart'; 6 | import 'package:wakaranai/ui/home/activity_history_page/cubit/manga_activity_history_cubit.dart'; 7 | import 'package:wakaranai/ui/home/configs_page/config_card.dart'; 8 | import 'package:wakaranai/ui/services/anime/anime_service_viewer/anime_service_viewer.dart'; 9 | import 'package:wakaranai/ui/services/manga/manga_service_viewer/manga_service_viewer.dart'; 10 | import 'package:wakaranai/utils/app_colors.dart'; 11 | import 'package:wakaranai/utils/text_styles.dart'; 12 | 13 | import '../../routes.dart'; 14 | 15 | class ConfigsGroup extends StatelessWidget { 16 | const ConfigsGroup( 17 | {super.key, required this.title, required this.remoteConfigs}); 18 | 19 | final String title; 20 | final List remoteConfigs; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return SliverList( 25 | delegate: SliverChildListDelegate( 26 | [ 27 | const SizedBox(height: 16.0), 28 | Padding( 29 | padding: const EdgeInsets.symmetric(horizontal: 16.0), 30 | child: Text(title, 31 | style: semibold(size: 18, color: AppColors.mainWhite)), 32 | ), 33 | const SizedBox(height: 16.0), 34 | ...remoteConfigs.map( 35 | (BaseExtension e) => ConfigCard( 36 | configInfo: e.config, 37 | onTap: () { 38 | _onCardClick(context, e); 39 | }, 40 | ), 41 | ), 42 | const SizedBox(height: 16.0), 43 | ], 44 | ), 45 | ); 46 | } 47 | 48 | void _onCardClick( 49 | BuildContext context, 50 | BaseExtension remoteConfig, 51 | ) async { 52 | if (remoteConfig.config.type == ConfigInfoType.MANGA) { 53 | Navigator.of(context) 54 | .pushNamed( 55 | Routes.mangaServiceViewer, 56 | arguments: MangaServiceViewData(remoteConfig: remoteConfig)) 57 | .then((_) { 58 | context.read().init(); 59 | }); 60 | } else if (remoteConfig.config.type == ConfigInfoType.ANIME) { 61 | Navigator.of(context) 62 | .pushNamed( 63 | Routes.animeServiceViewer, 64 | arguments: AnimeServiceViewerData(remoteConfig: remoteConfig)) 65 | .then((_) { 66 | context.read().init(); 67 | }); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/ui/home/configs_page/extension_sources/add_extension_page/add_extension_page_arguments.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/database/extension_source_type.dart'; 2 | 3 | class AddExtensionPageArguments { 4 | final String name; 5 | final String url; 6 | final ExtensionSourceType type; 7 | 8 | final bool update; 9 | 10 | const AddExtensionPageArguments({ 11 | required this.name, 12 | required this.url, 13 | required this.type, 14 | this.update = false, 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /lib/ui/home/configs_page/extension_sources/add_extension_page/add_extension_page_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/database/extension_source_type.dart'; 2 | 3 | class AddExtensionPageResult { 4 | final String name; 5 | final String url; 6 | final ExtensionSourceType type; 7 | 8 | const AddExtensionPageResult({ 9 | required this.name, 10 | required this.url, 11 | required this.type, 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /lib/ui/home/configs_page/extension_sources/cubit/extension_sources_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:wakaranai/data/domain/database/extension_source_domain.dart'; 5 | import 'package:wakaranai/generated/l10n.dart'; 6 | import 'package:wakaranai/repositories/database/extension_source_repository.dart'; 7 | import 'package:wakaranai/ui/home/configs_page/extension_sources/add_extension_page/add_extension_page_result.dart'; 8 | import 'package:wakaranai/ui/widgets/snackbars.dart'; 9 | 10 | part 'extension_sources_state.dart'; 11 | 12 | class ExtensionSourcesCubit extends Cubit { 13 | ExtensionSourcesCubit({ 14 | required this.extensionSourceRepository, 15 | }) : super(ExtensionSourcesInitial()); 16 | 17 | final ExtensionSourceRepository extensionSourceRepository; 18 | 19 | Future init() async { 20 | emit(ExtensionSourcesLoading()); 21 | 22 | try { 23 | final repositories = await extensionSourceRepository.getAll(); 24 | 25 | emit(ExtensionSourcesLoaded( 26 | repositories: repositories, 27 | )); 28 | } catch (e) { 29 | emit(ExtensionSourcesError( 30 | message: S.current.extension_sources_page_error_loading_sources, 31 | )); 32 | } 33 | } 34 | 35 | Future delete( 36 | BuildContext context, ExtensionSourceDomain domain) async { 37 | emit(ExtensionSourcesLoading()); 38 | 39 | final res = await extensionSourceRepository.delete(domain); 40 | 41 | if (res == null) { 42 | SnackBars.showErrorSnackBar( 43 | context: context, 44 | error: S.current.extension_source_page_error_removing_source); 45 | } 46 | 47 | await init(); 48 | } 49 | 50 | Future update( 51 | BuildContext context, ExtensionSourceDomain domain) async { 52 | emit(ExtensionSourcesLoading()); 53 | 54 | final res = await extensionSourceRepository.update(domain); 55 | 56 | if (res == null) { 57 | SnackBars.showErrorSnackBar( 58 | context: context, 59 | error: S.current.extension_source_page_error_updating_source); 60 | } 61 | 62 | await init(); 63 | } 64 | 65 | Future add(BuildContext context, AddExtensionPageResult data) async { 66 | emit(ExtensionSourcesLoading()); 67 | 68 | final res = await extensionSourceRepository.create( 69 | ExtensionSourceDomain( 70 | id: 0, 71 | name: data.name, 72 | url: data.url, 73 | type: data.type, 74 | createdAt: DateTime.now(), 75 | updatedAt: DateTime.now(), 76 | ), 77 | ); 78 | 79 | if (res == null) { 80 | SnackBars.showErrorSnackBar( 81 | context: context, 82 | error: S.current.extension_source_page_error_adding_source); 83 | } 84 | 85 | await init(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/ui/home/configs_page/extension_sources/cubit/extension_sources_state.dart: -------------------------------------------------------------------------------- 1 | part of 'extension_sources_cubit.dart'; 2 | 3 | @immutable 4 | abstract class ExtensionSourcesState { 5 | const ExtensionSourcesState(); 6 | } 7 | 8 | class ExtensionSourcesInitial extends ExtensionSourcesState {} 9 | 10 | class ExtensionSourcesLoading extends ExtensionSourcesState {} 11 | 12 | class ExtensionSourcesLoaded extends ExtensionSourcesState { 13 | final List repositories; 14 | 15 | const ExtensionSourcesLoaded({ 16 | required this.repositories, 17 | }); 18 | } 19 | 20 | class ExtensionSourcesError extends ExtensionSourcesState { 21 | final String message; 22 | 23 | const ExtensionSourcesError({ 24 | required this.message, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /lib/ui/home/configs_page/extension_sources/extension_sources_page_result.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/database/extension_source_type.dart'; 2 | 3 | class ExtensionSourcesPageResult { 4 | final int? id; 5 | final String url; 6 | final ExtensionSourceType type; 7 | final String name; 8 | 9 | const ExtensionSourcesPageResult({ 10 | this.id, 11 | required this.url, 12 | required this.type, 13 | required this.name, 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /lib/ui/home/cubit/home_page_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_bloc/flutter_bloc.dart'; 2 | 3 | part 'home_page_state.dart'; 4 | 5 | class HomePageCubit extends Cubit { 6 | HomePageCubit() : super(const HomePageState(currentPage: 0)); 7 | 8 | void changePage(int index) { 9 | emit(state.copyWith(currentPage: index)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/ui/home/cubit/home_page_state.dart: -------------------------------------------------------------------------------- 1 | part of 'home_page_cubit.dart'; 2 | 3 | class HomePageState { 4 | final int currentPage; 5 | 6 | const HomePageState({ 7 | required this.currentPage, 8 | }); 9 | 10 | HomePageState copyWith({ 11 | int? currentPage, 12 | }) { 13 | return HomePageState( 14 | currentPage: currentPage ?? this.currentPage, 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/ui/home/service_view_cubit_wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/api_clients/api_client.dart'; 2 | import 'package:capyscript/modules/waka_models/models/common/gallery_view.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:wakaranai/blocs/service_view/service_view_cubit.dart'; 6 | 7 | class ServiceViewCubitWrapper 8 | extends StatelessWidget { 9 | const ServiceViewCubitWrapper( 10 | {super.key, required this.client, required this.builder}); 11 | 12 | final T client; 13 | final Widget Function(BuildContext context, ServiceViewState state) 14 | builder; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return BlocProvider>( 19 | create: (BuildContext context) => 20 | ServiceViewCubit(ServiceViewInitial(client: client)), 21 | child: BlocBuilder, ServiceViewState>( 22 | builder: (BuildContext context, ServiceViewState state) => 23 | builder(context, state), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/ui/home/service_viewer_app_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/api_clients/api_client.dart'; 2 | import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:wakaranai/blocs/service_view/service_view_cubit.dart'; 5 | import 'package:wakaranai/generated/l10n.dart'; 6 | import 'package:wakaranai/ui/home/web_browser_page.dart'; 7 | import 'package:wakaranai/utils/app_colors.dart'; 8 | import 'package:wakaranai/utils/text_styles.dart'; 9 | 10 | class ServiceViewerAppBar extends StatelessWidget { 11 | const ServiceViewerAppBar( 12 | {required this.configInfo, 13 | required this.apiClient, 14 | required this.searchController, 15 | required this.cubit, 16 | this.state, 17 | super.key}); 18 | 19 | final ServiceViewCubit cubit; 20 | final ServiceViewInitialized? state; 21 | final ConfigInfo configInfo; 22 | final ApiClient apiClient; 23 | final TextEditingController searchController; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Padding( 28 | padding: EdgeInsets.only(top: MediaQuery.of(context).padding.top), 29 | child: Column( 30 | mainAxisSize: MainAxisSize.max, 31 | children: [ 32 | Row( 33 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 34 | children: [ 35 | Padding( 36 | padding: const EdgeInsets.only(left: 12.0), 37 | child: Text( 38 | configInfo.name, 39 | style: medium(size: 24), 40 | ), 41 | ), 42 | if (configInfo.protectorConfig != null) 43 | IconButton( 44 | icon: const Icon( 45 | Icons.explore_rounded, 46 | color: AppColors.mainWhite, 47 | ), 48 | onPressed: () { 49 | openWebView(context, apiClient, configInfo); 50 | }, 51 | ) 52 | ], 53 | ), 54 | if (state != null && configInfo.searchAvailable) 55 | Flexible( 56 | child: Padding( 57 | padding: const EdgeInsets.symmetric(horizontal: 12), 58 | child: TextField( 59 | controller: searchController, 60 | onSubmitted: (String value) { 61 | cubit.search(searchController.text); 62 | }, 63 | cursorColor: AppColors.primary, 64 | style: medium(size: 16), 65 | decoration: InputDecoration( 66 | contentPadding: const EdgeInsets.only(bottom: 4.0), 67 | isCollapsed: true, 68 | enabledBorder: const UnderlineInputBorder( 69 | borderSide: BorderSide(color: AppColors.primary)), 70 | focusedBorder: const UnderlineInputBorder( 71 | borderSide: BorderSide(color: AppColors.primary)), 72 | hintText: S.current.service_viewer_search_field_hint_text, 73 | hintStyle: medium(size: 16)), 74 | ), 75 | ), 76 | ) 77 | ], 78 | ), 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/ui/home/settings_page/cubit/settings/settings_state.dart: -------------------------------------------------------------------------------- 1 | part of 'settings_cubit.dart'; 2 | 3 | @immutable 4 | abstract class SettingsState { 5 | const SettingsState(); 6 | } 7 | 8 | class SettingsInitial extends SettingsState {} 9 | 10 | class SettingsInitialized extends SettingsState { 11 | final ChapterViewMode defaultMode; 12 | 13 | final bool loading; 14 | 15 | const SettingsInitialized({ 16 | required this.defaultMode, 17 | this.loading = false, 18 | }); 19 | 20 | SettingsInitialized copyWith({ 21 | ChapterViewMode? defaultMode, 22 | bool? loading, 23 | }) { 24 | return SettingsInitialized( 25 | defaultMode: defaultMode ?? this.defaultMode, 26 | loading: loading ?? this.loading, 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/ui/home/widgets/bottom_navigation_bar_item_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:wakaranai/ui/home/cubit/home_page_cubit.dart'; 4 | import 'package:wakaranai/utils/app_colors.dart'; 5 | 6 | class BottomNavigationBarItemWidgetData { 7 | final IconData icon; 8 | final String text; 9 | 10 | const BottomNavigationBarItemWidgetData({ 11 | required this.icon, 12 | required this.text, 13 | }); 14 | } 15 | 16 | class BottomNavigationBarItemWidget extends StatefulWidget { 17 | const BottomNavigationBarItemWidget({ 18 | super.key, 19 | required this.data, 20 | this.selected = false, 21 | required this.index, 22 | required this.borderRadius, 23 | }); 24 | 25 | final BottomNavigationBarItemWidgetData data; 26 | final int index; 27 | final bool selected; 28 | final BorderRadius borderRadius; 29 | 30 | @override 31 | State createState() => 32 | _BottomNavigationBarItemWidgetState(); 33 | } 34 | 35 | class _BottomNavigationBarItemWidgetState 36 | extends State 37 | with SingleTickerProviderStateMixin { 38 | late final AnimationController _controller; 39 | 40 | final ColorTween _colorTween = ColorTween( 41 | begin: AppColors.primary, 42 | end: AppColors.backgroundColor, 43 | ); 44 | 45 | @override 46 | void initState() { 47 | super.initState(); 48 | _controller = AnimationController( 49 | duration: const Duration(milliseconds: 200), 50 | vsync: this, 51 | ); 52 | 53 | if (widget.selected) { 54 | _controller.forward(); 55 | } 56 | } 57 | 58 | @override 59 | void dispose() { 60 | _controller.dispose(); 61 | super.dispose(); 62 | } 63 | 64 | @override 65 | void didUpdateWidget(covariant BottomNavigationBarItemWidget oldWidget) { 66 | if (widget != oldWidget) {} 67 | if (widget.selected) { 68 | _controller.forward(); 69 | } else { 70 | _controller.reverse(); 71 | } 72 | 73 | super.didUpdateWidget(oldWidget); 74 | } 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | return Expanded( 79 | child: InkWell( 80 | onTap: () { 81 | context.read().changePage(widget.index); 82 | }, 83 | borderRadius: widget.borderRadius, 84 | splashColor: AppColors.primary.withOpacity(0.8), 85 | child: Ink( 86 | height: 48, 87 | color: AppColors.backgroundColor, 88 | child: AnimatedBuilder( 89 | animation: _controller, 90 | builder: (context, child) => Icon( 91 | widget.data.icon, 92 | color: _colorTween.evaluate(_controller), 93 | ), 94 | ), 95 | ), 96 | ), 97 | ); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /lib/ui/local_gallery_view_wrapper.dart: -------------------------------------------------------------------------------- 1 | // import 'package:capyscript/modules/waka_models/models/config_info/config_info.dart'; 2 | // import 'package:flutter/material.dart'; 3 | // import 'package:flutter_bloc/flutter_bloc.dart'; 4 | 5 | // class LocalGalleryViewWrapper, 6 | // G extends LocalGalleryView> extends StatelessWidget { 7 | // const LocalGalleryViewWrapper( 8 | // {Key? key, required this.type, required this.uid, required this.builder}) 9 | // : super(key: key); 10 | // 11 | // final ConfigInfoType type; 12 | // final String uid; 13 | // final Widget Function(BuildContext context, LocalGalleryViewCardState state) 14 | // builder; 15 | // 16 | // @override 17 | // Widget build(BuildContext context) { 18 | // return BlocProvider( 19 | // create: (context) => LocalGalleryViewCardCubit(uid: uid)..init(), 20 | // child: BlocBuilder, 21 | // LocalGalleryViewCardState>( 22 | // builder: (context, state) { 23 | // return builder(context, state); 24 | // }, 25 | // ), 26 | // ); 27 | // } 28 | // } 29 | -------------------------------------------------------------------------------- /lib/ui/routes.dart: -------------------------------------------------------------------------------- 1 | class Routes { 2 | static const String splashScreen = '/'; 3 | static const String home = '/home'; 4 | static const String myExtensionSources = '/myExtensionSources'; 5 | static const String addExtensionSource = '/addExtensionSource'; 6 | static const String mangaServiceViewer = '/mangaServiceViewer'; 7 | static const String animeServiceViewer = '/animeServiceViewer'; 8 | static const String mangaConcreteViewer = '/mangaConcreteViewer'; 9 | static const String animeConcreteViewer = '/animeConcreteViewer'; 10 | static const String chapterViewer = '/chapterViewer'; 11 | static const String webBrowser = '/webBrowser'; 12 | static const String iframeAnimePlayer = '/iframeAnimePlayer'; 13 | } 14 | -------------------------------------------------------------------------------- /lib/ui/services/anime/anime_concrete_viewer/anime_player_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | import 'package:wakaranai/utils/text_styles.dart'; 4 | 5 | class AnimePlayerButton extends StatefulWidget { 6 | const AnimePlayerButton( 7 | {super.key, 8 | required this.title, 9 | required this.onClick, 10 | required this.selected}); 11 | 12 | final String title; 13 | final bool selected; 14 | final VoidCallback onClick; 15 | 16 | @override 17 | State createState() => _AnimePlayerButtonState(); 18 | } 19 | 20 | class _AnimePlayerButtonState extends State 21 | with SingleTickerProviderStateMixin { 22 | late final AnimationController _controller; 23 | 24 | late final ColorTween _colorTween = ColorTween( 25 | begin: AppColors.backgroundColor, 26 | end: AppColors.primary, 27 | ); 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | 33 | _controller = AnimationController( 34 | duration: const Duration(milliseconds: 500), 35 | vsync: this, 36 | reverseDuration: const Duration(milliseconds: 300), 37 | ); 38 | 39 | _colorTween.animate(_controller); 40 | 41 | if (widget.selected) { 42 | _controller.forward(); 43 | } 44 | } 45 | 46 | @override 47 | void didUpdateWidget(covariant AnimePlayerButton oldWidget) { 48 | if (oldWidget.selected != widget.selected) { 49 | if (widget.selected) { 50 | _controller.forward(); 51 | } else { 52 | _controller.reverse(); 53 | } 54 | } 55 | super.didUpdateWidget(oldWidget); 56 | } 57 | 58 | @override 59 | void dispose() { 60 | _controller.dispose(); 61 | super.dispose(); 62 | } 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return AnimatedBuilder( 67 | animation: _controller, 68 | builder: (context, child) { 69 | return Card( 70 | elevation: 8.0, 71 | margin: EdgeInsets.zero, 72 | color: _colorTween.evaluate(_controller), 73 | shape: RoundedRectangleBorder( 74 | borderRadius: BorderRadius.circular(24.0)), 75 | child: child!, 76 | ); 77 | }, 78 | child: InkWell( 79 | onTap: widget.onClick, 80 | borderRadius: BorderRadius.circular(24.0), 81 | child: Padding( 82 | padding: const EdgeInsets.all(8.0), 83 | child: Text(widget.title, style: medium(size: 16)), 84 | ), 85 | )); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/ui/services/anime/anime_iframe_player/cubit/anime_iframe_player_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:capyscript/modules/waka_models/models/anime/anime_concrete_view/anime_concrete_view.dart'; 5 | import 'package:capyscript/modules/waka_models/models/anime/anime_concrete_view/anime_video/anime_video.dart'; 6 | import 'package:meta/meta.dart'; 7 | import 'package:wakaranai/data/domain/database/anime_episode_activity_domain.dart'; 8 | import 'package:wakaranai/database/wakaranai_database.dart'; 9 | import 'package:wakaranai/repositories/database/anime_episode_activity_repository.dart'; 10 | import 'package:wakaranai/repositories/database/concerete_data_repository.dart'; 11 | 12 | part 'anime_iframe_player_state.dart'; 13 | 14 | class AnimeIframePlayerCubit extends Cubit { 15 | AnimeIframePlayerCubit({ 16 | required this.concreteDataRepository, 17 | required this.animeEpisodeActivityRepository, 18 | }) : super(AnimeIframePlayerInitial()); 19 | 20 | final ConcreteDataRepository concreteDataRepository; 21 | final AnimeEpisodeActivityRepository animeEpisodeActivityRepository; 22 | 23 | void init({ 24 | required AnimeConcreteView anime, 25 | required AnimeVideo video, 26 | }) async { 27 | final concreteData = await concreteDataRepository.getByUid(anime.uid); 28 | 29 | if (concreteData != null) { 30 | animeEpisodeActivityRepository 31 | .createUpdateBy<$AnimeEpisodeActivityTableTable, String>( 32 | AnimeEpisodeActivityDomain( 33 | id: 0, 34 | title: video.title, 35 | uid: video.uid, 36 | concreteId: concreteData.id, 37 | data: jsonEncode(video.data), 38 | watchedTime: null, 39 | totalTime: null, 40 | timestamp: video.timestamp, 41 | createdAt: DateTime.now(), 42 | ), 43 | by: (tbl) => tbl.uid, 44 | where: (tbl) => tbl.uid, 45 | ); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/ui/services/anime/anime_iframe_player/cubit/anime_iframe_player_state.dart: -------------------------------------------------------------------------------- 1 | part of 'anime_iframe_player_cubit.dart'; 2 | 3 | @immutable 4 | sealed class AnimeIframePlayerState {} 5 | 6 | final class AnimeIframePlayerInitial extends AnimeIframePlayerState {} 7 | -------------------------------------------------------------------------------- /lib/ui/services/concrete_viewer_mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/api_clients/api_client.dart'; 2 | import 'package:capyscript/modules/waka_models/models/common/concrete_view.dart'; 3 | import 'package:capyscript/modules/waka_models/models/common/gallery_view.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:wakaranai/ui/services/cubits/concrete_view/concrete_view_cubit.dart'; 7 | import 'package:wakaranai/ui/widgets/change_order_icon_button.dart'; 8 | import 'package:wakaranai/ui/widgets/expandable_fab_widget.dart'; 9 | 10 | mixin ConcreteViewerMixin, 11 | G extends GalleryView> { 12 | ConcreteViewCubit getConcreteViewCubit(BuildContext context) { 13 | return context.read>(); 14 | } 15 | 16 | ExpandableFabWidget getExpandableFabWidget( 17 | BuildContext context, ConcreteViewInitialized state) { 18 | return ExpandableFabWidget( 19 | expanded: state.selection.isNotEmpty, 20 | items: { 21 | ExpandableFabDirection.left: [ 22 | ExpandableFabItemData( 23 | icon: SwitchIconButton( 24 | iconOn: const Icon(Icons.delete), 25 | state: false, 26 | onTap: () { 27 | getConcreteViewCubit(context).markSelectedAsNotCompleted(); 28 | }, 29 | ), 30 | ), 31 | ExpandableFabItemData( 32 | icon: SwitchIconButton( 33 | iconOn: const Icon(Icons.check), 34 | state: false, 35 | onTap: () { 36 | getConcreteViewCubit(context).markSelectedAsCompleted(); 37 | }, 38 | ), 39 | ), 40 | ], 41 | ExpandableFabDirection.top: [ 42 | ExpandableFabItemData( 43 | icon: SwitchIconButton( 44 | iconOn: const Icon(Icons.clear), 45 | state: false, 46 | onTap: () { 47 | getConcreteViewCubit(context).clearSelection(); 48 | }, 49 | ), 50 | ), 51 | ExpandableFabItemData( 52 | icon: SwitchIconButton( 53 | iconOn: const Icon(Icons.border_clear), 54 | state: false, 55 | onTap: () { 56 | getConcreteViewCubit(context).invertSelection(); 57 | }, 58 | ), 59 | ), 60 | ExpandableFabItemData( 61 | icon: SwitchIconButton( 62 | iconOn: const Icon(Icons.select_all), 63 | state: false, 64 | onTap: () { 65 | getConcreteViewCubit(context).selectAll(); 66 | }, 67 | ), 68 | ), 69 | ], 70 | }, 71 | child: SwitchIconButton( 72 | iconOn: const Icon(Icons.filter_list_rounded), 73 | state: state.order == ConcreteViewOrder.def, 74 | onTap: () { 75 | getConcreteViewCubit(context).changeOrder( 76 | state.order == ConcreteViewOrder.def 77 | ? ConcreteViewOrder.defReverse 78 | : ConcreteViewOrder.def); 79 | }, 80 | ), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/ui/services/cubits/chapter_view/chapter_view_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:capyscript/modules/waka_models/models/manga/manga_concrete_view/chapter/pages/pages.dart'; 2 | import 'package:capyscript/modules/waka_models/models/manga/manga_concrete_view/chapters_group/chapters_group.dart'; 3 | import 'package:wakaranai/data/domain/database/concrete_data_domain.dart'; 4 | import 'package:wakaranai/ui/services/manga/manga_service_viewer/concrete_viewer/chapter_viewer/chapter_view_mode.dart'; 5 | import 'package:wakaranai/ui/services/manga/manga_service_viewer/concrete_viewer/chapter_viewer/chapter_viewer.dart'; 6 | 7 | abstract class ChapterViewState { 8 | const ChapterViewState(); 9 | } 10 | 11 | class ChapterViewInit extends ChapterViewState {} 12 | 13 | class ChapterViewInitialized extends ChapterViewState { 14 | final ChapterViewerData data; 15 | 16 | final List pages; 17 | final Pages currentPages; 18 | final Map headers; 19 | 20 | final ChaptersGroup group; 21 | 22 | final ConcreteDataDomain? concreteData; 23 | 24 | final int currentPage; 25 | final int totalPages; 26 | 27 | final ChapterViewMode mode; 28 | final bool controlsVisible; 29 | final bool controlsEnabled; 30 | 31 | final bool canGetNextPages; 32 | final bool canGetPreviousPages; 33 | 34 | const ChapterViewInitialized({ 35 | required this.data, 36 | required this.pages, 37 | required this.currentPages, 38 | required this.headers, 39 | required this.group, 40 | required this.concreteData, 41 | required this.currentPage, 42 | required this.totalPages, 43 | required this.mode, 44 | required this.controlsVisible, 45 | required this.controlsEnabled, 46 | required this.canGetNextPages, 47 | required this.canGetPreviousPages, 48 | }); 49 | 50 | ChapterViewInitialized copyWith({ 51 | ChapterViewerData? data, 52 | List? pages, 53 | Pages? currentPages, 54 | Map? headers, 55 | ChaptersGroup? group, 56 | ConcreteDataDomain? concreteData, 57 | int? currentPage, 58 | int? totalPages, 59 | ChapterViewMode? mode, 60 | bool? controlsVisible, 61 | bool? controlsEnabled, 62 | bool? canGetNextPages, 63 | bool? canGetPreviousPages, 64 | }) { 65 | return ChapterViewInitialized( 66 | data: data ?? this.data, 67 | pages: pages ?? this.pages, 68 | currentPages: currentPages ?? this.currentPages, 69 | headers: headers ?? this.headers, 70 | group: group ?? this.group, 71 | concreteData: concreteData ?? this.concreteData, 72 | currentPage: currentPage ?? this.currentPage, 73 | totalPages: totalPages ?? this.totalPages, 74 | mode: mode ?? this.mode, 75 | controlsVisible: controlsVisible ?? this.controlsVisible, 76 | controlsEnabled: controlsEnabled ?? this.controlsEnabled, 77 | canGetNextPages: canGetNextPages ?? this.canGetNextPages, 78 | canGetPreviousPages: canGetPreviousPages ?? this.canGetPreviousPages, 79 | ); 80 | } 81 | } 82 | 83 | class ChapterViewError extends ChapterViewState { 84 | final String message; 85 | 86 | const ChapterViewError({required this.message}); 87 | } 88 | -------------------------------------------------------------------------------- /lib/ui/services/cubits/concrete_view/concrete_view_state.dart: -------------------------------------------------------------------------------- 1 | part of 'concrete_view_cubit.dart'; 2 | 3 | class ConcreteViewState, 4 | G extends GalleryView> { 5 | final T apiClient; 6 | final ConfigInfo configInfo; 7 | 8 | const ConcreteViewState({ 9 | required this.apiClient, 10 | required this.configInfo, 11 | }); 12 | } 13 | 14 | class ConcreteViewInitialized< 15 | T extends ApiClient, 16 | C extends ConcreteView, 17 | G extends GalleryView> extends ConcreteViewState { 18 | final C concreteView; 19 | final ConcreteDataDomain? domain; 20 | final ConcreteViewOrder order; 21 | final int groupIndex; 22 | final Map chapterActivities; 23 | final Map animeEpisodeActivities; 24 | final Map imageHeaders; 25 | final List selection; 26 | 27 | const ConcreteViewInitialized({ 28 | required this.concreteView, 29 | required this.domain, 30 | required this.order, 31 | required this.groupIndex, 32 | required super.apiClient, 33 | required super.configInfo, 34 | required this.chapterActivities, 35 | required this.animeEpisodeActivities, 36 | required this.imageHeaders, 37 | required this.selection, 38 | }); 39 | 40 | ConcreteViewInitialized copyWith({ 41 | C? concreteView, 42 | ConcreteDataDomain? domain, 43 | int? groupIndex, 44 | ConcreteViewOrder? order, 45 | Map? imageHeaders, 46 | T? apiClient, 47 | ConfigInfo? configInfo, 48 | Map? chapterActivities, 49 | Map? animeEpisodeActivities, 50 | List? selection, 51 | }) { 52 | return ConcreteViewInitialized( 53 | concreteView: concreteView ?? this.concreteView, 54 | domain: domain ?? this.domain, 55 | order: order ?? this.order, 56 | groupIndex: groupIndex ?? this.groupIndex, 57 | imageHeaders: imageHeaders ?? this.imageHeaders, 58 | apiClient: apiClient ?? this.apiClient, 59 | configInfo: configInfo ?? this.configInfo, 60 | chapterActivities: chapterActivities ?? this.chapterActivities, 61 | animeEpisodeActivities: 62 | animeEpisodeActivities ?? this.animeEpisodeActivities, 63 | selection: selection ?? this.selection, 64 | ); 65 | } 66 | } 67 | 68 | class ConcreteViewError, 69 | G extends GalleryView> extends ConcreteViewState { 70 | final String message; 71 | 72 | const ConcreteViewError({ 73 | required this.message, 74 | required super.apiClient, 75 | required super.configInfo, 76 | }); 77 | } 78 | -------------------------------------------------------------------------------- /lib/ui/services/manga/manga_service_viewer/concrete_viewer/chapter_viewer/chapter_view_mode.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/generated/l10n.dart'; 2 | 3 | enum ChapterViewMode { rightToLeft, leftToRight, webtoon } 4 | 5 | String chapterViewModelToString(ChapterViewMode mode) { 6 | switch (mode) { 7 | case ChapterViewMode.rightToLeft: 8 | return S.current.chapter_viewer_right_to_left_read_mode; 9 | case ChapterViewMode.leftToRight: 10 | return S.current.chapter_viewer_left_to_right_read_mode; 11 | case ChapterViewMode.webtoon: 12 | return S.current.chapter_viewer_webtoon; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/ui/services/manga/manga_service_viewer/concrete_viewer/chapter_viewer/pages_change_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | 4 | class PagesChangeButton extends StatefulWidget { 5 | const PagesChangeButton({super.key, required this.onTap, required this.icon}); 6 | 7 | final VoidCallback onTap; 8 | final Widget icon; 9 | 10 | @override 11 | State createState() => _PagesChangeButtonState(); 12 | } 13 | 14 | class _PagesChangeButtonState extends State 15 | with TickerProviderStateMixin { 16 | late final AnimationController _shadowAnimationController; 17 | late final AnimationController _colorAnimationController; 18 | 19 | late final Animation _shadowAnimation; 20 | late final Animation _colorAnimation; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | 26 | _colorAnimationController = 27 | AnimationController(vsync: this, duration: const Duration(seconds: 2)) 28 | ..repeat(reverse: true); 29 | 30 | _colorAnimation = 31 | ColorTween(begin: AppColors.secondary, end: AppColors.mediumLight) 32 | .animate(CurveTween(curve: Curves.easeInOutCubic) 33 | .animate(_colorAnimationController)) 34 | ..addListener(_animationListener); 35 | 36 | _shadowAnimationController = 37 | AnimationController(vsync: this, duration: const Duration(seconds: 2)) 38 | ..repeat(reverse: true) 39 | ..forward(); 40 | 41 | _shadowAnimation = Tween(begin: 1, end: 4).animate( 42 | CurveTween(curve: Curves.easeInOutCubic) 43 | .animate(_shadowAnimationController)) 44 | ..addListener(_animationListener); 45 | } 46 | 47 | void _animationListener() { 48 | setState(() {}); 49 | } 50 | 51 | @override 52 | void dispose() { 53 | _colorAnimationController 54 | ..stop() 55 | ..removeListener(_animationListener) 56 | ..dispose(); 57 | _shadowAnimationController 58 | ..stop() 59 | ..removeListener(_animationListener) 60 | ..dispose(); 61 | 62 | super.dispose(); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return InkWell( 68 | onTap: widget.onTap, 69 | child: Container( 70 | width: 48, 71 | height: 48, 72 | decoration: BoxDecoration( 73 | color: _colorAnimation.value, 74 | boxShadow: [ 75 | BoxShadow( 76 | color: _colorAnimation.value ?? AppColors.primary, 77 | spreadRadius: _shadowAnimation.value, 78 | blurRadius: _shadowAnimation.value * 3) 79 | ], 80 | shape: BoxShape.circle), 81 | child: widget.icon, 82 | ), 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/ui/services/manga/manga_service_viewer/concrete_viewer/manga_provider_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | import 'package:wakaranai/utils/text_styles.dart'; 4 | 5 | class MangaProviderButton extends StatefulWidget { 6 | const MangaProviderButton( 7 | {super.key, 8 | required this.title, 9 | required this.onClick, 10 | required this.selected}); 11 | 12 | final String title; 13 | final bool selected; 14 | final VoidCallback onClick; 15 | 16 | @override 17 | State createState() => _MangaProviderButtonState(); 18 | } 19 | 20 | class _MangaProviderButtonState extends State 21 | with SingleTickerProviderStateMixin { 22 | late final AnimationController _controller; 23 | 24 | late final ColorTween _colorTween = ColorTween( 25 | begin: AppColors.backgroundColor, 26 | end: AppColors.primary, 27 | ); 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | 33 | _controller = AnimationController( 34 | duration: const Duration(milliseconds: 500), 35 | vsync: this, 36 | reverseDuration: const Duration(milliseconds: 300), 37 | ); 38 | 39 | _colorTween.animate(_controller); 40 | 41 | if (widget.selected) { 42 | _controller.forward(); 43 | } 44 | } 45 | 46 | @override 47 | void didUpdateWidget(covariant MangaProviderButton oldWidget) { 48 | if (oldWidget.selected != widget.selected) { 49 | if (widget.selected) { 50 | _controller.forward(); 51 | } else { 52 | _controller.reverse(); 53 | } 54 | } 55 | super.didUpdateWidget(oldWidget); 56 | } 57 | 58 | @override 59 | void dispose() { 60 | _controller.dispose(); 61 | super.dispose(); 62 | } 63 | 64 | @override 65 | Widget build(BuildContext context) { 66 | return AnimatedBuilder( 67 | animation: _controller, 68 | builder: (context, child) { 69 | return Card( 70 | elevation: 8.0, 71 | margin: EdgeInsets.zero, 72 | color: _colorTween.evaluate(_controller), 73 | shadowColor: AppColors.shadowColor, 74 | shape: 75 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(24.0)), 76 | child: child, 77 | ); 78 | }, 79 | child: InkWell( 80 | onTap: widget.onClick, 81 | borderRadius: BorderRadius.circular(24.0), 82 | child: Padding( 83 | padding: const EdgeInsets.all(8.0), 84 | child: Text(widget.title, style: medium(size: 16)), 85 | ), 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/ui/splashscreen/splashscreen_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:wakaranai/blocs/auth/authentication_cubit.dart'; 4 | import 'package:wakaranai/main.dart'; 5 | import 'package:wakaranai/ui/routes.dart'; 6 | import 'package:wakaranai/utils/app_colors.dart'; 7 | 8 | class SplashScreen extends StatefulWidget { 9 | const SplashScreen({super.key}); 10 | 11 | @override 12 | State createState() => _SplashScreenState(); 13 | } 14 | 15 | class _SplashScreenState extends State { 16 | @override 17 | void initState() { 18 | super.initState(); 19 | 20 | context.read().authorize('armatura@gmail.com', '1234'); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Scaffold( 26 | body: BlocBuilder( 27 | builder: (BuildContext context, AuthenticationState state) { 28 | Future.delayed(const Duration(seconds: 0), () { 29 | if (state is AuthenticationAuthenticated) { 30 | WakaranaiApp.navigator 31 | ?.pushNamedAndRemoveUntil(Routes.home, (Route route) => false); 32 | } 33 | }); 34 | 35 | return Container( 36 | color: AppColors.primary, 37 | ); 38 | }), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/ui/widgets/appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | AppBar buildAppBar() { 4 | return AppBar( 5 | toolbarHeight: 0, 6 | elevation: 0, 7 | title: null, 8 | ); 9 | } 10 | -------------------------------------------------------------------------------- /lib/ui/widgets/change_order_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | 4 | class SwitchIconButton extends StatelessWidget { 5 | const SwitchIconButton({ 6 | super.key, 7 | required this.state, 8 | required this.onTap, 9 | required this.iconOn, 10 | this.iconOff, 11 | }); 12 | 13 | final Widget iconOn; 14 | final Widget? iconOff; 15 | 16 | final bool state; 17 | final VoidCallback onTap; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return IconButton( 22 | padding: EdgeInsets.zero, 23 | onPressed: onTap, 24 | icon: Container( 25 | decoration: BoxDecoration( 26 | color: AppColors.backgroundColor, 27 | shape: BoxShape.circle, 28 | boxShadow: [ 29 | BoxShadow( 30 | color: AppColors.shadowColor, 31 | blurRadius: 4, 32 | spreadRadius: 2) 33 | ]), 34 | child: Padding( 35 | padding: const EdgeInsets.all(8.0), 36 | child: AnimatedRotation( 37 | duration: const Duration(milliseconds: 200), 38 | turns: state ? 0.5 : 0, 39 | curve: Curves.easeOutCubic, 40 | child: state ? iconOn : iconOff ?? iconOn, 41 | ), 42 | ), 43 | )); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/ui/widgets/confirmation_dialog/confirmation_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | import 'package:wakaranai/utils/text_styles.dart'; 4 | 5 | class ConfirmationDialog extends StatelessWidget { 6 | const ConfirmationDialog({ 7 | super.key, 8 | required this.title, 9 | required this.message, 10 | required this.yesText, 11 | required this.noText, 12 | }); 13 | 14 | final String title; 15 | final String message; 16 | 17 | final String yesText; 18 | final String noText; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return AlertDialog( 23 | title: Text(title), 24 | content: Text(message), 25 | actions: [ 26 | TextButton( 27 | onPressed: () => Navigator.of(context).pop(false), 28 | child: Text( 29 | noText, 30 | style: semibold(color: AppColors.mainGrey), 31 | ), 32 | ), 33 | TextButton( 34 | onPressed: () => Navigator.of(context).pop(true), 35 | child: Text( 36 | yesText, 37 | style: semibold(color: AppColors.primary), 38 | ), 39 | ), 40 | ], 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/ui/widgets/elevated_appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | 4 | class ElevatedAppbar extends StatelessWidget { 5 | const ElevatedAppbar({super.key, this.leading, this.title, this.actions}); 6 | 7 | final Widget? leading; 8 | final Widget? title; 9 | final Widget? actions; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return SliverAppBar( 14 | backgroundColor: AppColors.backgroundColor, 15 | foregroundColor: AppColors.mainWhite, 16 | shadowColor: AppColors.shadowColor, 17 | surfaceTintColor: AppColors.backgroundColor, 18 | leading: const SizedBox(), 19 | leadingWidth: 0, 20 | elevation: 16.0, 21 | snap: true, 22 | floating: true, 23 | pinned: true, 24 | title: Stack( 25 | alignment: Alignment.center, 26 | children: [ 27 | Align( 28 | alignment: Alignment.center, 29 | child: title, 30 | ), 31 | Align( 32 | alignment: Alignment.centerRight, 33 | child: actions, 34 | ), 35 | Align( 36 | alignment: Alignment.centerLeft, 37 | child: leading, 38 | ) 39 | ], 40 | )); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/ui/widgets/image_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:cached_network_image/cached_network_image.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:path_provider/path_provider.dart'; 7 | import 'package:wakaranai/utils/app_colors.dart'; 8 | 9 | class ImageWidget extends StatefulWidget { 10 | const ImageWidget( 11 | {super.key, required this.url, required this.uid, required this.headers}); 12 | 13 | final String uid; 14 | final String url; 15 | final Map headers; 16 | 17 | @override 18 | State createState() => _ImageWidgetState(); 19 | } 20 | 21 | class _ImageWidgetState extends State { 22 | File? _file; 23 | bool _initialized = false; 24 | 25 | @override 26 | void initState() { 27 | if (widget.url.startsWith("data:")) { 28 | (() async { 29 | final Directory temp = await getTemporaryDirectory(); 30 | final File file = File("${temp.path}/${widget.uid}.png"); 31 | if (!(await file.exists())) { 32 | await file.writeAsBytes(base64Decode(widget.url.split(',').last)); 33 | } 34 | 35 | _file = file; 36 | })() 37 | .then((Object? value) { 38 | _initialized = true; 39 | if (mounted) { 40 | setState(() {}); 41 | } 42 | }); 43 | } else { 44 | _initialized = true; 45 | } 46 | 47 | super.initState(); 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | if (!_initialized) { 53 | return SizedBox( 54 | height: MediaQuery.of(context).size.height * 0.7, 55 | width: MediaQuery.of(context).size.width, 56 | child: const Center( 57 | child: CircularProgressIndicator( 58 | color: AppColors.primary, 59 | ), 60 | ), 61 | ); 62 | } 63 | 64 | final double height = MediaQuery.of(context).size.height * 0.7; 65 | final double width = MediaQuery.of(context).size.width; 66 | 67 | if (_file != null) { 68 | return Image.file(_file!, 69 | width: width, 70 | height: height, 71 | fit: BoxFit.cover, 72 | isAntiAlias: true, 73 | errorBuilder: 74 | (BuildContext context, Object error, StackTrace? stackTrace) => 75 | SizedBox( 76 | height: height, 77 | width: width, 78 | child: Center( 79 | child: Text( 80 | "Error", 81 | style: Theme.of(context).textTheme.headlineMedium, 82 | ), 83 | ), 84 | )); 85 | } 86 | 87 | return CachedNetworkImage( 88 | imageUrl: widget.url, 89 | width: width, 90 | fit: BoxFit.cover, 91 | progressIndicatorBuilder: 92 | (BuildContext context, String url, DownloadProgress progress) => 93 | SizedBox( 94 | height: height, 95 | width: width, 96 | child: Center( 97 | child: CircularProgressIndicator( 98 | color: AppColors.primary, value: progress.progress), 99 | ), 100 | ), 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /lib/ui/widgets/infinite_rotation_animation.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class InfiniteRotationAnimation extends StatefulWidget { 7 | const InfiniteRotationAnimation({super.key, required this.child}); 8 | 9 | final Widget child; 10 | 11 | @override 12 | State createState() => 13 | _InfiniteRotationAnimationState(); 14 | } 15 | 16 | class _InfiniteRotationAnimationState extends State 17 | with SingleTickerProviderStateMixin { 18 | late final AnimationController _controller; 19 | 20 | late final Animation _rotationTween; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | 26 | _controller = AnimationController( 27 | duration: const Duration(seconds: 2), 28 | vsync: this, 29 | ) 30 | ..repeat() 31 | ..addListener(() { 32 | if (!mounted) return; 33 | setState(() {}); 34 | }); 35 | _rotationTween = Tween(begin: 0, end: 2 * pi) 36 | .chain(CurveTween(curve: Curves.easeInOut)) 37 | .animate(_controller); 38 | } 39 | 40 | @override 41 | void dispose() { 42 | _controller.dispose(); 43 | super.dispose(); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | return Transform.rotate( 49 | angle: _rotationTween.value, 50 | child: widget.child, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/ui/widgets/outlined_text_form_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | 4 | class OutlinedTextFormField extends StatelessWidget { 5 | const OutlinedTextFormField({ 6 | super.key, 7 | required this.controller, 8 | required this.title, 9 | required this.hint, 10 | this.validator, 11 | this.onChanged, 12 | }); 13 | 14 | final TextEditingController controller; 15 | final String title; 16 | final String hint; 17 | final String? Function(String?)? validator; 18 | final void Function(String)? onChanged; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return TextFormField( 23 | controller: controller, 24 | onChanged: onChanged, 25 | validator: validator, 26 | cursorColor: AppColors.secondary, 27 | decoration: InputDecoration( 28 | labelText: title, 29 | labelStyle: const TextStyle( 30 | color: AppColors.mainWhite, 31 | ), 32 | errorStyle: const TextStyle( 33 | color: AppColors.red, 34 | ), 35 | hintText: hint, 36 | hintStyle: const TextStyle( 37 | color: AppColors.mainGrey, 38 | ), 39 | focusColor: AppColors.secondary, 40 | iconColor: AppColors.secondary, 41 | fillColor: AppColors.secondary, 42 | hoverColor: AppColors.secondary, 43 | border: OutlineInputBorder( 44 | borderRadius: BorderRadius.circular(8), 45 | borderSide: const BorderSide( 46 | color: AppColors.secondary, 47 | ), 48 | ), 49 | enabledBorder: OutlineInputBorder( 50 | borderRadius: BorderRadius.circular(8), 51 | borderSide: const BorderSide( 52 | color: AppColors.secondary, 53 | ), 54 | ), 55 | errorBorder: OutlineInputBorder( 56 | borderRadius: BorderRadius.circular(8), 57 | borderSide: const BorderSide( 58 | color: AppColors.red, 59 | ), 60 | ), 61 | focusedErrorBorder: OutlineInputBorder( 62 | borderRadius: BorderRadius.circular(8), 63 | borderSide: const BorderSide( 64 | color: AppColors.red, 65 | ), 66 | ), 67 | disabledBorder: OutlineInputBorder( 68 | borderRadius: BorderRadius.circular(8), 69 | borderSide: BorderSide( 70 | color: AppColors.secondary.withOpacity(0.25), 71 | ), 72 | ), 73 | focusedBorder: OutlineInputBorder( 74 | borderRadius: BorderRadius.circular(8), 75 | borderSide: const BorderSide( 76 | color: AppColors.secondary, 77 | ), 78 | ), 79 | )); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/ui/widgets/primary_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | import 'package:wakaranai/utils/text_styles.dart'; 4 | 5 | class PrimaryButton extends StatelessWidget { 6 | const PrimaryButton( 7 | {super.key, 8 | this.padding, 9 | required this.title, 10 | this.color, 11 | this.onPressed}); 12 | 13 | final EdgeInsets? padding; 14 | final VoidCallback? onPressed; 15 | final String title; 16 | final Color? color; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Padding( 21 | padding: 22 | padding ?? const EdgeInsets.symmetric(horizontal: 12, vertical: 8), 23 | child: ElevatedButton( 24 | onPressed: onPressed, 25 | style: ButtonStyle( 26 | backgroundColor: 27 | MaterialStateProperty.all(color ?? AppColors.primary)), 28 | child: Text(title, style: medium()), 29 | )); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/ui/widgets/primary_input_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | import 'package:wakaranai/utils/text_styles.dart'; 4 | 5 | class PrimaryInputWidget extends StatelessWidget { 6 | const PrimaryInputWidget( 7 | {super.key, 8 | required this.controller, 9 | required this.label, 10 | this.validator}); 11 | 12 | final TextEditingController controller; 13 | final String label; 14 | final String? Function(String?)? validator; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return TextFormField( 19 | cursorColor: AppColors.primary, 20 | controller: controller, 21 | validator: validator, 22 | decoration: InputDecoration( 23 | labelStyle: regular(size: 16, color: AppColors.mainWhite), 24 | labelText: label, 25 | contentPadding: 26 | const EdgeInsets.symmetric(vertical: 4, horizontal: 8), 27 | enabledBorder: const OutlineInputBorder( 28 | borderSide: BorderSide(color: AppColors.secondary, width: 1)), 29 | errorBorder: const OutlineInputBorder( 30 | borderSide: BorderSide(color: AppColors.red, width: 1)), 31 | focusedErrorBorder: const OutlineInputBorder( 32 | borderSide: BorderSide(color: AppColors.red, width: 1)), 33 | focusedBorder: const OutlineInputBorder( 34 | borderSide: BorderSide(color: AppColors.primary, width: 1)))); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/ui/widgets/snackbars.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wakaranai/utils/app_colors.dart'; 3 | import 'package:wakaranai/utils/text_styles.dart'; 4 | 5 | class SnackBars { 6 | static void showErrorSnackBar( 7 | {required BuildContext context, required String error}) { 8 | ScaffoldMessenger.of(context).showSnackBar(SnackBar( 9 | backgroundColor: AppColors.backgroundColor, 10 | content: Text( 11 | error, 12 | style: semibold( 13 | size: 12, 14 | color: AppColors.red, 15 | ), 16 | ), 17 | )); 18 | } 19 | 20 | static void showSnackBar( 21 | {required BuildContext context, required String message}) { 22 | ScaffoldMessenger.of(context).showSnackBar(SnackBar( 23 | backgroundColor: AppColors.backgroundColor, 24 | content: Text( 25 | message, 26 | style: semibold( 27 | size: 12, 28 | color: AppColors.mainWhite, 29 | ), 30 | ), 31 | )); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/utils/app_colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class AppColors { 4 | static const Color backgroundColor = Color(0xFF303030); 5 | static final Color mainBlack = Colors.black.withAlpha(155); 6 | static final Color shadowColor = Colors.black.withOpacity(0.2); 7 | static const Color mainWhite = Colors.white; 8 | 9 | static const Color secondary = Color(0xFF00D3AC); 10 | static const Color primary = Color(0xFF00E676); 11 | 12 | static const Color mediumLight = Color(0xFF00BADA); 13 | static const Color mediumLight2 = Color(0xFF009EF1); 14 | 15 | static const Color mediumDark = Color(0xFF007CE9); 16 | static const Color mediumDark2 = Color(0xFF3F54C2); 17 | 18 | static const Color mainGrey = Colors.grey; 19 | static const Color red = Colors.redAccent; 20 | } 21 | -------------------------------------------------------------------------------- /lib/utils/browser.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_inappwebview/flutter_inappwebview.dart'; 2 | 3 | InAppWebViewSettings getDefaultBrowserSettings() => InAppWebViewSettings( 4 | userAgent: 5 | "Mozilla/5.0 (Linux; Android 13; SM-N960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.63 Mobile Safari/537.36", 6 | javaScriptEnabled: true, 7 | preferredContentMode: UserPreferredContentMode.DESKTOP, 8 | mediaPlaybackRequiresUserGesture: true, 9 | javaScriptCanOpenWindowsAutomatically: true, 10 | cacheEnabled: true, 11 | useHybridComposition: true, 12 | supportMultipleWindows: true, 13 | cacheMode: CacheMode.LOAD_DEFAULT, 14 | databaseEnabled: true, 15 | scrollBarStyle: ScrollBarStyle.SCROLLBARS_OUTSIDE_OVERLAY, 16 | allowContentAccess: true, 17 | allowFileAccess: true, 18 | domStorageEnabled: true, 19 | clearSessionCache: false, 20 | forceDark: ForceDark.ON, 21 | loadsImagesAutomatically: true, 22 | allowsInlineMediaPlayback: true, 23 | ); 24 | -------------------------------------------------------------------------------- /lib/utils/decoders.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | List decodeJsonStringList(String data) { 4 | return (jsonDecode(data) as List) 5 | .map((dynamic e) => e.toString()) 6 | .toList(); 7 | } 8 | -------------------------------------------------------------------------------- /lib/utils/enum_converters.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | T? decodeEnum(List values, String? value) { 4 | if (value == null) return null; 5 | return values.firstWhere((element) => element.toString() == value); 6 | } 7 | 8 | String encodeEnum(T value) { 9 | return value.toString(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/utils/github_url_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:wakaranai/data/domain/github_url_data.dart'; 2 | 3 | class GithubUrlParser { 4 | final String url; 5 | 6 | const GithubUrlParser({ 7 | required this.url, 8 | }); 9 | 10 | GithubUrlData? parse() { 11 | try { 12 | final uri = Uri.parse(url); 13 | final pathSegments = uri.pathSegments; 14 | final org = pathSegments[0]; 15 | final repo = pathSegments[1]; 16 | return GithubUrlData( 17 | org: org, 18 | repo: repo, 19 | ); 20 | } catch (e) { 21 | return null; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/utils/globals.dart: -------------------------------------------------------------------------------- 1 | import 'package:path_provider/path_provider.dart'; 2 | 3 | const String appDocumentsDir = 'wakaranai'; 4 | 5 | Future getAppDocumentsDir() async => 6 | '${(await getApplicationDocumentsDirectory()).path}/$appDocumentsDir'; 7 | -------------------------------------------------------------------------------- /lib/utils/heroes.dart: -------------------------------------------------------------------------------- 1 | class Heroes { 2 | static String galleryViewToConcreteView(String uid) => 3 | "galleryViewToConcreteView_$uid"; 4 | } 5 | -------------------------------------------------------------------------------- /lib/utils/images.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | ImageProvider getImageProvider(String url, {Map? headers}) { 7 | return url.startsWith("data:") 8 | ? Image.memory(base64Decode(url.split(',').last)).image 9 | : CachedNetworkImageProvider(url, headers: headers); 10 | } 11 | -------------------------------------------------------------------------------- /lib/utils/text_styles.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | 3 | import 'app_colors.dart'; 4 | 5 | TextStyle thin({double size = 16, Color? color}) { 6 | return TextStyle( 7 | fontSize: size, 8 | fontFamily: 'Ubuntu', 9 | fontWeight: FontWeight.w100, 10 | color: color ?? AppColors.mainWhite); 11 | } 12 | 13 | TextStyle bold({double size = 16, Color? color}) { 14 | return TextStyle( 15 | fontSize: size, 16 | fontFamily: 'Ubuntu', 17 | fontWeight: FontWeight.w700, 18 | color: color ?? AppColors.mainWhite); 19 | } 20 | 21 | TextStyle regular({double size = 14, Color? color}) { 22 | return TextStyle( 23 | fontSize: size, 24 | fontFamily: 'Ubuntu', 25 | fontWeight: FontWeight.w400, 26 | color: color ?? AppColors.mainWhite); 27 | } 28 | 29 | TextStyle semibold({double size = 14, Color? color}) { 30 | return TextStyle( 31 | fontSize: size, 32 | fontFamily: 'Ubuntu', 33 | color: color ?? AppColors.mainWhite, 34 | fontWeight: FontWeight.w600); 35 | } 36 | 37 | TextStyle medium({double size = 14, Color? color}) { 38 | return TextStyle( 39 | fontSize: size, 40 | fontFamily: "Ubuntu", 41 | color: color ?? AppColors.mainWhite, 42 | fontWeight: FontWeight.w500); 43 | } 44 | 45 | extension StringX on String { 46 | String get overflow => Characters(this) 47 | .replaceAll(Characters(''), Characters('\u{200B}')) 48 | .toString(); 49 | } 50 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: wakaranai 2 | description: Wakaranai is a manga reader app, designed to dynamically parse and fetch manga content from various websites. The app utilizes a custom script language called Capyscript 3 | 4 | publish_to: 'none' 5 | 6 | version: 0.2.0+4 7 | 8 | environment: 9 | sdk: ">=3.4.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_localizations: 15 | sdk: flutter 16 | 17 | capyscript: 18 | path: ../../StudioProjects/capyscript 19 | # git: https://github.com/Sayuri128/capyscript 20 | 21 | logger: ^2.4.0 22 | path: ^1.8.3 23 | path_provider: ^2.1.3 24 | cupertino_icons: ^1.0.8 25 | flutter_bloc: ^8.1.6 26 | equatable: ^2.0.5 27 | json_serializable: ^6.8.0 28 | retrofit: ^4.1.0 29 | dio: ^5.4.0 30 | json_annotation: ^4.9.0 31 | transparent_image: ^2.0.1 32 | intl: ^0.19.0 33 | intl_utils: ^2.8.7 34 | photo_view: ^0.15.0 35 | cached_network_image: ^3.3.1 36 | flutter_cache_manager: ^3.3.2 37 | adaptive_dialog: ^2.1.0 38 | flutter_svg: ^2.0.10+1 39 | pull_to_refresh: ^2.0.0 40 | flutter_dotenv: ^5.1.0 41 | flutter_launcher_icons: ^0.14.2 42 | another_xlider: ^3.0.2 43 | transparent_pointer: ^1.0.1 44 | visibility_detector: ^0.4.0+2 45 | scrollable_positioned_list: ^0.3.8 46 | shared_preferences: ^2.2.3 47 | flutter_inappwebview: ^6.1.5 48 | flutter_secure_storage: ^9.2.2 49 | flutter_isolate: ^2.0.4 50 | url_launcher: ^6.3.0 51 | drift: ^2.19.1+1 52 | sqlite3_flutter_libs: ^0.5.24 53 | collection: ^1.18.0 54 | 55 | dependency_overrides: 56 | watcher: ^1.1.0 57 | 58 | flutter_intl: 59 | enabled: true 60 | 61 | flutter_icons: 62 | android: true 63 | ios: true 64 | image_path: "assets/favicon.png" 65 | 66 | #dependency_overrides: 67 | # analyzer: ^4.1.0 68 | 69 | dev_dependencies: 70 | flutter_test: 71 | sdk: flutter 72 | 73 | flutter_lints: ^5.0.0 74 | build_runner: ^2.4.11 75 | retrofit_generator: ^9.1.5 76 | drift_dev: ^2.19.1 77 | 78 | flutter: 79 | 80 | uses-material-design: true 81 | 82 | assets: 83 | - .env 84 | 85 | fonts: 86 | - family: Ubuntu mono 87 | fonts: 88 | - asset: fonts/Ubuntu-B.ttf 89 | - asset: fonts/Ubuntu-C.ttf 90 | - asset: fonts/Ubuntu-L.ttf 91 | - asset: fonts/Ubuntu-M.ttf 92 | - asset: fonts/Ubuntu-R.ttf 93 | - asset: fonts/Ubuntu-Th.ttf 94 | - asset: fonts/UbuntuMono-B.ttf 95 | - asset: fonts/UbuntuMono-R.ttf 96 | 97 | --------------------------------------------------------------------------------