├── .githooks └── pre-commit ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── publish.yml │ └── web.yml ├── .gitignore ├── .metadata ├── .sdkmanrc ├── .vscode ├── launch.json ├── settings.json └── sorayomi.code-snippets ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── suwayomi │ │ │ │ └── tachidesk_sorayomi │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ ├── android12splash.png │ │ │ ├── branding.png │ │ │ └── splash.png │ │ │ ├── drawable-mdpi │ │ │ ├── android12splash.png │ │ │ ├── branding.png │ │ │ └── splash.png │ │ │ ├── drawable-night-hdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-mdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-night-xhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-xxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night-xxxhdpi │ │ │ ├── android12splash.png │ │ │ └── splash.png │ │ │ ├── drawable-night │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ ├── android12splash.png │ │ │ ├── branding.png │ │ │ └── splash.png │ │ │ ├── drawable-xxhdpi │ │ │ ├── android12splash.png │ │ │ ├── branding.png │ │ │ └── splash.png │ │ │ ├── drawable-xxxhdpi │ │ │ ├── android12splash.png │ │ │ ├── branding.png │ │ │ └── splash.png │ │ │ ├── drawable │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── launcher_icon.png │ │ │ ├── values-night-v31 │ │ │ └── styles.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values-v31 │ │ │ └── styles.xml │ │ │ ├── values │ │ │ └── styles.xml │ │ │ └── xml │ │ │ └── network_security_config.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── icons │ ├── dark_icon.png │ ├── launcher │ ├── from_suwayomi.png │ ├── ios_sorayomi_icon.png │ ├── sorayomi_icon.ico │ ├── sorayomi_icon.png │ └── sorayomi_preview_icon.png │ ├── light_icon.png │ └── previous_done.png ├── build.yaml ├── devtools_options.yaml ├── graphql.config.yml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── 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-50x50@1x.png │ │ ├── Icon-App-50x50@2x.png │ │ ├── Icon-App-57x57@1x.png │ │ ├── Icon-App-57x57@2x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-72x72@1x.png │ │ ├── Icon-App-72x72@2x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ ├── BrandingImage.imageset │ │ ├── BrandingImage.png │ │ ├── BrandingImage@2x.png │ │ ├── BrandingImage@3x.png │ │ └── Contents.json │ ├── LaunchBackground.imageset │ │ ├── Contents.json │ │ ├── background.png │ │ └── darkbackground.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ ├── LaunchImageDark.png │ │ ├── LaunchImageDark@2x.png │ │ ├── LaunchImageDark@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── l10n.yaml ├── lib ├── main.dart └── src │ ├── abstracts │ ├── locale_enum.dart │ └── value_enum.dart │ ├── constants │ ├── app_constants.dart │ ├── app_sizes.dart │ ├── db_keys.dart │ ├── endpoints.dart │ ├── enum.dart │ ├── gen │ │ └── assets.gen.dart │ ├── language_list.dart │ ├── navigation_bar_data.dart │ ├── quick_open_help_text.dart │ ├── reader_keyboard_shortcuts.dart │ └── urls.dart │ ├── features │ ├── about │ │ ├── data │ │ │ ├── about_repository.dart │ │ │ └── graphql │ │ │ │ └── query.graphql │ │ ├── domain │ │ │ ├── about │ │ │ │ ├── about_dto.dart │ │ │ │ └── graphql │ │ │ │ │ └── fragment.graphql │ │ │ └── server_update │ │ │ │ ├── graphql │ │ │ │ └── fragment.graphql │ │ │ │ └── server_update.dart │ │ └── presentation │ │ │ └── about │ │ │ ├── about_screen.dart │ │ │ ├── controllers │ │ │ └── about_controller.dart │ │ │ └── widget │ │ │ ├── app_update_dialog.dart │ │ │ ├── clipboard_list_tile.dart │ │ │ └── media_launch_button.dart │ ├── browse_center │ │ ├── data │ │ │ ├── extension_repository │ │ │ │ ├── extension_repository.dart │ │ │ │ └── graphql │ │ │ │ │ └── query.graphql │ │ │ └── source_repository │ │ │ │ ├── graphql │ │ │ │ └── query.graphql │ │ │ │ └── source_repository.dart │ │ ├── domain │ │ │ ├── extension │ │ │ │ ├── extension_model.dart │ │ │ │ └── graphql │ │ │ │ │ └── fragment.graphql │ │ │ ├── filter │ │ │ │ ├── filter_model.dart │ │ │ │ └── graphql │ │ │ │ │ └── fragment.graphql │ │ │ ├── language │ │ │ │ └── language_model.dart │ │ │ ├── manga_page │ │ │ │ ├── graphql │ │ │ │ │ └── fragment.graphql │ │ │ │ └── manga_page.dart │ │ │ ├── source │ │ │ │ ├── graphql │ │ │ │ │ └── fragment.graphql │ │ │ │ └── source_model.dart │ │ │ └── source_preference │ │ │ │ ├── graphql │ │ │ │ └── fragment.graphql │ │ │ │ └── source_preference.dart │ │ └── presentation │ │ │ ├── browse │ │ │ └── browse_screen.dart │ │ │ ├── extension │ │ │ ├── controller │ │ │ │ └── extension_controller.dart │ │ │ ├── extension_screen.dart │ │ │ └── widgets │ │ │ │ ├── extension_language_filter_dialog.dart │ │ │ │ ├── extension_list_tile.dart │ │ │ │ └── install_extension_file.dart │ │ │ ├── global_search │ │ │ ├── controller │ │ │ │ └── source_quick_search_controller.dart │ │ │ ├── global_search_screen.dart │ │ │ └── widgets │ │ │ │ └── source_short_search.dart │ │ │ ├── source │ │ │ ├── controller │ │ │ │ └── source_controller.dart │ │ │ ├── source_screen.dart │ │ │ └── widgets │ │ │ │ ├── source_language_filter.dart │ │ │ │ └── source_list_tile.dart │ │ │ ├── source_manga_list │ │ │ ├── controller │ │ │ │ └── source_manga_controller.dart │ │ │ ├── source_manga_list_screen.dart │ │ │ └── widgets │ │ │ │ ├── filter_to_widget.dart │ │ │ │ ├── source_manga_display_icon_popup.dart │ │ │ │ ├── source_manga_display_view.dart │ │ │ │ ├── source_manga_filter.dart │ │ │ │ ├── source_manga_grid_view.dart │ │ │ │ ├── source_manga_list_view.dart │ │ │ │ └── source_type_selectable_chip.dart │ │ │ └── source_preference │ │ │ ├── controller │ │ │ └── source_preference_controller.dart │ │ │ ├── source_preference_screen.dart │ │ │ └── widgets │ │ │ └── source_preference_to_widget.dart │ ├── library │ │ ├── data │ │ │ ├── category_repository.dart │ │ │ └── graphql │ │ │ │ └── query.graphql │ │ ├── domain │ │ │ └── category │ │ │ │ ├── category_model.dart │ │ │ │ └── graphql │ │ │ │ └── fragment.graphql │ │ └── presentation │ │ │ ├── category │ │ │ ├── controller │ │ │ │ └── edit_category_controller.dart │ │ │ ├── edit_category_screen.dart │ │ │ └── widgets │ │ │ │ ├── category_create_fab.dart │ │ │ │ ├── category_tile.dart │ │ │ │ └── edit_category_dialog.dart │ │ │ └── library │ │ │ ├── category_manga_list.dart │ │ │ ├── controller │ │ │ └── library_controller.dart │ │ │ ├── library_screen.dart │ │ │ └── widgets │ │ │ ├── library_manga_display.dart │ │ │ ├── library_manga_filter.dart │ │ │ ├── library_manga_organizer.dart │ │ │ └── library_manga_sort_tile.dart │ ├── manga_book │ │ ├── data │ │ │ ├── downloads │ │ │ │ ├── downloads_repository.dart │ │ │ │ └── graphql │ │ │ │ │ └── query.graphql │ │ │ ├── manga_book │ │ │ │ ├── manga_book_repository.dart │ │ │ │ └── query.graphql │ │ │ └── updates │ │ │ │ ├── graphql │ │ │ │ └── query.graphql │ │ │ │ └── updates_repository.dart │ │ ├── domain │ │ │ ├── chapter │ │ │ │ ├── chapter_model.dart │ │ │ │ └── graphql │ │ │ │ │ └── fragment.graphql │ │ │ ├── chapter_batch │ │ │ │ └── chapter_batch_model.dart │ │ │ ├── chapter_page │ │ │ │ ├── chapter_page_model.dart │ │ │ │ └── graphql │ │ │ │ │ └── fragment.graphql │ │ │ ├── chapter_patch │ │ │ │ └── chapter_put_model.dart │ │ │ ├── downloads │ │ │ │ ├── downloads_model.dart │ │ │ │ └── graphql │ │ │ │ │ └── fragment.graphql │ │ │ ├── downloads_queue │ │ │ │ ├── downloads_queue_model.dart │ │ │ │ └── graphql │ │ │ │ │ └── fragment.graphql │ │ │ ├── manga │ │ │ │ ├── graphql │ │ │ │ │ └── fragment.graphql │ │ │ │ └── manga_model.dart │ │ │ └── update_status │ │ │ │ ├── graphql │ │ │ │ └── fragment.graphql │ │ │ │ └── update_status_model.dart │ │ ├── presentation │ │ │ ├── downloads │ │ │ │ ├── controller │ │ │ │ │ └── downloads_controller.dart │ │ │ │ ├── downloads_screen.dart │ │ │ │ └── widgets │ │ │ │ │ ├── download_progress_list_tile.dart │ │ │ │ │ └── downloads_fab.dart │ │ │ ├── manga_details │ │ │ │ ├── controller │ │ │ │ │ └── manga_details_controller.dart │ │ │ │ ├── manga_details_screen.dart │ │ │ │ └── widgets │ │ │ │ │ ├── big_screen_manga_details.dart │ │ │ │ │ ├── chapter_list_tile.dart │ │ │ │ │ ├── edit_manga_category_dialog.dart │ │ │ │ │ ├── manga_chapter_filter.dart │ │ │ │ │ ├── manga_chapter_organizer.dart │ │ │ │ │ ├── manga_chapter_sort.dart │ │ │ │ │ ├── manga_chapter_sort_tile.dart │ │ │ │ │ ├── manga_description.dart │ │ │ │ │ └── small_screen_manga_details.dart │ │ │ ├── manga_thumbnail_viewer │ │ │ │ └── manga_thumbnail_viewer.dart │ │ │ ├── reader │ │ │ │ ├── controller │ │ │ │ │ └── reader_controller.dart │ │ │ │ ├── reader_screen.dart │ │ │ │ └── widgets │ │ │ │ │ ├── chapter_separator.dart │ │ │ │ │ ├── page_number_slider.dart │ │ │ │ │ ├── reader_mode │ │ │ │ │ ├── continuous_reader_mode.dart │ │ │ │ │ └── single_page_reader_mode.dart │ │ │ │ │ ├── reader_navigation_layout │ │ │ │ │ ├── layouts │ │ │ │ │ │ ├── edge_layout.dart │ │ │ │ │ │ ├── kindlish_layout.dart │ │ │ │ │ │ ├── l_shaped_layout.dart │ │ │ │ │ │ └── right_and_left_layout.dart │ │ │ │ │ └── reader_navigation_layout.dart │ │ │ │ │ └── reader_wrapper.dart │ │ │ └── updates │ │ │ │ ├── updates_screen.dart │ │ │ │ └── widgets │ │ │ │ └── chapter_manga_list_tile.dart │ │ └── widgets │ │ │ ├── chapter_actions │ │ │ ├── multi_chapters_action_icon.dart │ │ │ ├── multi_chapters_actions_bottom_app_bar.dart │ │ │ └── single_chapter_action_icon.dart │ │ │ ├── download_status_icon.dart │ │ │ ├── update_status_fab.dart │ │ │ ├── update_status_popup_menu.dart │ │ │ └── update_status_summary_sheet.dart │ ├── quick_open │ │ ├── domain │ │ │ └── quick_search_result.dart │ │ └── presentation │ │ │ ├── quick_search │ │ │ ├── controller │ │ │ │ └── quick_search_controller.dart │ │ │ ├── quick_search_screen.dart │ │ │ └── widgets │ │ │ │ ├── category_query_list_tile.dart │ │ │ │ ├── quick_query_result.dart │ │ │ │ ├── quick_search_bar.dart │ │ │ │ ├── search_result_to_widget.dart │ │ │ │ └── source_query_list_tile.dart │ │ │ └── search_stack │ │ │ └── search_stack_screen.dart │ └── settings │ │ ├── controller │ │ └── server_controller.dart │ │ ├── data │ │ ├── graphql │ │ │ └── query.graphql │ │ └── settings_repository.dart │ │ ├── domain │ │ └── settings │ │ │ ├── graphql │ │ │ └── fragment.graphql │ │ │ └── settings.dart │ │ ├── presentation │ │ ├── appearance │ │ │ ├── appearance_screen.dart │ │ │ └── widgets │ │ │ │ ├── app_theme_selector │ │ │ │ └── app_theme_selector.dart │ │ │ │ ├── grid_cover_width_slider │ │ │ │ └── grid_cover_width_slider.dart │ │ │ │ └── is_true_black │ │ │ │ └── is_true_black_tile.dart │ │ ├── backup │ │ │ ├── backup_screen.dart │ │ │ ├── controllers │ │ │ │ └── backup_controller.dart │ │ │ ├── data │ │ │ │ ├── backup_settings_repository.dart │ │ │ │ └── graphql │ │ │ │ │ └── query.graphql │ │ │ └── widgets │ │ │ │ ├── automatic_backup │ │ │ │ └── automatic_backup_section.dart │ │ │ │ └── backup_and_restore │ │ │ │ ├── backup_and_restore_section.dart │ │ │ │ └── widgets │ │ │ │ ├── backup_missing_dialog.dart │ │ │ │ ├── create_backup_dialog.dart │ │ │ │ └── restore_status_progress.dart │ │ ├── browse │ │ │ ├── browse_settings_screen.dart │ │ │ ├── data │ │ │ │ ├── browse_settings_repository.dart │ │ │ │ └── graphql │ │ │ │ │ └── query.graphql │ │ │ └── widgets │ │ │ │ ├── extension_repository │ │ │ │ └── extension_repository_screen.dart │ │ │ │ └── show_nsfw_switch │ │ │ │ └── show_nsfw_switch.dart │ │ ├── downloads │ │ │ ├── data │ │ │ │ ├── downloads_settings_repository.dart │ │ │ │ └── graphql │ │ │ │ │ └── query.graphql │ │ │ └── downloads_settings_screen.dart │ │ ├── general │ │ │ ├── general_screen.dart │ │ │ └── quick_search_toggle │ │ │ │ └── quick_search_toggle_tile.dart │ │ ├── library │ │ │ ├── data │ │ │ │ ├── graphql │ │ │ │ │ └── query.graphql │ │ │ │ └── library_settings_repository.dart │ │ │ ├── library_settings_screen.dart │ │ │ └── widgets │ │ │ │ ├── hide_empty_category │ │ │ │ └── hide_empty_category.dart │ │ │ │ └── skip_updating_entries_popup.dart │ │ ├── more │ │ │ └── more_screen.dart │ │ ├── reader │ │ │ ├── reader_settings_screen.dart │ │ │ └── widgets │ │ │ │ ├── reader_initial_overlay_tile │ │ │ │ └── reader_initial_overlay_tile.dart │ │ │ │ ├── reader_invert_tap_tile │ │ │ │ └── reader_invert_tap_tile.dart │ │ │ │ ├── reader_magnifier_size_slider │ │ │ │ └── reader_magnifier_size_slider.dart │ │ │ │ ├── reader_mode_tile │ │ │ │ └── reader_mode_tile.dart │ │ │ │ ├── reader_navigation_layout_tile │ │ │ │ └── reader_navigation_layout_tile.dart │ │ │ │ ├── reader_padding_slider │ │ │ │ └── reader_padding_slider.dart │ │ │ │ ├── reader_pinch_to_zoom │ │ │ │ └── reader_pinch_to_zoom.dart │ │ │ │ ├── reader_scroll_animation_tile │ │ │ │ └── reader_scroll_animation_tile.dart │ │ │ │ ├── reader_swipe_toggle_tile │ │ │ │ └── reader_swipe_chapter_toggle_tile.dart │ │ │ │ ├── reader_volume_tap_invert_tile │ │ │ │ └── reader_volume_tap_invert_tile.dart │ │ │ │ └── reader_volume_tap_tile │ │ │ │ └── reader_volume_tap_tile.dart │ │ ├── server │ │ │ ├── data │ │ │ │ ├── graphql │ │ │ │ │ └── query.graphql │ │ │ │ └── server_settings_repository.dart │ │ │ ├── server_screen.dart │ │ │ └── widget │ │ │ │ ├── authentication │ │ │ │ ├── auth_type │ │ │ │ │ └── auth_type_tile.dart │ │ │ │ └── authentication_section.dart │ │ │ │ ├── client │ │ │ │ ├── client_section.dart │ │ │ │ ├── server_port_tile │ │ │ │ │ └── server_port_tile.dart │ │ │ │ └── server_url_tile │ │ │ │ │ ├── server_search_button.dart │ │ │ │ │ └── server_url_tile.dart │ │ │ │ ├── cloud_flare │ │ │ │ └── cloud_flare_section.dart │ │ │ │ ├── credential_popup │ │ │ │ └── credentials_popup.dart │ │ │ │ ├── misc_settings │ │ │ │ └── misc_settings_section.dart │ │ │ │ ├── server_binding │ │ │ │ └── server_binding_section.dart │ │ │ │ └── socks_proxy │ │ │ │ └── socks_proxy_section.dart │ │ └── settings │ │ │ └── settings_screen.dart │ │ └── widgets │ │ ├── app_theme_mode_tile │ │ └── app_theme_mode_tile.dart │ │ └── slider_setting_tile │ │ └── slider_setting_tile.dart │ ├── global_providers │ └── global_providers.dart │ ├── graphql │ ├── domain │ │ └── page_info.dart │ ├── fragments.graphql │ └── schema.graphql │ ├── l10n │ ├── app_ar.arb │ ├── app_de.arb │ ├── app_en.arb │ ├── app_es.arb │ ├── app_fil.arb │ ├── app_fr.arb │ ├── app_id.arb │ ├── app_it.arb │ ├── app_ja.arb │ ├── app_ko.arb │ ├── app_nl.arb │ ├── app_pt.arb │ ├── app_pt_PT.arb │ ├── app_ru.arb │ ├── app_th.arb │ ├── app_tr.arb │ ├── app_uk.arb │ ├── app_vi.arb │ ├── app_zh.arb │ ├── app_zh_Hans.arb │ └── app_zh_Hant.arb │ ├── routes │ ├── router_config.dart │ └── sub_routes │ │ ├── browser_routes.dart │ │ ├── common_routes.dart │ │ ├── downloads_routes.dart │ │ ├── library_routes.dart │ │ ├── more_routes.dart │ │ └── updates_routes.dart │ ├── sorayomi.dart │ ├── utils │ ├── callbacks.dart │ ├── extensions │ │ ├── cache_manager_extensions.dart │ │ ├── custom_extensions.dart │ │ └── custom_extensions │ │ │ ├── async_value_extensions.dart │ │ │ ├── bool_extensions.dart │ │ │ ├── context_extensions.dart │ │ │ ├── date_time_extensions.dart │ │ │ ├── double_extensions.dart │ │ │ ├── generic_extensions.dart │ │ │ ├── graphql_extensions.dart │ │ │ ├── int_extensions.dart │ │ │ ├── iterable_extensions.dart │ │ │ ├── logger_extensions.dart │ │ │ ├── map_extensions.dart │ │ │ └── string_extensions.dart │ ├── freezed_converters │ │ └── language_json_converter.dart │ ├── hooks │ │ ├── paging_controller_hook.dart │ │ └── polling_hook.dart │ ├── launch_url_in_web.dart │ ├── logger │ │ ├── logger.dart │ │ ├── logger_link.dart │ │ └── provider_state_logger.dart │ ├── misc │ │ ├── app_utils.dart │ │ ├── file_picker_utils.dart │ │ ├── number_range_formatter.dart │ │ ├── scalars.dart │ │ └── toast │ │ │ └── toast.dart │ └── mixin │ │ ├── shared_preferences_client_mixin.dart │ │ └── state_provider_mixin.dart │ └── widgets │ ├── async_buttons │ ├── async_checkbox_list_tile.dart │ ├── async_elevated_button.dart │ ├── async_list_tile.dart │ ├── async_outline_button.dart │ ├── async_text_button.dart │ └── async_text_button_icon.dart │ ├── custom_checkbox_list_tile.dart │ ├── custom_circular_progress_indicator.dart │ ├── emoticons.dart │ ├── input_popup │ ├── domain │ │ └── settings_prop_type.dart │ ├── settings_prop_pop_up.dart │ ├── settings_prop_tile.dart │ └── widgets │ │ ├── number_picker_dialog.dart │ │ ├── number_slider_dialog.dart │ │ └── text_field_dialog.dart │ ├── manga_cover │ ├── grid │ │ └── manga_cover_grid_tile.dart │ ├── list │ │ ├── manga_cover_descriptive_list_tile.dart │ │ └── manga_cover_list_tile.dart │ ├── providers │ │ └── manga_cover_providers.dart │ └── widgets │ │ ├── manga_badges.dart │ │ └── manga_chips.dart │ ├── number_picker │ ├── number_picker.dart │ └── number_picker_with_label.dart │ ├── popup_widgets │ ├── multi_select_popup.dart │ ├── pop_button.dart │ ├── radio_list_popup.dart │ ├── slider_popup.dart │ └── text_field_popup.dart │ ├── search_field.dart │ ├── section_title.dart │ ├── server_image.dart │ ├── shell │ ├── big_screen_navigation_bar.dart │ ├── navigation_shell_screen.dart │ └── small_screen_navigation_bar.dart │ └── sort_list_tile.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements └── build │ └── .last_build_id ├── pubspec.lock ├── pubspec.yaml ├── scripts ├── debian │ ├── changelog │ ├── control │ ├── copyright │ ├── install │ ├── package.dirs │ ├── rules │ ├── source │ │ ├── format │ │ └── include-binaries │ └── tachidesk-sorayomi.links ├── tachidesk-sorayomi-arm64.wxs ├── tachidesk-sorayomi-x64.wxs └── tachidesk-sorayomi.desktop ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html ├── manifest.json └── splash │ ├── img │ ├── branding-1x.png │ ├── branding-2x.png │ ├── branding-3x.png │ ├── branding-4x.png │ ├── branding-dark-1x.png │ ├── branding-dark-2x.png │ ├── branding-dark-3x.png │ ├── branding-dark-4x.png │ ├── dark-1x.png │ ├── dark-2x.png │ ├── dark-3x.png │ ├── dark-4x.png │ ├── light-1x.png │ ├── light-2x.png │ ├── light-3x.png │ └── light-4x.png │ ├── splash.js │ └── style.css └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **PLEASE READ THIS** 11 | 12 | I acknowledge that: 13 | 14 | - I have updated to the latest version of the app. 15 | - I have tried the troubleshooting guide described in `README.md` 16 | - If this is a request for adding/changing an extension it should be brought up to Tachiyomi: https://github.com/tachiyomiorg/tachiyomi-extensions/issues/new/choose 17 | - If this is an issue with some extension not working properly, It does work in Tachiyomi application as intended. 18 | - I have searched the existing issues and this is a new ticket **NOT** a duplicate or related to another open issue 19 | - I will fill out the title and the information in this template 20 | 21 | Note that the issue will be automatically closed if you do not fill out the title or requested information. 22 | 23 | **DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT** 24 | 25 | --- 26 | 27 | **Is your feature request related to a problem? Please describe.** 28 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 29 | 30 | **Describe the solution you'd like** 31 | A clear and concise description of what you want to happen. 32 | 33 | **Describe alternatives you've considered** 34 | A clear and concise description of any alternative solutions or features you've considered. 35 | 36 | **Additional context** 37 | Add any other context or screenshots about the feature request here. 38 | -------------------------------------------------------------------------------- /.github/workflows/web.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Contributors to the Suwayomi project 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | name: gh-Pages 8 | 9 | on: 10 | workflow_dispatch: 11 | push: 12 | tags: 13 | - "*.*.*" 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 # Only works with v2 21 | - uses: subosito/flutter-action@v2 22 | with: 23 | channel: 'stable' 24 | 25 | # Recreating the project 26 | - run: flutter doctor -v 27 | - uses: actions/checkout@v3 28 | # - run: flutter create . 29 | - run: flutter pub get 30 | - run: flutter gen-l10n 31 | - run: dart run build_runner build --delete-conflicting-outputs 32 | 33 | - uses: bluefireteam/flutter-gh-pages@v9 34 | with: 35 | baseHref: /Tachidesk-Sorayomi/ 36 | webRenderer: canvaskit 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | lib/util_scripts 3 | sym_* 4 | *.class 5 | *.log 6 | *.pyc 7 | *.swp 8 | .DS_Store 9 | .atom/ 10 | .build/ 11 | .buildlog/ 12 | .history 13 | .svn/ 14 | .swiftpm/ 15 | migrate_working_dir/ 16 | 17 | # IntelliJ related 18 | *.iml 19 | *.ipr 20 | *.iws 21 | .idea/ 22 | 23 | # The .vscode folder contains launch configuration and tasks you configure in 24 | # VS Code which you may wish to be included in version control, so this line 25 | # is commented out by default. 26 | #.vscode/ 27 | 28 | # Flutter/Dart/Pub related 29 | **/doc/api/ 30 | **/ios/Flutter/.last_build_id 31 | .dart_tool/ 32 | .flutter-plugins 33 | .flutter-plugins-dependencies 34 | .packages 35 | .pub-cache/ 36 | .pub/ 37 | /build/ 38 | 39 | # Symbolication related 40 | app.*.symbols 41 | 42 | # Obfuscation related 43 | app.*.map.json 44 | 45 | # Android Studio will place build artifacts here 46 | /android/app/debug 47 | /android/app/profile 48 | /android/app/release 49 | 50 | 51 | *.g.dart 52 | *.freezed.dart 53 | *.graphql.dart 54 | **/__generated__ 55 | lib/src/l10n/generated 56 | -------------------------------------------------------------------------------- /.sdkmanrc: -------------------------------------------------------------------------------- 1 | # Enable auto-env through the sdkman_auto_env config 2 | # Add key=value pairs of SDKs to use below 3 | java=17.0.13-zulu 4 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "tachidesk_sorayomi", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "tachidesk_sorayomi (profile mode)", 14 | "request": "launch", 15 | "type": "dart", 16 | "flutterMode": "profile" 17 | }, 18 | { 19 | "name": "tachidesk_sorayomi (release mode)", 20 | "request": "launch", 21 | "type": "dart", 22 | "flutterMode": "release" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "behaviour", 4 | "canonicalized", 5 | "Compat", 6 | "dattatreya", 7 | "Gdefault", 8 | "Gupdate", 9 | "Gvalues", 10 | "horiz", 11 | "keydown", 12 | "kindlish", 13 | "localsourcelang", 14 | "Mangas", 15 | "metas", 16 | "microtask", 17 | "phonelink", 18 | "proto", 19 | "pubspec", 20 | "readmore", 21 | "reddy", 22 | "Scanlator", 23 | "scanlators", 24 | "Solverr", 25 | "sublist", 26 | "suwayomi", 27 | "tachidesk", 28 | "Tachiyomi", 29 | "tekartik", 30 | "vals", 31 | "Webtoon", 32 | "webtoons", 33 | "WEBUI" 34 | ], 35 | "licenser.license": "MPLv2", 36 | "licenser.projectName": "Tachidesk-Sorayomi", 37 | "licenser.author": "Contributors to the Suwayomi project", 38 | "editor.wordSeparators": "`~!@#%^&*()-=+[{]}\\|;:'\",.<>/?", 39 | "files.exclude": { 40 | // "**/__generated__": true, 41 | // "**/*.graphql.dart": true, 42 | // "**/*.freezed.dart": true, 43 | "**/*.g.dart": true, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /.vscode/sorayomi.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | // Place your sorayomi workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and 3 | // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope 4 | // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is 5 | // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: 6 | // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. 7 | // Placeholders with the same ids are connected. 8 | // Example: 9 | "Basic Dart Graphql Query Request": { 10 | "scope": "dart", 11 | "prefix": "req", 12 | "body": [ 13 | "static G${1:query}Req ${1/^(.)(.*)$/${1:/downcase}${2}/}() => G${1:query}Req();" 14 | ], 15 | "description": "Creates graphql query", 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /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/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/suwayomi/tachidesk_sorayomi/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.suwayomi.tachidesk_sorayomi 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | import dev.darttools.flutter_android_volume_keydown.FlutterAndroidVolumeKeydownActivity; 6 | 7 | class MainActivity: FlutterAndroidVolumeKeydownActivity() { 8 | } 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-hdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/branding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-hdpi/branding.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-mdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/branding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-mdpi/branding.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-hdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-mdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-xhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-night/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/branding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xhdpi/branding.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/branding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xxhdpi/branding.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/android12splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xxxhdpi/android12splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/branding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xxxhdpi/branding.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | } 19 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 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.12.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /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 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | } 19 | 20 | plugins { 21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 22 | id "com.android.application" version "8.8.0" apply false 23 | id "org.jetbrains.kotlin.android" version "2.1.10" apply false 24 | id "com.google.gms.google-services" version "4.4.0" apply false 25 | id "com.google.firebase.crashlytics" version "2.9.9" apply false 26 | } 27 | 28 | include ":app" 29 | -------------------------------------------------------------------------------- /assets/icons/dark_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/dark_icon.png -------------------------------------------------------------------------------- /assets/icons/launcher/from_suwayomi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/launcher/from_suwayomi.png -------------------------------------------------------------------------------- /assets/icons/launcher/ios_sorayomi_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/launcher/ios_sorayomi_icon.png -------------------------------------------------------------------------------- /assets/icons/launcher/sorayomi_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/launcher/sorayomi_icon.ico -------------------------------------------------------------------------------- /assets/icons/launcher/sorayomi_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/launcher/sorayomi_icon.png -------------------------------------------------------------------------------- /assets/icons/launcher/sorayomi_preview_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/launcher/sorayomi_preview_icon.png -------------------------------------------------------------------------------- /assets/icons/light_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/light_icon.png -------------------------------------------------------------------------------- /assets/icons/previous_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/assets/icons/previous_done.png -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | explicit_to_json: true 7 | graphql_codegen: 8 | options: 9 | addTypenameExcludedPaths: 10 | - subscription 11 | outputDirectory: __generated__ 12 | clients: 13 | - graphql 14 | - graphql_flutter 15 | scalars: 16 | ISODateTime: 17 | type: DateTime 18 | JSON: 19 | type: Map 20 | Upload: 21 | type: MultipartFile 22 | fromJsonFunctionName: fileFromJson 23 | toJsonFunctionName: fileToJson 24 | import: package:tachidesk_sorayomi/src/utils/misc/scalars.dart 25 | LongString: 26 | type: String 27 | fromJsonFunctionName: longStringFromJson 28 | toJsonFunctionName: longStringToJson 29 | import: package:tachidesk_sorayomi/src/utils/misc/scalars.dart 30 | Cursor: 31 | type: int 32 | fromJsonFunctionName: cursorFromJson 33 | toJsonFunctionName: cursorToJson 34 | import: package:tachidesk_sorayomi/src/utils/misc/scalars.dart 35 | -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /graphql.config.yml: -------------------------------------------------------------------------------- 1 | schema: "lib/src/graphql/schema.graphql" 2 | documents: "lib/**/*.{graphql,js,ts,jsx,tsx}" 3 | -------------------------------------------------------------------------------- /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 | 11.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /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 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /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/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/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/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/BrandingImage.imageset/BrandingImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/BrandingImage.imageset/BrandingImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/BrandingImage.imageset/BrandingImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/BrandingImage.imageset/BrandingImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/BrandingImage.imageset/BrandingImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/BrandingImage.imageset/BrandingImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/BrandingImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "BrandingImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "BrandingImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "BrandingImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "dark" 12 | } 13 | ], 14 | "filename" : "darkbackground.png", 15 | "idiom" : "universal" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "LaunchImageDark.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "LaunchImage@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "LaunchImageDark@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "LaunchImage@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "LaunchImageDark@3x.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@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/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /l10n.yaml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023 Contributors to the Suwayomi project 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | synthetic-package: false 8 | arb-dir: lib/src/l10n 9 | template-arb-file: app_en.arb 10 | output-localization-file: app_localizations.dart 11 | output-dir: lib/src/l10n/generated 12 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/services.dart'; 9 | import 'package:go_router/go_router.dart'; 10 | import 'package:graphql_flutter/graphql_flutter.dart'; 11 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 12 | import 'package:package_info_plus/package_info_plus.dart'; 13 | import 'package:shared_preferences/shared_preferences.dart'; 14 | 15 | import 'src/features/about/presentation/about/controllers/about_controller.dart'; 16 | import 'src/global_providers/global_providers.dart'; 17 | import 'src/sorayomi.dart'; 18 | 19 | Future main() async { 20 | WidgetsFlutterBinding.ensureInitialized(); 21 | final packageInfo = await PackageInfo.fromPlatform(); 22 | final sharedPreferences = await SharedPreferences.getInstance(); 23 | await initHiveForFlutter(); 24 | 25 | SystemChrome.setPreferredOrientations(DeviceOrientation.values); 26 | GoRouter.optionURLReflectsImperativeAPIs = true; 27 | runApp( 28 | ProviderScope( 29 | overrides: [ 30 | packageInfoProvider.overrideWithValue(packageInfo), 31 | sharedPreferencesProvider.overrideWithValue(sharedPreferences), 32 | hiveStoreProvider.overrideWithValue(HiveStore()) 33 | ], 34 | child: const Sorayomi(), 35 | ), 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/abstracts/locale_enum.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'value_enum.dart'; 4 | 5 | abstract interface class LocaleEnum implements ValueEnum { 6 | LocaleEnum(this.value); 7 | 8 | @override 9 | final String value; 10 | 11 | String toLocale(BuildContext context); 12 | } 13 | -------------------------------------------------------------------------------- /lib/src/abstracts/value_enum.dart: -------------------------------------------------------------------------------- 1 | abstract interface class ValueEnum implements Enum { 2 | ValueEnum(this.value); 3 | 4 | final String value; 5 | } 6 | -------------------------------------------------------------------------------- /lib/src/constants/app_constants.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | const Duration kDuration = Duration(milliseconds: 500); 10 | const Duration kInstantDuration = Duration(microseconds: 1); 11 | const Duration kLongDuration = Duration(seconds: 2); 12 | 13 | const Curve kCurve = Curves.easeIn; 14 | const Size kMagnifierSize = Size(77.37, 37.9); 15 | const MagnifierDecoration kMagnifierDecoration = MagnifierDecoration( 16 | shape: RoundedRectangleBorder( 17 | borderRadius: BorderRadius.all(Radius.circular(40))), 18 | shadows: [ 19 | BoxShadow( 20 | blurRadius: 1.5, 21 | offset: Offset(0, 2), 22 | spreadRadius: 0.75, 23 | color: Color.fromARGB(25, 0, 0, 0)) 24 | ], 25 | ); 26 | 27 | const kDebounceDuration = Duration(milliseconds: 500); 28 | 29 | const kPositionPlaceholder = -1; 30 | -------------------------------------------------------------------------------- /lib/src/constants/navigation_bar_data.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../utils/extensions/custom_extensions.dart'; 10 | 11 | class NavigationBarData { 12 | final String Function(BuildContext context) label; 13 | final IconData icon; 14 | final IconData activeIcon; 15 | static final navList = [ 16 | NavigationBarData( 17 | icon: Icons.collections_bookmark_outlined, 18 | activeIcon: Icons.collections_bookmark_rounded, 19 | label: (context) => context.l10n.library, 20 | ), 21 | NavigationBarData( 22 | icon: Icons.new_releases_outlined, 23 | activeIcon: Icons.new_releases_rounded, 24 | label: (context) => context.l10n.updates, 25 | ), 26 | NavigationBarData( 27 | icon: Icons.explore_outlined, 28 | activeIcon: Icons.explore_rounded, 29 | label: (context) => context.l10n.browse, 30 | ), 31 | NavigationBarData( 32 | icon: Icons.download_outlined, 33 | activeIcon: Icons.download_rounded, 34 | label: (context) => context.l10n.downloads, 35 | ), 36 | NavigationBarData( 37 | icon: Icons.more_horiz_outlined, 38 | activeIcon: Icons.more_horiz_rounded, 39 | label: (context) => context.l10n.more, 40 | ), 41 | ]; 42 | 43 | NavigationBarData({ 44 | required this.label, 45 | required this.icon, 46 | required this.activeIcon, 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/constants/quick_open_help_text.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../features/quick_open/domain/quick_search_result.dart'; 10 | import '../utils/extensions/custom_extensions.dart'; 11 | 12 | List getQuickShowHintTextList(BuildContext context) { 13 | return [ 14 | QuickSearchResult.helpText( 15 | prefill: '@S', 16 | pattern: '@', 17 | hintText: context.l10n.quickSearchSource, 18 | ), 19 | QuickSearchResult.helpText( 20 | prefill: '@S/M', 21 | pattern: '@/', 22 | hintText: context.l10n.quickSearchSourceManga, 23 | ), 24 | QuickSearchResult.helpText( 25 | prefill: '#C', 26 | pattern: '#', 27 | hintText: context.l10n.quickSearchCategory, 28 | ), 29 | QuickSearchResult.helpText( 30 | prefill: '#C/M', 31 | pattern: '#/', 32 | hintText: context.l10n.quickSearchCategoryManga, 33 | ), 34 | QuickSearchResult.helpText( 35 | prefill: '#C/M:CN', 36 | pattern: '#/:', 37 | hintText: context.l10n.quickSearchCategoryMangaChapter, 38 | ), 39 | QuickSearchResult.helpText( 40 | prefill: 'X', 41 | hintText: context.l10n.quickSearchContext, 42 | ), 43 | ]; 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/constants/urls.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | enum AppUrls { 8 | sorayomiGithubUrl(url: "https://github.com/Suwayomi/Tachidesk-Sorayomi"), 9 | sorayomiLatestReleaseUrl( 10 | url: "https://github.com/Suwayomi/Tachidesk-Sorayomi/releases/latest"), 11 | tachideskHelp(url: "https://github.com/Suwayomi/suwayomi-Server/wiki"), 12 | tachideskReddit(url: "https://www.reddit.com/r/Tachidesk"), 13 | sorayomiWhatsNew( 14 | url: "https://github.com/Suwayomi/Tachidesk-Sorayomi/releases/tag/"), 15 | sorayomiLatestReleaseApiUrl( 16 | url: 17 | "https://api.github.com/repos/Suwayomi/Tachidesk-Sorayomi/releases/latest", 18 | ), 19 | flareSolverr( 20 | url: 21 | "https://github.com/FlareSolverr/FlareSolverr?tab=readme-ov-file#installation"); 22 | 23 | const AppUrls({required this.url}); 24 | 25 | final String url; 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/features/about/data/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | query GetServerUpdate { 5 | checkForServerUpdates { 6 | ...ServerUpdateDto 7 | } 8 | } 9 | 10 | query GetAbout { 11 | aboutServer { 12 | ...AboutDto 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/features/about/domain/about/about_dto.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'graphql/__generated__/fragment.graphql.dart'; 8 | 9 | typedef AboutDto = Fragment$AboutDto; 10 | -------------------------------------------------------------------------------- /lib/src/features/about/domain/about/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment AboutDto on AboutServerPayload { 2 | buildTime 3 | buildType 4 | discord 5 | github 6 | name 7 | revision 8 | version 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/features/about/domain/server_update/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | 2 | fragment ServerUpdateDto on CheckForServerUpdatesPayload { 3 | channel 4 | tag 5 | url 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/features/about/domain/server_update/server_update.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:freezed_annotation/freezed_annotation.dart'; 8 | 9 | import 'graphql/__generated__/fragment.graphql.dart'; 10 | 11 | part 'server_update.freezed.dart'; 12 | 13 | @freezed 14 | class ServerUpdate with _$ServerUpdate { 15 | factory ServerUpdate({ 16 | String? channel, 17 | String? tag, 18 | String? url, 19 | }) = _ServerUpdate; 20 | } 21 | 22 | extension ServerUpdateConverter on Fragment$ServerUpdateDto { 23 | ServerUpdate get toDto => ServerUpdate(channel: channel, tag: tag, url: url); 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/features/about/presentation/about/controllers/about_controller.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'dart:async'; 8 | 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | import 'package:package_info_plus/package_info_plus.dart'; 11 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 12 | 13 | import '../../../data/about_repository.dart'; 14 | import '../../../domain/about/about_dto.dart'; 15 | 16 | part 'about_controller.g.dart'; 17 | 18 | @riverpod 19 | Future about(Ref ref) => 20 | ref.watch(aboutRepositoryProvider).getAbout(); 21 | 22 | @riverpod 23 | PackageInfo packageInfo(ref) => throw UnimplementedError(); 24 | -------------------------------------------------------------------------------- /lib/src/features/about/presentation/about/widget/clipboard_list_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/services.dart'; 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | 11 | import '../../../../../utils/extensions/custom_extensions.dart'; 12 | import '../../../../../utils/misc/toast/toast.dart'; 13 | 14 | class ClipboardListTile extends ConsumerWidget { 15 | const ClipboardListTile({ 16 | super.key, 17 | required this.title, 18 | required this.value, 19 | }); 20 | final String title; 21 | final String? value; 22 | @override 23 | Widget build(BuildContext context, WidgetRef ref) => ListTile( 24 | title: Text(title), 25 | subtitle: value.isNotBlank ? Text(value!) : null, 26 | onTap: value.isNotBlank 27 | ? () { 28 | final msg = "$title: $value"; 29 | Clipboard.setData(ClipboardData(text: msg)); 30 | ref 31 | .read(toastProvider) 32 | ?.show(context.l10n.copyMsg(msg), instantShow: true); 33 | } 34 | : null, 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/features/about/presentation/about/widget/media_launch_button.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../../../../../utils/extensions/custom_extensions.dart'; 10 | import '../../../../../utils/launch_url_in_web.dart'; 11 | import '../../../../../utils/misc/toast/toast.dart'; 12 | 13 | class MediaLaunchButton extends StatelessWidget { 14 | const MediaLaunchButton({ 15 | super.key, 16 | required this.toast, 17 | required this.title, 18 | required this.iconData, 19 | required this.url, 20 | }); 21 | 22 | final Toast? toast; 23 | final String title; 24 | final IconData iconData; 25 | final String url; 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return url.isNotBlank 30 | ? context.isSmallTabletOrLess 31 | ? IconButton( 32 | tooltip: title, 33 | onPressed: () => launchUrlInWeb(context, url, toast), 34 | icon: Icon(iconData), 35 | ) 36 | : TextButton.icon( 37 | label: Text(title), 38 | onPressed: () => launchUrlInWeb(context, url, toast), 39 | icon: Icon(iconData), 40 | ) 41 | : const SizedBox.shrink(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/data/extension_repository/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | mutation UpdateExtension($id: String!, $install: Boolean = false, $uninstall: Boolean = false, $update: Boolean = false) { 4 | updateExtension( 5 | input: {id: $id, patch: {install: $install, uninstall: $uninstall, update: $update}} 6 | ) { 7 | clientMutationId 8 | extension { 9 | ...ExtensionDto 10 | } 11 | } 12 | } 13 | 14 | mutation InstallExternalExtension($extensionFile: Upload!) { 15 | installExternalExtension(input: {extensionFile: $extensionFile}) { 16 | clientMutationId 17 | extension { 18 | ...ExtensionDto 19 | } 20 | } 21 | } 22 | 23 | mutation FetchExtensionList { 24 | fetchExtensions(input: {}) { 25 | clientMutationId 26 | extensions { 27 | ...ExtensionDto 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/data/source_repository/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | query SourceList { 2 | sources { 3 | nodes { 4 | ...SourceDto 5 | } 6 | } 7 | } 8 | 9 | query SourceFilterById($id: LongString!) { 10 | source(id: $id) { 11 | filters { 12 | ...FilterDto 13 | } 14 | } 15 | } 16 | 17 | query SourceById($id: LongString!) { 18 | source(id: $id) { 19 | ...SourceDto 20 | } 21 | } 22 | 23 | mutation FetchSourceManga($input: FetchSourceMangaInput!) { 24 | fetchSourceManga(input: $input) { 25 | ...SourceMangaPage 26 | } 27 | } 28 | 29 | query SourcePreferenceById($id: LongString!) { 30 | source(id: $id) { 31 | preferences { 32 | ...SourcePreference 33 | } 34 | } 35 | } 36 | 37 | mutation UpdateSourcePreference($input: UpdateSourcePreferenceInput!) { 38 | updateSourcePreference(input: $input) { 39 | __typename 40 | preferences { 41 | ...SourcePreference 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/extension/extension_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import '../../../../utils/freezed_converters/language_json_converter.dart'; 8 | import '../language/language_model.dart'; 9 | import 'graphql/__generated__/fragment.graphql.dart'; 10 | 11 | typedef Extension = Fragment$ExtensionDto; 12 | 13 | extension ExtensionExtensions on Extension { 14 | Language? get language => LanguageJsonConverter.fromJson(lang); 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/extension/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | 2 | fragment ExtensionDto on ExtensionType { 3 | apkName 4 | hasUpdate 5 | iconUrl 6 | isInstalled 7 | isNsfw 8 | isObsolete 9 | lang 10 | name 11 | pkgName 12 | repo 13 | versionCode 14 | versionName 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/filter/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | 2 | fragment PrimitiveFilterDto on Filter { 3 | __typename 4 | ... on CheckBoxFilter { 5 | __typename 6 | checkBoxState: default 7 | name 8 | } 9 | ... on HeaderFilter { 10 | __typename 11 | name 12 | } 13 | ... on SelectFilter { 14 | __typename 15 | selectState: default 16 | name 17 | displayValues: values 18 | } 19 | ... on TriStateFilter { 20 | __typename 21 | tristate: default 22 | name 23 | } 24 | ... on TextFilter { 25 | __typename 26 | textState: default 27 | name 28 | } 29 | ... on SortFilter { 30 | __typename 31 | sortState: default { 32 | ...SortSelectionDto 33 | } 34 | name 35 | displayValues: values 36 | } 37 | ... on SeparatorFilter { 38 | __typename 39 | name 40 | } 41 | } 42 | fragment FilterDto on Filter { 43 | ...PrimitiveFilterDto 44 | ... on GroupFilter { 45 | __typename 46 | name 47 | groupState: filters { 48 | ...PrimitiveFilterDto 49 | } 50 | } 51 | } 52 | 53 | fragment SortSelectionDto on SortSelection { 54 | ascending 55 | index 56 | } 57 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/language/language_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:freezed_annotation/freezed_annotation.dart'; 8 | 9 | part 'language_model.freezed.dart'; 10 | part 'language_model.g.dart'; 11 | 12 | @freezed 13 | class Language with _$Language { 14 | Language._(); 15 | factory Language({ 16 | String? code, 17 | String? name, 18 | String? nativeName, 19 | }) = _Language; 20 | 21 | String? get displayName => nativeName ?? name ?? code; 22 | 23 | factory Language.fromJson(Map json) => 24 | _$LanguageFromJson(json); 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/manga_page/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment SourceMangaPage on FetchSourceMangaPayload { 2 | hasNextPage 3 | mangas { 4 | ...MangaDto 5 | } 6 | } 7 | 8 | fragment MangaPageDto on MangaNodeList { 9 | nodes { 10 | ...MangaDto 11 | } 12 | pageInfo { 13 | ...PageInfoDto 14 | } 15 | totalCount 16 | } 17 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/manga_page/manga_page.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'graphql/__generated__/fragment.graphql.dart'; 8 | 9 | typedef MangaPage = Fragment$SourceMangaPage; 10 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/source/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment SourceDto on SourceType { 2 | displayName 3 | iconUrl 4 | id 5 | isConfigurable 6 | isNsfw 7 | lang 8 | name 9 | supportsLatest 10 | extension { 11 | pkgName 12 | repo 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/source_preference/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment SourcePreference on Preference { 2 | __typename 3 | ... on CheckBoxPreference { 4 | checkBoxValue: currentValue 5 | summary 6 | checkBoxDefaultValue: default 7 | key 8 | checkBoxTitle: title 9 | } 10 | ... on EditTextPreference { 11 | editTextValue: currentValue 12 | EditTextDefaultValue: default 13 | editTextTitle: title 14 | text 15 | summary 16 | key 17 | dialogTitle 18 | dialogMessage 19 | } 20 | ... on SwitchPreference { 21 | switchValue: currentValue 22 | summary 23 | key 24 | switchDefaultValue: default 25 | switchTitle: title 26 | } 27 | ... on MultiSelectListPreference { 28 | dialogMessage 29 | dialogTitle 30 | multiSelectTitle: title 31 | summary 32 | key 33 | entryValues 34 | entries 35 | multiSelectDefaultValue: default 36 | multiSelectValue:currentValue 37 | } 38 | ... on ListPreference { 39 | listValue:currentValue 40 | listDefaultValue: default 41 | listTitle: title 42 | summary 43 | key 44 | entryValues 45 | entries 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/domain/source_preference/source_preference.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import '../../../../graphql/__generated__/schema.graphql.dart'; 8 | import 'graphql/__generated__/fragment.graphql.dart'; 9 | 10 | typedef SourcePreference = Fragment$SourcePreference; 11 | 12 | typedef CheckBoxPreference = Fragment$SourcePreference$$CheckBoxPreference; 13 | 14 | typedef SwitchPreferenceCompat = Fragment$SourcePreference$$SwitchPreference; 15 | 16 | typedef ListPreference = Fragment$SourcePreference$$ListPreference; 17 | 18 | typedef MultiSelectListPreference 19 | = Fragment$SourcePreference$$MultiSelectListPreference; 20 | 21 | typedef EditTextPreference = Fragment$SourcePreference$$EditTextPreference; 22 | 23 | typedef SourcePreferenceChange = Input$SourcePreferenceChangeInput; 24 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/presentation/source_manga_list/controller/source_manga_controller.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 9 | 10 | import '../../../../../constants/db_keys.dart'; 11 | import '../../../../../constants/enum.dart'; 12 | import '../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 13 | import '../../../data/source_repository/source_repository.dart'; 14 | import '../../../domain/filter/filter_model.dart'; 15 | import '../../../domain/source/source_model.dart'; 16 | 17 | part 'source_manga_controller.g.dart'; 18 | 19 | @riverpod 20 | Future source(Ref ref, String sourceId) => 21 | ref.watch(sourceRepositoryProvider).getSource(sourceId); 22 | 23 | @riverpod 24 | Future?> baseSourceMangaFilterList(Ref ref, String sourceId) => 25 | ref.read(sourceRepositoryProvider).getSourceFilter(sourceId); 26 | 27 | @riverpod 28 | class SourceDisplayMode extends _$SourceDisplayMode 29 | with SharedPreferenceEnumClientMixin { 30 | @override 31 | DisplayMode? build() => initialize( 32 | DBKeys.sourceDisplayMode, 33 | enumList: DisplayMode.sourceDisplayList, 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/presentation/source_manga_list/widgets/source_type_selectable_chip.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../../../../../constants/app_sizes.dart'; 10 | import '../../../../../utils/extensions/custom_extensions.dart'; 11 | import '../../../domain/source/source_model.dart'; 12 | 13 | class SourceTypeSelectableChip extends StatelessWidget { 14 | const SourceTypeSelectableChip({ 15 | super.key, 16 | required this.value, 17 | required this.groupValue, 18 | required this.onSelected, 19 | }); 20 | final SourceType value; 21 | final SourceType groupValue; 22 | final ValueChanged onSelected; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | final selected = value == groupValue; 27 | return Padding( 28 | padding: KEdgeInsets.h4v8.size, 29 | child: ChoiceChip( 30 | label: Text(value.toLocale(context)), 31 | selected: selected, 32 | avatar: selected 33 | ? null 34 | : Icon( 35 | selected ? value.selectedIcon : value.icon, 36 | color: context.theme.colorScheme.onSurface 37 | .withValues(alpha: selected ? .5 : 1), 38 | ), 39 | onSelected: onSelected, 40 | ), 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/src/features/browse_center/presentation/source_preference/controller/source_preference_controller.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 9 | 10 | import '../../../data/source_repository/source_repository.dart'; 11 | import '../../../domain/source_preference/source_preference.dart'; 12 | 13 | part 'source_preference_controller.g.dart'; 14 | 15 | @riverpod 16 | Future?> baseSourcePreferenceList( 17 | Ref ref, String sourceId) => 18 | ref.read(sourceRepositoryProvider).getSourcePreference(sourceId); 19 | -------------------------------------------------------------------------------- /lib/src/features/library/data/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | query AllCategories($condition: CategoryConditionInput, $filter: CategoryFilterInput, $first: Int, $offset: Int) { 5 | categories( 6 | condition: $condition 7 | filter: $filter 8 | first: $first 9 | orderBy: ORDER 10 | orderByType: ASC 11 | offset: $offset 12 | ) { 13 | nodes { 14 | ...CategoryDto 15 | } 16 | pageInfo { 17 | ...PageInfoDto 18 | } 19 | totalCount 20 | } 21 | } 22 | 23 | mutation CreateCategory($input: CreateCategoryInput!) { 24 | createCategory(input: $input) { 25 | __typename 26 | } 27 | } 28 | 29 | mutation UpdateCategory($input: UpdateCategoryInput!) { 30 | updateCategory(input: $input) { 31 | __typename 32 | } 33 | } 34 | 35 | mutation DeleteCategory($input: DeleteCategoryInput!) { 36 | deleteCategory(input: $input) { 37 | __typename 38 | } 39 | } 40 | 41 | mutation UpdateCategoryOrder($input: UpdateCategoryOrderInput!) { 42 | updateCategoryOrder(input: $input) { 43 | categories { 44 | ...CategoryDto 45 | } 46 | } 47 | } 48 | 49 | query GetCategoryMangas($id: Int!) { 50 | category(id: $id) { 51 | id 52 | mangas { 53 | nodes { 54 | ...MangaDto 55 | __typename 56 | } 57 | pageInfo { 58 | ...PageInfoDto 59 | __typename 60 | } 61 | totalCount 62 | __typename 63 | } 64 | __typename 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/features/library/domain/category/category_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | import '../../../../graphql/__generated__/schema.graphql.dart'; 7 | import './graphql/__generated__/fragment.graphql.dart'; 8 | 9 | typedef CategoryDto = Fragment$CategoryDto; 10 | 11 | typedef CategoryCreate = Input$CreateCategoryInput; 12 | 13 | typedef CategoryUpdate = Input$UpdateCategoryPatchInput; 14 | -------------------------------------------------------------------------------- /lib/src/features/library/domain/category/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | 2 | fragment CategoryDto on CategoryType { 3 | defaultCategory : default 4 | id 5 | includeInDownload 6 | includeInUpdate 7 | name 8 | order 9 | mangas { 10 | totalCount 11 | } 12 | meta { 13 | key 14 | value 15 | } 16 | } 17 | 18 | fragment CategoryPageDto on CategoryNodeList { 19 | nodes { 20 | ...CategoryDto 21 | } 22 | pageInfo { 23 | ...PageInfoDto 24 | } 25 | totalCount 26 | } 27 | -------------------------------------------------------------------------------- /lib/src/features/library/presentation/library/widgets/library_manga_filter.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | import '../../../../../utils/extensions/custom_extensions.dart'; 11 | import '../../../../../widgets/custom_checkbox_list_tile.dart'; 12 | import '../controller/library_controller.dart'; 13 | 14 | class LibraryMangaFilter extends ConsumerWidget { 15 | const LibraryMangaFilter({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context, WidgetRef ref) { 19 | return ListView( 20 | children: [ 21 | CustomCheckboxListTile( 22 | title: context.l10n.unread, 23 | provider: libraryMangaFilterUnreadProvider, 24 | onChanged: ref.read(libraryMangaFilterUnreadProvider.notifier).update, 25 | ), 26 | CustomCheckboxListTile( 27 | title: context.l10n.completed, 28 | provider: libraryMangaFilterCompletedProvider, 29 | onChanged: 30 | ref.read(libraryMangaFilterCompletedProvider.notifier).update, 31 | ), 32 | CustomCheckboxListTile( 33 | title: context.l10n.downloaded, 34 | provider: libraryMangaFilterDownloadedProvider, 35 | onChanged: 36 | ref.read(libraryMangaFilterDownloadedProvider.notifier).update, 37 | ), 38 | ], 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/features/library/presentation/library/widgets/library_manga_organizer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../../../../../constants/enum.dart'; 10 | import '../../../../../utils/extensions/custom_extensions.dart'; 11 | import 'library_manga_display.dart'; 12 | import 'library_manga_filter.dart'; 13 | import 'library_manga_sort_tile.dart'; 14 | 15 | class LibraryMangaOrganizer extends StatelessWidget { 16 | const LibraryMangaOrganizer({ 17 | super.key, 18 | /* required this.controller */ 19 | }); 20 | // final ScrollController controller; 21 | @override 22 | Widget build(BuildContext context) { 23 | return DefaultTabController( 24 | length: 3, 25 | child: Scaffold( 26 | appBar: TabBar( 27 | tabs: [ 28 | Tab(text: context.l10n.filter), 29 | Tab(text: context.l10n.sort), 30 | Tab(text: context.l10n.display), 31 | ], 32 | ), 33 | body: TabBarView( 34 | children: [ 35 | const LibraryMangaFilter(), 36 | ListView( 37 | children: [ 38 | for (MangaSort sortType in MangaSort.values) 39 | LibraryMangaSortTile(sortType: sortType), 40 | ], 41 | ), 42 | const LibraryMangaDisplay(), 43 | ], 44 | ), 45 | ), 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/src/features/library/presentation/library/widgets/library_manga_sort_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | import '../../../../../constants/enum.dart'; 11 | import '../../../../../utils/extensions/custom_extensions.dart'; 12 | import '../../../../../widgets/sort_list_tile.dart'; 13 | import '../controller/library_controller.dart'; 14 | 15 | class LibraryMangaSortTile extends ConsumerWidget { 16 | const LibraryMangaSortTile({ 17 | super.key, 18 | required this.sortType, 19 | }); 20 | final MangaSort sortType; 21 | @override 22 | Widget build(BuildContext context, WidgetRef ref) { 23 | final sortedBy = ref.watch(libraryMangaSortProvider); 24 | final sortedDirection = ref.watch(libraryMangaSortDirectionProvider); 25 | return SortListTile( 26 | selected: sortType == sortedBy, 27 | title: Text(sortType.toLocale(context)), 28 | ascending: sortedDirection.ifNull(true), 29 | onChanged: (bool? value) => ref 30 | .read(libraryMangaSortDirectionProvider.notifier) 31 | .update(!(sortedDirection.ifNull())), 32 | onSelected: () => 33 | ref.read(libraryMangaSortProvider.notifier).update(sortType), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/data/downloads/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | mutation EnqueueChapterDownloads($input: EnqueueChapterDownloadsInput!) { 4 | enqueueChapterDownloads(input: $input) { 5 | __typename 6 | } 7 | } 8 | 9 | mutation DequeueChapterDownloads($input: DequeueChapterDownloadInput!) { 10 | dequeueChapterDownload(input: $input) { 11 | __typename 12 | } 13 | } 14 | 15 | mutation StopDownloader($input: StopDownloaderInput!) { 16 | stopDownloader(input: $input) { 17 | __typename 18 | } 19 | } 20 | mutation StartDownloader($input: StartDownloaderInput!) { 21 | startDownloader(input: $input) { 22 | __typename 23 | } 24 | } 25 | 26 | mutation ClearDownloader($input: ClearDownloaderInput!) { 27 | clearDownloader(input: $input) { 28 | __typename 29 | } 30 | } 31 | 32 | mutation ReorderChapterDownload($input: ReorderChapterDownloadInput!) { 33 | reorderChapterDownload(input: $input) { 34 | __typename 35 | downloadStatus { 36 | ...DownloadStatusDto 37 | } 38 | } 39 | } 40 | 41 | subscription DownloadStatusChanged($input: DownloadChangedInput!) { 42 | downloadStatusChanged(input: $input) { 43 | ...DownloadUpdatesDto 44 | } 45 | } 46 | 47 | query GetDownloadStatus { 48 | downloadStatus { 49 | ...DownloadStatusDto 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/data/updates/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | query GetChapterWithMangaPage($after: Cursor, $before: Cursor, $condition: ChapterConditionInput, $filter: ChapterFilterInput, $first: Int, $last: Int, $offset: Int, $order: [ChapterOrderInput!]) { 2 | chapters( 3 | after: $after 4 | before: $before 5 | condition: $condition 6 | filter: $filter 7 | first: $first 8 | last: $last 9 | offset: $offset 10 | order: $order 11 | ) { 12 | ...ChapterPageWithMangaDto 13 | } 14 | } 15 | 16 | 17 | mutation UpdateLibraryMangas($input: UpdateLibraryMangaInput!) { 18 | updateLibraryManga(input: $input) { 19 | updateStatus { 20 | ...UpdateStatusBaseDto 21 | } 22 | } 23 | } 24 | 25 | mutation UpdateCategoryMangas($input: UpdateCategoryMangaInput!) { 26 | updateCategoryManga(input: $input) { 27 | updateStatus { 28 | ...UpdateStatusBaseDto 29 | } 30 | } 31 | } 32 | 33 | mutation StopCategoryUpdate($input: UpdateStopInput!) { 34 | updateStop(input: $input) { 35 | __typename 36 | } 37 | } 38 | 39 | query UpdateStatusDto { 40 | updateStatus { 41 | ...UpdateStatusDto 42 | } 43 | } 44 | 45 | subscription UpdateStatusChange { 46 | updateStatusChanged { 47 | ...UpdateStatusDto 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/chapter/chapter_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import '../../../../utils/extensions/custom_extensions.dart'; 8 | import 'graphql/__generated__/fragment.graphql.dart'; 9 | 10 | typedef ChapterDto = Fragment$ChapterDto; 11 | 12 | typedef ChapterWithMangaDto = Fragment$ChapterWithMangaDto; 13 | 14 | extension ChapterExtension on Fragment$ChapterDto { 15 | bool query([String? query]) { 16 | return name.query(query) || index == int.tryParse(query ?? ''); 17 | } 18 | 19 | int get index => sourceOrder; 20 | 21 | Map get metaData => 22 | {for (final metaItem in meta) metaItem.key: metaItem.value}; 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/chapter/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment ChapterDto on ChapterType { 2 | chapterNumber 3 | fetchedAt 4 | id 5 | isBookmarked 6 | isDownloaded 7 | isRead 8 | lastPageRead 9 | lastReadAt 10 | mangaId 11 | name 12 | pageCount 13 | realUrl 14 | scanlator 15 | sourceOrder 16 | uploadDate 17 | url 18 | meta { 19 | key 20 | value 21 | } 22 | } 23 | fragment ChapterWithMangaDto on ChapterType { 24 | ...ChapterDto, 25 | manga { 26 | ...MangaBaseDto, 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/chapter_batch/chapter_batch_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import '../../../../graphql/__generated__/schema.graphql.dart'; 8 | 9 | typedef ChapterBatch = Input$UpdateChaptersInput; 10 | 11 | typedef ChapterChange = Input$UpdateChapterPatchInput; 12 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/chapter_page/chapter_page_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import './graphql/__generated__/fragment.graphql.dart'; 8 | 9 | typedef ChapterPageDto = Fragment$ChapterPageDto; 10 | 11 | typedef ChapterPageWithMangaDto = Fragment$ChapterPageWithMangaDto; 12 | 13 | typedef ChapterPagesDto = Fragment$ChapterPagesDto; 14 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/chapter_page/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment ChapterPageDto on ChapterNodeList { 2 | nodes { 3 | ...ChapterDto 4 | } 5 | pageInfo { 6 | ...PageInfoDto 7 | } 8 | totalCount 9 | } 10 | 11 | fragment ChapterPageWithMangaDto on ChapterNodeList { 12 | nodes { 13 | ...ChapterWithMangaDto 14 | } 15 | pageInfo { 16 | ...PageInfoDto 17 | } 18 | totalCount 19 | } 20 | 21 | fragment ChapterPagesDto on FetchChapterPagesPayload { 22 | chapter { 23 | id 24 | pageCount 25 | __typename 26 | } 27 | pages 28 | __typename 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/chapter_patch/chapter_put_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:freezed_annotation/freezed_annotation.dart'; 8 | 9 | part 'chapter_put_model.freezed.dart'; 10 | part 'chapter_put_model.g.dart'; 11 | 12 | @freezed 13 | class ChapterPut with _$ChapterPut { 14 | factory ChapterPut({ 15 | bool? read, 16 | bool? bookmarked, 17 | bool? markPrevRead, 18 | int? lastPageRead, 19 | }) = _ChapterPut; 20 | 21 | factory ChapterPut.fromJson(Map json) => 22 | _$ChapterPutFromJson(json); 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/downloads/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment DownloadDto on DownloadType { 2 | chapter { 3 | id 4 | name 5 | sourceOrder 6 | isDownloaded 7 | } 8 | 9 | manga { 10 | id 11 | title 12 | downloadCount 13 | thumbnailUrl 14 | } 15 | progress 16 | state 17 | tries 18 | position 19 | } 20 | 21 | fragment DownloadUpdateDto on DownloadUpdate { 22 | type 23 | download { 24 | ...DownloadDto 25 | } 26 | } 27 | 28 | fragment DownloadUpdatesDto on DownloadUpdates { 29 | state 30 | omittedUpdates 31 | updates { 32 | ...DownloadUpdateDto 33 | } 34 | initial { 35 | ...DownloadDto 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/downloads_queue/downloads_queue_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import './graphql/__generated__/fragment.graphql.dart'; 8 | 9 | typedef DownloadStatusDto = Fragment$DownloadStatusDto; 10 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/downloads_queue/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | fragment DownloadStatusDto on DownloadStatus { 4 | state 5 | queue { 6 | ...DownloadDto 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/manga/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | fragment MangaDto on MangaType { 2 | age 3 | artist 4 | author 5 | chaptersAge 6 | chaptersLastFetchedAt 7 | description 8 | downloadCount 9 | genre 10 | id 11 | inLibrary 12 | inLibraryAt 13 | initialized 14 | lastFetchedAt 15 | lastReadChapter { 16 | ...ChapterDto 17 | } 18 | latestFetchedChapter { 19 | ...ChapterDto 20 | } 21 | latestReadChapter { 22 | ...ChapterDto 23 | } 24 | latestUploadedChapter { 25 | ...ChapterDto 26 | } 27 | meta { 28 | key 29 | value 30 | } 31 | realUrl 32 | source { 33 | ...SourceDto 34 | } 35 | sourceId 36 | status 37 | thumbnailUrl 38 | thumbnailUrlLastFetched 39 | title 40 | unreadCount 41 | updateStrategy 42 | url 43 | } 44 | 45 | fragment MangaMetaDto on MangaMetaType { 46 | key, 47 | value, 48 | mangaId, 49 | } 50 | 51 | 52 | fragment MangaBaseDto on MangaType { 53 | age 54 | artist 55 | author 56 | chaptersLastFetchedAt 57 | description 58 | genre 59 | id 60 | inLibrary 61 | inLibraryAt 62 | initialized 63 | lastFetchedAt 64 | meta { 65 | key 66 | value 67 | } 68 | realUrl 69 | sourceId 70 | status 71 | thumbnailUrl 72 | thumbnailUrlLastFetched 73 | title 74 | unreadCount 75 | updateStrategy 76 | url 77 | } 78 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/update_status/graphql/fragment.graphql: -------------------------------------------------------------------------------- 1 | 2 | fragment UpdateStatusBaseDto on UpdateStatus { 3 | isRunning 4 | } 5 | fragment UpdateStatusJobDto on UpdateStatusType { 6 | mangas { 7 | ...MangaPageDto 8 | } 9 | } 10 | 11 | fragment UpdateStatusDto on UpdateStatus { 12 | ...UpdateStatusBaseDto 13 | completeJobs { 14 | ...UpdateStatusJobDto 15 | } 16 | failedJobs { 17 | ...UpdateStatusJobDto 18 | } 19 | pendingJobs { 20 | ...UpdateStatusJobDto 21 | } 22 | runningJobs { 23 | ...UpdateStatusJobDto 24 | } 25 | skippedCategories { 26 | categories { 27 | ...CategoryPageDto 28 | } 29 | } 30 | skippedJobs { 31 | ...UpdateStatusJobDto 32 | } 33 | updatingCategories { 34 | categories { 35 | ...CategoryPageDto 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/domain/update_status/update_status_model.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import '../../../../utils/extensions/custom_extensions.dart'; 8 | import '../manga/manga_model.dart'; 9 | import 'graphql/__generated__/fragment.graphql.dart'; 10 | 11 | typedef UpdateStatusDto = Fragment$UpdateStatusDto; 12 | 13 | extension UpdateStatusExt on UpdateStatusDto { 14 | int get total => 15 | (pendingJobs.mangas.totalCount.toInt()).getValueOnNullOrNegative() + 16 | (runningJobs.mangas.totalCount.toInt()).getValueOnNullOrNegative() + 17 | (completeJobs.mangas.totalCount.toInt()).getValueOnNullOrNegative() + 18 | (failedJobs.mangas.totalCount.toInt()).getValueOnNullOrNegative(); 19 | 20 | int get updateChecked => 21 | (completeJobs.mangas.totalCount.toInt()).getValueOnNullOrNegative() + 22 | (failedJobs.mangas.totalCount.toInt()).getValueOnNullOrNegative(); 23 | 24 | bool get isUpdateCheckCompleted => total == updateChecked; 25 | 26 | bool get isUpdateChecking => 27 | (total).isGreaterThan(0) && !(isUpdateCheckCompleted); 28 | } 29 | 30 | extension U on Fragment$UpdateStatusJobDto { 31 | bool get isNotBlank => mangas.nodes.isNotBlank; 32 | 33 | List get mangaList => mangas.nodes; 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/presentation/manga_details/widgets/manga_chapter_organizer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../../../../../utils/extensions/custom_extensions.dart'; 10 | import 'manga_chapter_filter.dart'; 11 | import 'manga_chapter_sort.dart'; 12 | 13 | class MangaChapterOrganizer extends StatelessWidget { 14 | const MangaChapterOrganizer({super.key, required this.mangaId}); 15 | final int mangaId; 16 | @override 17 | Widget build(BuildContext context) { 18 | return DefaultTabController( 19 | length: 2, 20 | child: Scaffold( 21 | appBar: TabBar( 22 | tabs: [ 23 | Tab(text: context.l10n.filter), 24 | Tab(text: context.l10n.sort), 25 | ], 26 | ), 27 | body: TabBarView( 28 | children: [ 29 | MangaChapterFilter(mangaId: mangaId), 30 | const MangaChapterSort(), 31 | ], 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/presentation/manga_details/widgets/manga_chapter_sort.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | 11 | import '../../../../../constants/enum.dart'; 12 | import 'manga_chapter_sort_tile.dart'; 13 | 14 | class MangaChapterSort extends ConsumerWidget { 15 | const MangaChapterSort({super.key}); 16 | 17 | @override 18 | Widget build(BuildContext context, WidgetRef ref) { 19 | return ListView( 20 | children: [ 21 | const Divider(height: .5), 22 | for (ChapterSort chapterSort in ChapterSort.values) 23 | MangaChapterSortTile(sortType: chapterSort), 24 | ], 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/presentation/manga_details/widgets/manga_chapter_sort_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | import '../../../../../constants/enum.dart'; 11 | import '../../../../../utils/extensions/custom_extensions.dart'; 12 | import '../../../../../widgets/sort_list_tile.dart'; 13 | import '../controller/manga_details_controller.dart'; 14 | 15 | class MangaChapterSortTile extends ConsumerWidget { 16 | const MangaChapterSortTile({ 17 | super.key, 18 | required this.sortType, 19 | }); 20 | final ChapterSort sortType; 21 | @override 22 | Widget build(BuildContext context, WidgetRef ref) { 23 | final sortedBy = ref.watch(mangaChapterSortProvider); 24 | final sortedDirection = ref.watch(mangaChapterSortDirectionProvider); 25 | return SortListTile( 26 | selected: sortType == sortedBy, 27 | title: Text(sortType.toLocale(context)), 28 | ascending: sortedDirection.ifNull(true), 29 | onChanged: (bool? value) => ref 30 | .read(mangaChapterSortDirectionProvider.notifier) 31 | .update(!(sortedDirection.ifNull())), 32 | onSelected: () => 33 | ref.read(mangaChapterSortProvider.notifier).update(sortType), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/presentation/manga_thumbnail_viewer/manga_thumbnail_viewer.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'dart:ui'; 8 | 9 | import 'package:flutter/material.dart'; 10 | import 'package:go_router/go_router.dart'; 11 | 12 | import '../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../widgets/server_image.dart'; 14 | 15 | class MangaThumbnailViewer extends StatelessWidget { 16 | const MangaThumbnailViewer({ 17 | super.key, 18 | required this.imageUrl, 19 | }); 20 | final String imageUrl; 21 | @override 22 | Widget build(BuildContext context) { 23 | return GestureDetector( 24 | onTap: () => context.pop(), 25 | child: Scaffold( 26 | appBar: AppBar(backgroundColor: Colors.transparent), 27 | backgroundColor: Colors.transparent, 28 | body: BackdropFilter( 29 | filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0), 30 | child: Container( 31 | decoration: BoxDecoration( 32 | color: context.colorScheme.surface.withValues(alpha: 0.1), 33 | ), 34 | child: InteractiveViewer( 35 | maxScale: 4, 36 | child: Center( 37 | child: ServerImage(imageUrl: imageUrl), 38 | ), 39 | ), 40 | ), 41 | ), 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/presentation/reader/controller/reader_controller.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 9 | 10 | import '../../../data/manga_book/manga_book_repository.dart'; 11 | import '../../../domain/chapter/chapter_model.dart'; 12 | import '../../../domain/chapter_page/chapter_page_model.dart'; 13 | 14 | part 'reader_controller.g.dart'; 15 | 16 | @riverpod 17 | FutureOr chapter( 18 | Ref ref, { 19 | required int chapterId, 20 | }) => 21 | ref.watch(mangaBookRepositoryProvider).getChapter(chapterId: chapterId); 22 | 23 | @riverpod 24 | Future chapterPages(Ref ref, {required int chapterId}) => ref 25 | .watch(mangaBookRepositoryProvider) 26 | .getChapterPages(chapterId: chapterId); 27 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/presentation/reader/widgets/reader_navigation_layout/layouts/right_and_left_layout.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | class RightAndLeftLayout extends StatelessWidget { 10 | const RightAndLeftLayout({ 11 | super.key, 12 | this.onLeftTap, 13 | this.onRightTap, 14 | this.leftColor, 15 | this.rightColor, 16 | }); 17 | final VoidCallback? onLeftTap; 18 | final VoidCallback? onRightTap; 19 | final Color? leftColor; 20 | final Color? rightColor; 21 | @override 22 | Widget build(BuildContext context) { 23 | return Row( 24 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 25 | children: [ 26 | Expanded( 27 | child: GestureDetector( 28 | behavior: HitTestBehavior.translucent, 29 | onTap: onLeftTap, 30 | child: Container(color: leftColor), 31 | ), 32 | ), 33 | const Expanded(child: SizedBox.expand()), 34 | Expanded( 35 | child: GestureDetector( 36 | behavior: HitTestBehavior.translucent, 37 | onTap: onRightTap, 38 | child: Container(color: rightColor), 39 | ), 40 | ), 41 | ], 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/features/manga_book/widgets/update_status_fab.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | import '../../../routes/router_config.dart'; 11 | import '../../../utils/extensions/custom_extensions.dart'; 12 | import '../data/updates/updates_repository.dart'; 13 | import '../domain/update_status/update_status_model.dart'; 14 | 15 | class UpdateStatusFab extends ConsumerWidget { 16 | const UpdateStatusFab({super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context, WidgetRef ref) { 20 | final updateStatus = ref.watch(updatesSocketProvider); 21 | final showStatus = (updateStatus.valueOrNull?.isUpdateChecking).ifNull(); 22 | return FloatingActionButton.extended( 23 | icon: showStatus ? null : const Icon(Icons.refresh_rounded), 24 | onPressed: () => showStatus 25 | ? const UpdateStatusRoute().push(context) 26 | : ref.read(updatesRepositoryProvider).fetchUpdates(), 27 | label: showStatus 28 | ? Text("${updateStatus.valueOrNull?.updateChecked.padLeft()}" 29 | "/${updateStatus.valueOrNull?.total.padLeft()}") 30 | : Text(context.l10n.update), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/src/features/quick_open/presentation/quick_search/widgets/quick_query_result.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | import '../../../../../utils/extensions/custom_extensions.dart'; 11 | import '../controller/quick_search_controller.dart'; 12 | import 'search_result_to_widget.dart'; 13 | 14 | class QuickQueryResult extends ConsumerWidget { 15 | const QuickQueryResult({ 16 | super.key, 17 | required this.afterClick, 18 | this.controller, 19 | }); 20 | final VoidCallback afterClick; 21 | final TextEditingController? controller; 22 | @override 23 | Widget build(BuildContext context, WidgetRef ref) { 24 | final results = ref.watch(processesQuickSearchProvider(context: context)); 25 | if (results.isBlank) return const SizedBox.shrink(); 26 | return Card( 27 | clipBehavior: Clip.hardEdge, 28 | child: SingleChildScrollView( 29 | child: Column( 30 | mainAxisSize: MainAxisSize.min, 31 | children: results! 32 | .map( 33 | (e) => SearchResultToWidget( 34 | result: e, 35 | afterClick: afterClick, 36 | controller: controller, 37 | ), 38 | ) 39 | .toList(), 40 | ), 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/features/settings/controller/server_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../../../utils/extensions/custom_extensions.dart'; 4 | import '../data/settings_repository.dart'; 5 | import '../domain/settings/settings.dart'; 6 | 7 | part 'server_controller.g.dart'; 8 | 9 | @riverpod 10 | class Settings extends _$Settings { 11 | @override 12 | Future build() => 13 | ref.watch(settingsRepositoryProvider).getServerSettings(); 14 | 15 | void updateState(SettingsDto value) => 16 | state = state.copyWithData((_) => value); 17 | } 18 | -------------------------------------------------------------------------------- /lib/src/features/settings/data/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | query ServerSettings { 4 | settings { 5 | ...SettingsDto 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /lib/src/features/settings/data/settings_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:graphql/client.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | import '../../../global_providers/global_providers.dart'; 6 | import '../../../utils/extensions/custom_extensions.dart'; 7 | import '../domain/settings/settings.dart'; 8 | import './graphql/__generated__/query.graphql.dart'; 9 | 10 | part 'settings_repository.g.dart'; 11 | 12 | class SettingsRepository { 13 | const SettingsRepository(this.ferryClient); 14 | 15 | final GraphQLClient ferryClient; 16 | 17 | Future getServerSettings() => ferryClient 18 | .query$ServerSettings(Options$Query$ServerSettings()) 19 | .getData((data) => data.settings); 20 | } 21 | 22 | @riverpod 23 | SettingsRepository settingsRepository(Ref ref) => 24 | SettingsRepository(ref.watch(graphQlClientProvider)); 25 | -------------------------------------------------------------------------------- /lib/src/features/settings/domain/settings/settings.dart: -------------------------------------------------------------------------------- 1 | import '../../../../graphql/__generated__/schema.graphql.dart'; 2 | import 'graphql/__generated__/fragment.graphql.dart'; 3 | 4 | typedef SettingsDto = Fragment$SettingsDto; 5 | typedef AutomaticBackupSettingsDto = Fragment$AutomaticBackupSettingsDto; 6 | typedef BrowserSettingsDto = Fragment$BrowserSettingsDto; 7 | typedef CloudFlareBypassDto = Fragment$CloudFlareBypassDto; 8 | typedef DownloadsSettingsDto = Fragment$DownloadsSettingsDto; 9 | typedef LibrarySettingsDto = Fragment$LibrarySettingsDto; 10 | typedef MiscSettingsDto = Fragment$MiscSettingsDto; 11 | typedef RestoreStatusDto = Fragment$RestoreStatusDto; 12 | typedef ServerBindingDto = Fragment$ServerBindingDto; 13 | typedef SocksProxyDto = Fragment$SocksProxyDto; 14 | typedef RestoreState = Enum$BackupRestoreState; 15 | 16 | extension RestoreStateExtension on RestoreState { 17 | bool get isFinalState => 18 | this == RestoreState.SUCCESS || this == RestoreState.FAILURE; 19 | } 20 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/appearance/appearance_screen.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | import '../../../../utils/extensions/custom_extensions.dart'; 11 | import '../../widgets/app_theme_mode_tile/app_theme_mode_tile.dart'; 12 | import 'widgets/app_theme_selector/app_theme_selector.dart'; 13 | import 'widgets/grid_cover_width_slider/grid_cover_width_slider.dart'; 14 | import 'widgets/is_true_black/is_true_black_tile.dart'; 15 | 16 | class AppearanceScreen extends ConsumerWidget { 17 | const AppearanceScreen({super.key}); 18 | 19 | @override 20 | Widget build(context, ref) { 21 | final themeMode = ref.watch(appThemeModeProvider); 22 | return Scaffold( 23 | appBar: AppBar(title: Text(context.l10n.appearance)), 24 | body: ListView( 25 | children: [ 26 | const AppThemeModeTile(), 27 | if (themeMode != ThemeMode.light) const IsTrueBlackTile(), 28 | const AppThemeSelector(), 29 | const GridCoverWidthSlider(), 30 | ], 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/appearance/widgets/is_true_black/is_true_black_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../../constants/db_keys.dart'; 12 | import '../../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'is_true_black_tile.g.dart'; 16 | 17 | @riverpod 18 | class IsTrueBlack extends _$IsTrueBlack with SharedPreferenceClientMixin { 19 | @override 20 | bool? build() => initialize(DBKeys.isTrueBlack); 21 | } 22 | 23 | class IsTrueBlackTile extends HookConsumerWidget { 24 | const IsTrueBlackTile({super.key}); 25 | @override 26 | Widget build(BuildContext context, WidgetRef ref) { 27 | return SwitchListTile( 28 | controlAffinity: ListTileControlAffinity.trailing, 29 | secondary: const Icon(Icons.circle_rounded, color: Colors.black), 30 | title: Text(context.l10n.isTrueBlack), 31 | onChanged: ref.read(isTrueBlackProvider.notifier).update, 32 | value: ref.watch(isTrueBlackProvider).ifNull(), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/backup/backup_screen.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | import '../../../../utils/extensions/custom_extensions.dart'; 11 | import '../../controller/server_controller.dart'; 12 | import 'widgets/automatic_backup/automatic_backup_section.dart'; 13 | import 'widgets/backup_and_restore/backup_and_restore_section.dart'; 14 | 15 | class BackupScreen extends ConsumerWidget { 16 | const BackupScreen({super.key}); 17 | 18 | @override 19 | Widget build(context, ref) { 20 | return Scaffold( 21 | appBar: AppBar(title: Text(context.l10n.backup)), 22 | body: RefreshIndicator( 23 | onRefresh: () => ref.refresh(settingsProvider.future), 24 | child: ListTileTheme( 25 | data: const ListTileThemeData( 26 | subtitleTextStyle: TextStyle(color: Colors.grey), 27 | ), 28 | child: ListView( 29 | children: const [ 30 | BackupAndRestoreSection(), 31 | AutomaticBackupSection(), 32 | ], 33 | ), 34 | ), 35 | ), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/backup/controllers/backup_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../../domain/settings/settings.dart'; 5 | import '../data/backup_settings_repository.dart'; 6 | 7 | part 'backup_controller.g.dart'; 8 | 9 | @riverpod 10 | Future restoreStatus(Ref ref, String restoreId) => 11 | ref.watch(backupSettingsRepositoryProvider).getRestoreStatus(restoreId); 12 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/backup/data/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | mutation UpdateBackupTTL($backupTTL: Int = 14) { 3 | setSettings(input: {settings: {backupTTL: $backupTTL}}) { 4 | settings { 5 | ...SettingsDto 6 | } 7 | } 8 | } 9 | 10 | mutation UpdateBackupTime($backupTime: String = "12:00") { 11 | setSettings(input: {settings: {backupTime: $backupTime}}) { 12 | settings { 13 | ...SettingsDto 14 | } 15 | } 16 | } 17 | 18 | mutation UpdateBackupPath($backupPath: String!) { 19 | setSettings(input: {settings: {backupPath: $backupPath}}) { 20 | settings { 21 | ...SettingsDto 22 | } 23 | } 24 | } 25 | 26 | mutation UpdateBackupInterval($backupInterval: Int = 1) { 27 | setSettings(input: {settings: {backupInterval: $backupInterval}}) { 28 | settings { 29 | ...SettingsDto 30 | } 31 | } 32 | } 33 | 34 | query RestoreStatus($restoreId: String!) { 35 | restoreStatus(id: $restoreId) { 36 | ...RestoreStatusDto 37 | } 38 | } 39 | 40 | mutation RestoreBackup($backup: Upload!) { 41 | restoreBackup(input: { backup: $backup }) { 42 | clientMutationId 43 | id 44 | status { 45 | ...RestoreStatusDto 46 | } 47 | } 48 | } 49 | 50 | mutation CreateBackup($includeCategories: Boolean = true, $includeChapters: Boolean = true) { 51 | createBackup( 52 | input: {includeCategories: $includeCategories, includeChapters: $includeChapters} 53 | ) { 54 | clientMutationId 55 | url 56 | } 57 | } 58 | 59 | query ValidateBackup($backup: Upload!) { 60 | validateBackup(input: { backup: $backup }) { 61 | missingSources { 62 | name 63 | id 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/browse/data/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | mutation UpdateSourceInParallel($maxSourcesInParallel: Int = 6) { 4 | setSettings(input: {settings: {maxSourcesInParallel: $maxSourcesInParallel}}) { 5 | settings { 6 | ...SettingsDto 7 | } 8 | } 9 | } 10 | 11 | mutation UpdateLocalSourcePath($localSourcePath: String!) { 12 | setSettings(input: {settings: {localSourcePath: $localSourcePath}}) { 13 | settings { 14 | ...SettingsDto 15 | } 16 | } 17 | } 18 | 19 | mutation UpdateExtensionRepos($extensionRepos: [String!]!) { 20 | setSettings(input: {settings: {extensionRepos: $extensionRepos}}) { 21 | settings { 22 | ...SettingsDto 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/browse/widgets/show_nsfw_switch/show_nsfw_switch.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../../constants/db_keys.dart'; 12 | import '../../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'show_nsfw_switch.g.dart'; 16 | 17 | @riverpod 18 | class ShowNSFW extends _$ShowNSFW with SharedPreferenceClientMixin { 19 | @override 20 | bool? build() => initialize(DBKeys.showNSFW); 21 | } 22 | 23 | class ShowNSFWTile extends ConsumerWidget { 24 | const ShowNSFWTile({super.key}); 25 | @override 26 | Widget build(BuildContext context, WidgetRef ref) { 27 | return SwitchListTile( 28 | controlAffinity: ListTileControlAffinity.trailing, 29 | secondary: const Icon(Icons.eighteen_up_rating_rounded), 30 | title: Text(context.l10n.nsfw), 31 | onChanged: ref.read(showNSFWProvider.notifier).update, 32 | value: ref.watch(showNSFWProvider).ifNull(), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/downloads/data/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | mutation UpdateDownloadsLocation($downloadsPath: String!) { 4 | setSettings(input: {settings: {downloadsPath: $downloadsPath}}) { 5 | settings { 6 | ...SettingsDto 7 | } 8 | } 9 | } 10 | 11 | mutation UpdateDownloadAsCbz($downloadAsCbz: Boolean = false) { 12 | setSettings(input: {settings: {downloadAsCbz: $downloadAsCbz}}) { 13 | settings { 14 | ...SettingsDto 15 | } 16 | } 17 | } 18 | 19 | mutation UpdateAutoDownloadNewChaptersLimit($autoDownloadNewChaptersLimit: Int = 3) { 20 | setSettings(input: {settings: {autoDownloadNewChaptersLimit: $autoDownloadNewChaptersLimit}}) { 21 | settings { 22 | ...SettingsDto 23 | } 24 | } 25 | } 26 | 27 | mutation ToggleExcludeEntryWithUnreadChapters($excludeEntryWithUnreadChapters: Boolean = false) { 28 | setSettings(input: {settings: {excludeEntryWithUnreadChapters: $excludeEntryWithUnreadChapters}}) { 29 | settings { 30 | ...SettingsDto 31 | } 32 | } 33 | } 34 | 35 | mutation ToggleAutoDownloadNewChapters($autoDownloadNewChapters: Boolean = false) { 36 | setSettings(input: {settings: {autoDownloadNewChapters: $autoDownloadNewChapters}}) { 37 | settings { 38 | ...SettingsDto 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/general/quick_search_toggle/quick_search_toggle_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../constants/db_keys.dart'; 12 | import '../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'quick_search_toggle_tile.g.dart'; 16 | 17 | @riverpod 18 | class QuickSearchToggle extends _$QuickSearchToggle 19 | with SharedPreferenceClientMixin { 20 | @override 21 | bool? build() => initialize(DBKeys.quickSearchToggle); 22 | } 23 | 24 | class QuickSearchToggleTile extends HookConsumerWidget { 25 | const QuickSearchToggleTile({super.key}); 26 | @override 27 | Widget build(BuildContext context, WidgetRef ref) { 28 | return SwitchListTile( 29 | controlAffinity: ListTileControlAffinity.trailing, 30 | secondary: const Icon(Icons.search_rounded), 31 | title: Text(context.l10n.quickSearch), 32 | onChanged: ref.read(quickSearchToggleProvider.notifier).update, 33 | value: ref.watch(quickSearchToggleProvider).ifNull(), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/library/data/graphql/query.graphql: -------------------------------------------------------------------------------- 1 | 2 | 3 | mutation UpdateMangaMetaData($updateMangas: Boolean = false) { 4 | setSettings(input: {settings: {updateMangas: $updateMangas}}) { 5 | settings { 6 | ...SettingsDto 7 | } 8 | } 9 | } 10 | 11 | mutation UpdateGlobalUpdateInterval($globalUpdateInterval: Float = 12) { 12 | setSettings(input: {settings: {globalUpdateInterval: $globalUpdateInterval}}) { 13 | settings { 14 | ...SettingsDto 15 | } 16 | } 17 | } 18 | 19 | mutation ToggleExcludeNotStarted($excludeNotStarted: Boolean = false) { 20 | setSettings(input: {settings: {excludeNotStarted: $excludeNotStarted}}) { 21 | settings { 22 | ...SettingsDto 23 | } 24 | } 25 | } 26 | 27 | mutation ToggleExcludeUnreadChapters($excludeUnreadChapters: Boolean = false) { 28 | setSettings(input: {settings: {excludeUnreadChapters: $excludeUnreadChapters}}) { 29 | settings { 30 | ...SettingsDto 31 | } 32 | } 33 | } 34 | 35 | mutation ToggleExcludeCompleted($excludeCompleted: Boolean = false) { 36 | setSettings(input: {settings: {excludeCompleted: $excludeCompleted}}) { 37 | settings { 38 | ...SettingsDto 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/library/widgets/hide_empty_category/hide_empty_category.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../../constants/db_keys.dart'; 12 | import '../../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'hide_empty_category.g.dart'; 16 | 17 | @riverpod 18 | class HideEmptyCategory extends _$HideEmptyCategory 19 | with SharedPreferenceClientMixin { 20 | @override 21 | bool? build() => initialize(DBKeys.hideEmptyCategory); 22 | } 23 | 24 | class HideEmptyCategoryTile extends HookConsumerWidget { 25 | const HideEmptyCategoryTile({super.key}); 26 | 27 | @override 28 | Widget build(BuildContext context, WidgetRef ref) { 29 | return SwitchListTile( 30 | controlAffinity: ListTileControlAffinity.trailing, 31 | secondary: const Icon(Icons.category_rounded), 32 | title: Text(context.l10n.hideEmptyCategory), 33 | onChanged: ref.read(hideEmptyCategoryProvider.notifier).update, 34 | value: ref.watch(hideEmptyCategoryProvider).ifNull(), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/reader/widgets/reader_initial_overlay_tile/reader_initial_overlay_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../../constants/db_keys.dart'; 12 | import '../../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'reader_initial_overlay_tile.g.dart'; 16 | 17 | @riverpod 18 | class ReaderInitialOverlay extends _$ReaderInitialOverlay 19 | with SharedPreferenceClientMixin { 20 | @override 21 | bool? build() => initialize(DBKeys.readerOverlay); 22 | } 23 | 24 | class ReaderInitialOverlayTile extends HookConsumerWidget { 25 | const ReaderInitialOverlayTile({super.key}); 26 | @override 27 | Widget build(BuildContext context, WidgetRef ref) { 28 | return SwitchListTile( 29 | controlAffinity: ListTileControlAffinity.trailing, 30 | secondary: const Icon(Icons.layers_outlined), 31 | title: Text(context.l10n.readerOverlay), 32 | subtitle: Text(context.l10n.readerOverlaySubtitle), 33 | onChanged: ref.read(readerInitialOverlayProvider.notifier).update, 34 | value: ref.watch(readerInitialOverlayProvider).ifNull(), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/reader/widgets/reader_invert_tap_tile/reader_invert_tap_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../../constants/db_keys.dart'; 12 | import '../../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'reader_invert_tap_tile.g.dart'; 16 | 17 | @riverpod 18 | class InvertTap extends _$InvertTap with SharedPreferenceClientMixin { 19 | @override 20 | bool? build() => initialize(DBKeys.invertTap); 21 | } 22 | 23 | class ReaderInvertTapTile extends HookConsumerWidget { 24 | const ReaderInvertTapTile({super.key}); 25 | @override 26 | Widget build(BuildContext context, WidgetRef ref) { 27 | return SwitchListTile( 28 | controlAffinity: ListTileControlAffinity.trailing, 29 | secondary: const Icon(Icons.switch_left_rounded), 30 | title: Text(context.l10n.readerNavigationLayoutInvert), 31 | onChanged: ref.read(invertTapProvider.notifier).update, 32 | value: ref.watch(invertTapProvider).ifNull(), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/reader/widgets/reader_pinch_to_zoom/reader_pinch_to_zoom.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | import 'package:flutter/material.dart'; 7 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 8 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 9 | 10 | import '../../../../../../constants/db_keys.dart'; 11 | import '../../../../../../utils/extensions/custom_extensions.dart'; 12 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 13 | 14 | part 'reader_pinch_to_zoom.g.dart'; 15 | 16 | @riverpod 17 | class PinchToZoom extends _$PinchToZoom with SharedPreferenceClientMixin { 18 | @override 19 | bool? build() => initialize(DBKeys.pinchToZoom); 20 | } 21 | 22 | class ReaderPinchToZoom extends HookConsumerWidget { 23 | const ReaderPinchToZoom({super.key}); 24 | @override 25 | Widget build(BuildContext context, WidgetRef ref) { 26 | return SwitchListTile( 27 | controlAffinity: ListTileControlAffinity.trailing, 28 | secondary: const Icon(Icons.pinch_rounded), 29 | title: Text(context.l10n.pinchToZoom), 30 | onChanged: ref.read(pinchToZoomProvider.notifier).update, 31 | value: ref.watch(pinchToZoomProvider).ifNull(), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/reader/widgets/reader_scroll_animation_tile/reader_scroll_animation_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../../constants/db_keys.dart'; 12 | import '../../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'reader_scroll_animation_tile.g.dart'; 16 | 17 | @riverpod 18 | class ReaderScrollAnimation extends _$ReaderScrollAnimation 19 | with SharedPreferenceClientMixin { 20 | @override 21 | bool? build() => initialize(DBKeys.scrollAnimation); 22 | } 23 | 24 | class ReaderScrollAnimationTile extends HookConsumerWidget { 25 | const ReaderScrollAnimationTile({super.key}); 26 | @override 27 | Widget build(BuildContext context, WidgetRef ref) { 28 | return SwitchListTile( 29 | controlAffinity: ListTileControlAffinity.trailing, 30 | secondary: const Icon(Icons.animation_rounded), 31 | title: Text(context.l10n.readerScrollAnimation), 32 | onChanged: ref.read(readerScrollAnimationProvider.notifier).update, 33 | value: ref.watch(readerScrollAnimationProvider).ifNull(), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/reader/widgets/reader_swipe_toggle_tile/reader_swipe_chapter_toggle_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 10 | 11 | import '../../../../../../constants/db_keys.dart'; 12 | import '../../../../../../utils/extensions/custom_extensions.dart'; 13 | import '../../../../../../utils/mixin/shared_preferences_client_mixin.dart'; 14 | 15 | part 'reader_swipe_chapter_toggle_tile.g.dart'; 16 | 17 | @riverpod 18 | class SwipeChapterToggle extends _$SwipeChapterToggle 19 | with SharedPreferenceClientMixin { 20 | @override 21 | bool? build() => initialize(DBKeys.swipeToggle); 22 | } 23 | 24 | class SwipeChapterToggleTile extends HookConsumerWidget { 25 | const SwipeChapterToggleTile({super.key}); 26 | @override 27 | Widget build(BuildContext context, WidgetRef ref) { 28 | return SwitchListTile( 29 | controlAffinity: ListTileControlAffinity.trailing, 30 | secondary: const Icon(Icons.swipe_rounded), 31 | title: Text(context.l10n.readerSwipeChapterToggle), 32 | subtitle: Text(context.l10n.readerSwipeChapterToggleDescription), 33 | onChanged: ref.read(swipeChapterToggleProvider.notifier).update, 34 | value: ref.watch(swipeChapterToggleProvider).ifNull(), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/server/widget/authentication/authentication_section.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | 4 | import '../../../../../../constants/enum.dart'; 5 | import '../../../../../../global_providers/global_providers.dart'; 6 | import '../../../../../../utils/extensions/custom_extensions.dart'; 7 | import '../../../../../../widgets/section_title.dart'; 8 | import '../credential_popup/credentials_popup.dart'; 9 | import 'auth_type/auth_type_tile.dart'; 10 | 11 | class AuthenticationSection extends ConsumerWidget { 12 | const AuthenticationSection({super.key}); 13 | 14 | @override 15 | Widget build(context, ref) { 16 | final authType = ref.watch(authTypeKeyProvider); 17 | return Column( 18 | crossAxisAlignment: CrossAxisAlignment.start, 19 | children: [ 20 | SectionTitle(title: context.l10n.authentication), 21 | const AuthTypeTile(), 22 | if (authType != null && authType != AuthType.none) 23 | ListTile( 24 | leading: const Icon(Icons.password_rounded), 25 | title: Text(context.l10n.credentials), 26 | onTap: () { 27 | showDialog( 28 | context: context, 29 | builder: (context) => const CredentialsPopup(), 30 | ); 31 | }, 32 | ), 33 | ], 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/src/features/settings/presentation/server/widget/client/client_section.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../../../../../../utils/extensions/custom_extensions.dart'; 4 | import '../../../../../../widgets/section_title.dart'; 5 | import 'server_port_tile/server_port_tile.dart'; 6 | import 'server_url_tile/server_url_tile.dart'; 7 | 8 | class ClientSection extends StatelessWidget { 9 | const ClientSection({super.key}); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | crossAxisAlignment: CrossAxisAlignment.start, 15 | children: [ 16 | SectionTitle(title: context.l10n.client), 17 | const ServerUrlTile(), 18 | const ServerPortTile(), 19 | ], 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/src/graphql/domain/page_info.dart: -------------------------------------------------------------------------------- 1 | import '../__generated__/fragments.graphql.dart'; 2 | 3 | typedef PageInfo = Fragment$PageInfoDto; 4 | -------------------------------------------------------------------------------- /lib/src/graphql/fragments.graphql: -------------------------------------------------------------------------------- 1 | fragment PageInfoDto on PageInfo { 2 | endCursor 3 | hasNextPage 4 | hasPreviousPage 5 | startCursor 6 | } 7 | -------------------------------------------------------------------------------- /lib/src/l10n/app_fil.arb: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /lib/src/l10n/app_nl.arb: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /lib/src/l10n/app_th.arb: -------------------------------------------------------------------------------- 1 | { 2 | "appTheme": "โหมดธีมแอป", 3 | "@appTheme": { 4 | "description": "Popup title and Button text to change App Theme Mode" 5 | }, 6 | "addToLibrary": "เพิ่มเข้าในไลบรารี", 7 | "@addToLibrary": { 8 | "description": "Button text to add Manga to Library in Manga Details Screen" 9 | }, 10 | "about": "เกี่ยวกับ", 11 | "@about": { 12 | "description": "Screen title and Button text of About screen" 13 | }, 14 | "addCategory": "เพิ่มหมวดหมู่", 15 | "@addCategory": { 16 | "description": "Popup title and Button text to add new category in Edit Category Screen" 17 | }, 18 | "allScanlators": "ตัวสแกนแปลทั้งหมด", 19 | "@allScanlators": { 20 | "description": "Text for all Scanlators in manga description screen chapter filter" 21 | }, 22 | "appTitle": "ทาจิเดสก์ โซราโยมิ", 23 | "@appTitle": { 24 | "description": "Name of the app (Tachidesk Sorayomi in native script)" 25 | }, 26 | "appearance": "รูปร่าง", 27 | "@appearance": { 28 | "description": "Screen title and Button text of Appearance screen" 29 | }, 30 | "authType": "ประเภทการรับรองความถูกต้อง", 31 | "@authType": { 32 | "description": "Popup title and Button text to change App Authentication" 33 | }, 34 | "appLanguage": "ภาษาแอป", 35 | "@appLanguage": { 36 | "description": "Popup title and Button text to change App Language" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/routes/sub_routes/downloads_routes.dart: -------------------------------------------------------------------------------- 1 | part of '../router_config.dart'; 2 | 3 | class DownloadsBranch extends StatefulShellBranchData { 4 | const DownloadsBranch(); 5 | } 6 | 7 | class DownloadsRoute extends GoRouteData { 8 | const DownloadsRoute(); 9 | @override 10 | Widget build(context, state) => const DownloadsScreen(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/routes/sub_routes/library_routes.dart: -------------------------------------------------------------------------------- 1 | part of '../router_config.dart'; 2 | 3 | // Library Branch 4 | class LibraryBranch extends StatefulShellBranchData { 5 | static final $initialLocation = const LibraryRoute(categoryId: 0).location; 6 | const LibraryBranch(); 7 | } 8 | 9 | class LibraryRoute extends GoRouteData { 10 | const LibraryRoute({required this.categoryId}); 11 | final int categoryId; 12 | @override 13 | Widget build(context, state) => LibraryScreen(categoryId: categoryId); 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/routes/sub_routes/updates_routes.dart: -------------------------------------------------------------------------------- 1 | part of '../router_config.dart'; 2 | 3 | class UpdatesBranch extends StatefulShellBranchData { 4 | const UpdatesBranch(); 5 | } 6 | 7 | class UpdatesRoute extends GoRouteData { 8 | const UpdatesRoute(); 9 | @override 10 | Widget build(context, state) => const UpdatesScreen(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/src/utils/callbacks.dart: -------------------------------------------------------------------------------- 1 | typedef Convert = O Function(I); 2 | 3 | typedef ConvertO = O? Function(I); 4 | -------------------------------------------------------------------------------- /lib/src/utils/extensions/custom_extensions.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'dart:math'; 8 | 9 | import 'package:flutter/foundation.dart'; 10 | import 'package:flutter/material.dart'; 11 | import 'package:go_router/go_router.dart'; 12 | import "package:gql/language.dart" show printNode; 13 | import 'package:graphql/client.dart'; 14 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 15 | import 'package:intl/intl.dart'; 16 | 17 | import '../../l10n/generated/app_localizations.dart'; 18 | import '../../widgets/custom_circular_progress_indicator.dart'; 19 | import '../../widgets/emoticons.dart'; 20 | import '../callbacks.dart'; 21 | import '../logger/logger.dart'; 22 | import '../misc/app_utils.dart'; 23 | import '../misc/toast/toast.dart'; 24 | 25 | part 'custom_extensions/async_value_extensions.dart'; 26 | part 'custom_extensions/bool_extensions.dart'; 27 | part 'custom_extensions/context_extensions.dart'; 28 | part 'custom_extensions/date_time_extensions.dart'; 29 | part 'custom_extensions/double_extensions.dart'; 30 | part 'custom_extensions/generic_extensions.dart'; 31 | part 'custom_extensions/graphql_extensions.dart'; 32 | part 'custom_extensions/int_extensions.dart'; 33 | part 'custom_extensions/iterable_extensions.dart'; 34 | part 'custom_extensions/logger_extensions.dart'; 35 | part 'custom_extensions/map_extensions.dart'; 36 | part 'custom_extensions/string_extensions.dart'; 37 | -------------------------------------------------------------------------------- /lib/src/utils/extensions/custom_extensions/bool_extensions.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | part of '../custom_extensions.dart'; 8 | 9 | extension BoolExtensions on bool? { 10 | bool get isNull => this == null; 11 | bool get isNotNull => !isNull; 12 | bool ifNull([bool alternative = false]) => this ?? alternative; 13 | int get toInt => this != null ? (this! ? 1 : 2) : 0; 14 | int get toIntWithNegative => this != null ? (this! ? 1 : -1) : 0; 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/utils/extensions/custom_extensions/double_extensions.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | part of '../custom_extensions.dart'; 8 | 9 | extension DoubleExtensions on double? { 10 | bool liesBetween({double lower = 0, double upper = 1}) => 11 | this != null ? this! >= lower && this! <= upper : false; 12 | bool get isZero => this != null ? this! == 0 : true; 13 | bool get isNotZero => !isZero; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/utils/extensions/custom_extensions/generic_extensions.dart: -------------------------------------------------------------------------------- 1 | part of '../custom_extensions.dart'; 2 | 3 | extension ApplyExtension on T? { 4 | U? apply(Convert convert) { 5 | if (this != null) return convert(this as T); 6 | return null; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lib/src/utils/extensions/custom_extensions/iterable_extensions.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | part of '../custom_extensions.dart'; 8 | 9 | extension IterableExtensions on Iterable? { 10 | bool get isNull => this == null; 11 | 12 | bool get isBlank => isNull || this!.isEmpty; 13 | 14 | bool get isNotBlank => !isBlank; 15 | 16 | bool get isSingletonList => isNotBlank && this!.length == 1; 17 | 18 | T? get firstOrNull { 19 | if (isNull) return null; 20 | var iterator = this!.iterator; 21 | if (iterator.moveNext()) return iterator.current; 22 | return null; 23 | } 24 | 25 | String get toPath => isNotBlank ? this!.join("/") : "/"; 26 | 27 | T? lastWhereOrNull(bool Function(T element) test, {T Function()? orElse}) { 28 | if (isNull) return null; 29 | try { 30 | return this!.lastWhere(test, orElse: orElse); 31 | } catch (e) { 32 | return null; 33 | } 34 | } 35 | 36 | T? firstWhereOrNull(bool Function(T element) test, {T Function()? orElse}) { 37 | if (isNull) return null; 38 | try { 39 | return this!.firstWhere(test, orElse: orElse); 40 | } catch (e) { 41 | return null; 42 | } 43 | } 44 | 45 | T? get getRandom => 46 | isNull ? null : this!.elementAt(Random().nextInt(this!.length)); 47 | 48 | Iterable? get filterOutNulls => this?.where((element) => element != null); 49 | } 50 | -------------------------------------------------------------------------------- /lib/src/utils/extensions/custom_extensions/map_extensions.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | part of '../custom_extensions.dart'; 7 | 8 | extension MapExtensions on Map { 9 | Map get filterOutNulls { 10 | final Map filtered = {}; 11 | forEach((K key, V value) { 12 | if (value != null) filtered[key] = value; 13 | }); 14 | return filtered; 15 | } 16 | 17 | Map toggleKey(K key, V value) { 18 | if (containsKey(key)) { 19 | return {...this}..remove(key); 20 | } else { 21 | return {...this, key: value}; 22 | } 23 | } 24 | } 25 | 26 | extension NullableMapExtensions on Map? { 27 | bool get isNull => this == null; 28 | 29 | bool get isBlank => isNull || this!.isEmpty; 30 | 31 | bool get isNotBlank => !isBlank; 32 | 33 | String get toToastString { 34 | String result = ""; 35 | this?.forEach((key, value) { 36 | if (value == null) return; 37 | if (result.isNotBlank) { 38 | result += "\n"; 39 | } 40 | result += value.toString(); 41 | }); 42 | return result; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/src/utils/freezed_converters/language_json_converter.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import '../../constants/language_list.dart'; 8 | import '../../features/browse_center/domain/language/language_model.dart'; 9 | 10 | class LanguageJsonConverter { 11 | static Language? fromJson(String? langCode) => langCode != null 12 | ? languageMap[langCode.toLowerCase()] ?? 13 | Language(code: langCode.toLowerCase()) 14 | : null; 15 | static String? toJson(Language? lang) => lang?.code; 16 | } 17 | -------------------------------------------------------------------------------- /lib/src/utils/hooks/polling_hook.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | 5 | T? usePolling({ 6 | required Duration pollingInterval, 7 | required FutureOr Function() pollFunction, 8 | bool delayedStart = false, 9 | }) { 10 | final data = useState(null); 11 | 12 | useEffect(() { 13 | Future poll() async { 14 | while (true) { 15 | if (delayedStart) { 16 | await Future.delayed(pollingInterval); 17 | } 18 | final result = await pollFunction(); 19 | data.value = result; 20 | if (!delayedStart) { 21 | await Future.delayed(pollingInterval); 22 | } 23 | } 24 | } 25 | 26 | poll(); 27 | 28 | // Cleanup function 29 | return () { 30 | // No cleanup needed for this simple example 31 | }; 32 | }, []); 33 | 34 | return data.value; 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/utils/launch_url_in_web.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/services.dart'; 9 | import 'package:url_launcher/url_launcher.dart'; 10 | 11 | import 'extensions/custom_extensions.dart'; 12 | import 'misc/toast/toast.dart'; 13 | 14 | Future launchUrlInWeb(BuildContext context, String url, 15 | [Toast? toast]) async { 16 | if (!await launchUrl( 17 | Uri.parse(url), 18 | mode: LaunchMode.externalApplication, 19 | webOnlyWindowName: "_blank", 20 | )) { 21 | await Clipboard.setData(ClipboardData(text: url)); 22 | if (context.mounted) toast?.showError(context.l10n.errorLaunchURL(url)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/src/utils/logger/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:logger/logger.dart' as l; 3 | import 'package:logger/web.dart'; 4 | 5 | class Logger { 6 | static final l.Logger _logger = l.Logger( 7 | printer: l.PrettyPrinter( 8 | errorMethodCount: 30, 9 | printEmojis: false, 10 | dateTimeFormat: DateTimeFormat.onlyTimeAndSinceStart, 11 | ), 12 | output: MyConsoleOutput(), 13 | ); 14 | 15 | const Logger._(); 16 | 17 | static l.Logger get instance => _logger; 18 | } 19 | 20 | l.Logger get logger => Logger.instance; 21 | 22 | class MyConsoleOutput extends ConsoleOutput { 23 | @override 24 | void output(OutputEvent event) { 25 | for (int i = 0; i < event.lines.length; i++) { 26 | debugPrint(event.lines[i]); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/src/utils/logger/logger_link.dart: -------------------------------------------------------------------------------- 1 | import 'package:graphql/client.dart'; 2 | 3 | class LoggerLink extends Link { 4 | @override 5 | Stream request( 6 | Request request, [ 7 | NextLink? forward, 8 | ]) { 9 | Stream response = forward!(request).map((Response fetchResult) { 10 | // if (kDebugMode) { 11 | // request.log(); 12 | // logger.i(fetchResult.response); 13 | // } 14 | return fetchResult; 15 | }).handleError((error) { 16 | throw error; 17 | }); 18 | 19 | return response; 20 | } 21 | 22 | LoggerLink(); 23 | } 24 | -------------------------------------------------------------------------------- /lib/src/utils/logger/provider_state_logger.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/rendering.dart'; 8 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 9 | 10 | /// Useful to log state change in our application 11 | /// Read the logs and you'll better understand what's going on under the hood 12 | class ProviderStateLogger extends ProviderObserver { 13 | const ProviderStateLogger(); 14 | @override 15 | void didUpdateProvider( 16 | ProviderBase provider, 17 | Object? previousValue, 18 | Object? newValue, 19 | ProviderContainer container, 20 | ) { 21 | debugPrint(''' 22 | { 23 | provider: ${provider.name ?? provider.runtimeType}, 24 | oldValue: $previousValue, 25 | newValue: $newValue 26 | } 27 | '''); 28 | super.didUpdateProvider(provider, previousValue, newValue, container); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/src/utils/misc/number_range_formatter.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class NumberRangeFormatter extends TextInputFormatter { 4 | final int maxValue; 5 | 6 | NumberRangeFormatter({required this.maxValue}); 7 | 8 | @override 9 | TextEditingValue formatEditUpdate( 10 | TextEditingValue oldValue, 11 | TextEditingValue newValue, 12 | ) { 13 | if (newValue.text.isEmpty) { 14 | return const TextEditingValue( 15 | text: "", 16 | selection: TextSelection.collapsed(offset: 0), 17 | ); 18 | } 19 | String truncated = newValue.text; 20 | TextSelection newSelection = newValue.selection; 21 | 22 | int parsedValue = int.tryParse(newValue.text) ?? 0; 23 | if (parsedValue > maxValue) { 24 | truncated = oldValue.text; 25 | newSelection = oldValue.selection; 26 | } 27 | 28 | return TextEditingValue( 29 | text: truncated, 30 | selection: newSelection, 31 | composing: TextRange.empty, 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/utils/misc/scalars.dart: -------------------------------------------------------------------------------- 1 | import "package:http/http.dart" show MultipartFile; 2 | 3 | export "package:http/http.dart" show MultipartFile; 4 | 5 | MultipartFile fileFromJson(dynamic data) { 6 | return data as MultipartFile; 7 | } 8 | 9 | dynamic fileToJson(MultipartFile data) { 10 | return data; 11 | } 12 | 13 | dynamic dateTimeToJson(DateTime data) { 14 | return data.millisecondsSinceEpoch ~/ 1000; 15 | } 16 | 17 | DateTime dateTimeFromJson(dynamic data) { 18 | return DateTime.fromMillisecondsSinceEpoch(data & 1000); 19 | } 20 | 21 | dynamic longStringToJson(String data) { 22 | return data; 23 | } 24 | 25 | String longStringFromJson(dynamic data) { 26 | return data.toString(); 27 | } 28 | 29 | dynamic cursorToJson(int data) { 30 | return data; 31 | } 32 | 33 | int cursorFromJson(dynamic data) { 34 | return int.tryParse(data) ?? 0; 35 | } 36 | -------------------------------------------------------------------------------- /lib/src/utils/mixin/state_provider_mixin.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | mixin StateProviderMixin { 10 | @protected 11 | set state(State value); 12 | 13 | void update(State state) => this.state = state; 14 | } 15 | -------------------------------------------------------------------------------- /lib/src/widgets/async_buttons/async_checkbox_list_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_hooks/flutter_hooks.dart'; 9 | 10 | import '../../utils/extensions/custom_extensions.dart'; 11 | 12 | class AsyncCheckboxListTile extends HookWidget { 13 | const AsyncCheckboxListTile({ 14 | super.key, 15 | required this.value, 16 | this.onChanged, 17 | required this.title, 18 | }); 19 | 20 | final bool value; 21 | final ValueChanged? onChanged; 22 | final Widget title; 23 | @override 24 | Widget build(BuildContext context) { 25 | final localValue = useState(value); 26 | useEffect(() { 27 | localValue.value = value; 28 | return null; 29 | }, [value]); 30 | return CheckboxListTile( 31 | value: localValue.value, 32 | onChanged: onChanged != null 33 | ? (val) { 34 | localValue.value = (val.ifNull()); 35 | onChanged!(val.ifNull()); 36 | } 37 | : null, 38 | title: title, 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/src/widgets/async_buttons/async_elevated_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | 5 | import '../custom_circular_progress_indicator.dart'; 6 | 7 | class AsyncElevatedButton extends HookWidget { 8 | const AsyncElevatedButton({ 9 | super.key, 10 | required this.onPressed, 11 | required this.child, 12 | this.style, 13 | }); 14 | final AsyncCallback? onPressed; 15 | final Widget child; 16 | final ButtonStyle? style; 17 | @override 18 | Widget build(BuildContext context) { 19 | final isLoading = useState(false); 20 | return ElevatedButton( 21 | onPressed: onPressed == null || isLoading.value 22 | ? null 23 | : () async { 24 | isLoading.value = (true); 25 | await onPressed?.call(); 26 | isLoading.value = (false); 27 | }, 28 | style: style, 29 | child: isLoading.value 30 | ? const MiniCircularProgressIndicator(color: Colors.white) 31 | : child, 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/widgets/async_buttons/async_list_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | 5 | import '../custom_circular_progress_indicator.dart'; 6 | 7 | class AsyncListTile extends HookWidget { 8 | const AsyncListTile({ 9 | super.key, 10 | this.leading, 11 | this.title, 12 | this.subtitle, 13 | this.trailing, 14 | this.onTap, 15 | this.showInLeading = true, 16 | }); 17 | final Widget? leading; 18 | final Widget? title; 19 | final Widget? subtitle; 20 | final Widget? trailing; 21 | final AsyncValueSetter>? onTap; 22 | final bool showInLeading; 23 | @override 24 | Widget build(BuildContext context) { 25 | final isLoading = useState(false); 26 | return ListTile( 27 | leading: showInLeading && isLoading.value 28 | ? const MiniCircularProgressIndicator() 29 | : leading, 30 | title: title, 31 | subtitle: subtitle, 32 | trailing: trailing, 33 | onTap: onTap == null 34 | ? null 35 | : () async { 36 | isLoading.value = true; 37 | await onTap?.call(isLoading); 38 | isLoading.value = false; 39 | }, 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/src/widgets/async_buttons/async_outline_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | 5 | import '../custom_circular_progress_indicator.dart'; 6 | 7 | class AsyncOutlineButton extends HookWidget { 8 | const AsyncOutlineButton({ 9 | super.key, 10 | required this.onPressed, 11 | required this.child, 12 | this.style, 13 | }); 14 | final AsyncCallback? onPressed; 15 | final Widget child; 16 | final ButtonStyle? style; 17 | @override 18 | Widget build(BuildContext context) { 19 | final isLoading = useState(false); 20 | return OutlinedButton( 21 | onPressed: onPressed == null || isLoading.value 22 | ? null 23 | : () async { 24 | isLoading.value = (true); 25 | await onPressed?.call(); 26 | isLoading.value = (false); 27 | }, 28 | style: style, 29 | child: isLoading.value ? const MiniCircularProgressIndicator() : child, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/widgets/async_buttons/async_text_button.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/foundation.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_hooks/flutter_hooks.dart'; 10 | 11 | import '../custom_circular_progress_indicator.dart'; 12 | 13 | class AsyncTextButton extends HookWidget { 14 | const AsyncTextButton({ 15 | super.key, 16 | required this.onPressed, 17 | required this.child, 18 | this.icon, 19 | }); 20 | 21 | final AsyncCallback? onPressed; 22 | final Widget child; 23 | final Widget? icon; 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | final isLoading = useState(false); 28 | return TextButton.icon( 29 | onPressed: (!isLoading.value && onPressed != null) 30 | ? () async { 31 | isLoading.value = (true); 32 | await onPressed!(); 33 | isLoading.value = (false); 34 | } 35 | : null, 36 | icon: isLoading.value ? const SizedBox.shrink() : icon, 37 | label: isLoading.value ? const MiniCircularProgressIndicator() : child, 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/src/widgets/custom_checkbox_list_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 10 | 11 | import '../utils/extensions/custom_extensions.dart'; 12 | 13 | class CustomCheckboxListTile> 14 | extends ConsumerWidget { 15 | const CustomCheckboxListTile({ 16 | super.key, 17 | required this.title, 18 | required this.provider, 19 | required this.onChanged, 20 | this.tristate = true, 21 | }); 22 | final String title; 23 | final AutoDisposeNotifierProvider provider; 24 | final ValueChanged onChanged; 25 | final bool tristate; 26 | @override 27 | Widget build(BuildContext context, WidgetRef ref) { 28 | final val = ref.watch(provider); 29 | return CheckboxListTile( 30 | controlAffinity: ListTileControlAffinity.leading, 31 | activeColor: context.theme.indicatorColor, 32 | value: tristate ? val : val.ifNull(true), 33 | title: Text(title), 34 | tristate: tristate, 35 | onChanged: onChanged, 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/widgets/input_popup/domain/settings_prop_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'settings_prop_type.freezed.dart'; 4 | 5 | typedef SettingsUpdateRequest = Future Function(V); 6 | 7 | @freezed 8 | sealed class SettingsPropType with _$SettingsPropType { 9 | const factory SettingsPropType.textField({ 10 | String? hintText, 11 | String? value, 12 | SettingsUpdateRequest? onChanged, 13 | @Default(false) bool canObscure, 14 | }) = TextFieldProp; 15 | const factory SettingsPropType.numberPicker({ 16 | required int min, 17 | required int max, 18 | int? value, 19 | SettingsUpdateRequest? onChanged, 20 | }) = NumberPickerProp; 21 | const factory SettingsPropType.numberSlider({ 22 | required int min, 23 | required int max, 24 | int? value, 25 | SettingsUpdateRequest? onChanged, 26 | }) = NumberSliderProp; 27 | const factory SettingsPropType.switchTile({ 28 | bool? value, 29 | SettingsUpdateRequest? onChanged, 30 | }) = SwitchProp; 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/widgets/manga_cover/providers/manga_cover_providers.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 8 | 9 | import '../../../constants/db_keys.dart'; 10 | import '../../../utils/mixin/shared_preferences_client_mixin.dart'; 11 | 12 | part 'manga_cover_providers.g.dart'; 13 | 14 | @riverpod 15 | class DownloadedBadge extends _$DownloadedBadge 16 | with SharedPreferenceClientMixin { 17 | @override 18 | bool? build() => initialize(DBKeys.downloadedBadge); 19 | } 20 | 21 | @riverpod 22 | class UnreadBadge extends _$UnreadBadge with SharedPreferenceClientMixin { 23 | @override 24 | bool? build() => initialize(DBKeys.unreadBadge); 25 | } 26 | 27 | // @riverpod 28 | // class LanguageBadge extends _$LanguageBadge 29 | // with SharedPreferenceClient { 30 | // @override 31 | // bool? build() => initialize( 32 | // 33 | // key: DBKeys.languageBadge.name, 34 | // initial: DBKeys.languageBadge.initial, 35 | // ); 36 | // } 37 | -------------------------------------------------------------------------------- /lib/src/widgets/number_picker/number_picker_with_label.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_hooks/flutter_hooks.dart'; 3 | 4 | import 'number_picker.dart'; 5 | 6 | class NumberPickerWithLabel extends HookWidget { 7 | const NumberPickerWithLabel({ 8 | super.key, 9 | required this.label, 10 | required this.min, 11 | required this.max, 12 | required this.value, 13 | required this.onChanged, 14 | this.textFieldWidth = 24, 15 | }); 16 | final String label; 17 | 18 | final int min; 19 | final int max; 20 | final int value; 21 | final ValueChanged onChanged; 22 | final double textFieldWidth; 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | useEffect(() { 27 | Future.microtask(() => onChanged(value)); 28 | return null; 29 | }, []); 30 | return Row( 31 | children: [ 32 | Expanded( 33 | child: Text(label), 34 | ), 35 | NumberPicker( 36 | value: value, 37 | min: min, 38 | max: max, 39 | onChanged: onChanged, 40 | textFieldWidth: textFieldWidth, 41 | ), 42 | ], 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/src/widgets/popup_widgets/pop_button.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../../utils/extensions/custom_extensions.dart'; 10 | 11 | class PopButton extends StatelessWidget { 12 | const PopButton({super.key, this.popText}); 13 | final String? popText; 14 | @override 15 | Widget build(BuildContext context) { 16 | return TextButton( 17 | onPressed: () => Navigator.pop(context), 18 | child: Text(popText ?? context.l10n.cancel), 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/src/widgets/section_title.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../constants/app_sizes.dart'; 4 | import '../utils/extensions/custom_extensions.dart'; 5 | 6 | class SectionTitle extends StatelessWidget { 7 | const SectionTitle({super.key, required this.title}); 8 | final String title; 9 | @override 10 | Widget build(BuildContext context) => Padding( 11 | padding: KEdgeInsets.h16.size + KEdgeInsets.v4.size, 12 | child: Text( 13 | title, 14 | style: context.textTheme.titleSmall 15 | ?.copyWith(color: context.theme.primaryColor), 16 | ), 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/widgets/sort_list_tile.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 Contributors to the Suwayomi project 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import '../utils/extensions/custom_extensions.dart'; 10 | 11 | class SortListTile extends StatelessWidget { 12 | const SortListTile({ 13 | super.key, 14 | required this.ascending, 15 | required this.selected, 16 | required this.onChanged, 17 | required this.onSelected, 18 | this.title, 19 | this.subtitle, 20 | }); 21 | final bool ascending; 22 | final bool selected; 23 | final ValueChanged onChanged; 24 | final VoidCallback onSelected; 25 | final Widget? title; 26 | final Widget? subtitle; 27 | @override 28 | Widget build(BuildContext context) { 29 | IconData icon = 30 | ascending ? Icons.arrow_upward_rounded : Icons.arrow_downward_rounded; 31 | 32 | return ListTile( 33 | leading: selected 34 | ? Icon(icon, color: context.theme.indicatorColor) 35 | : SizedBox(width: context.theme.iconTheme.size), 36 | title: title, 37 | subtitle: subtitle, 38 | onTap: () => selected ? onChanged(!ascending) : onSelected(), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | 11 | void fl_register_plugins(FlPluginRegistry* registry) { 12 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 13 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 14 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 15 | } 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | url_launcher_linux 7 | ) 8 | 9 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 10 | ) 11 | 12 | set(PLUGIN_BUNDLED_LIBRARIES) 13 | 14 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 15 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 16 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 18 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 19 | endforeach(plugin) 20 | 21 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 22 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 23 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 24 | endforeach(ffi_plugin) 25 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import connectivity_plus 9 | import file_picker 10 | import network_info_plus 11 | import package_info_plus 12 | import path_provider_foundation 13 | import shared_preferences_foundation 14 | import sqflite_darwin 15 | import url_launcher_macos 16 | 17 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 18 | ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) 19 | FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) 20 | NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) 21 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 22 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 23 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 24 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 25 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 26 | } 27 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.15' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | target.build_configurations.each do |config| 40 | config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.15' 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @main 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | 10 | override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { 11 | return true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = Sorayomi 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.suwayomi.tachideskSorayomi 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2022 com.suwayomi. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | com.apple.security.network.client 12 | 13 | com.apple.security.files.user-selected.read-only 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | com.apple.security.files.user-selected.read-only 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/build/.last_build_id: -------------------------------------------------------------------------------- 1 | b18191054b7e296b4379e9085d256a59 -------------------------------------------------------------------------------- /scripts/debian/changelog: -------------------------------------------------------------------------------- 1 | tachidesk-sorayomi ($pkgver-$pkgrel) unstable; urgency=medium 2 | 3 | * See https://github.com/Suwayomi/Tachidesk-Sorayomi 4 | 5 | -- Mahor Fri, 17 Feb 2022 00:00:00 +0000 6 | -------------------------------------------------------------------------------- /scripts/debian/control: -------------------------------------------------------------------------------- 1 | Source: tachidesk-sorayomi 2 | Section: web 3 | Priority: optional 4 | Maintainer: Mahor 5 | Build-Depends: debhelper-compat (= 12) 6 | Standards-Version: 4.5.1 7 | Homepage: https://github.com/Suwayomi/Tachidesk-Sorayomi 8 | 9 | Package: tachidesk-sorayomi 10 | Architecture: amd64 11 | Depends: ${misc:Depends}, zenity 12 | Description: Manga Reader 13 | A free and open source manga reader server that runs extensions built for Tachiyomi. 14 | Tachidesk is an independent Tachiyomi compatible software and is not a Fork of Tachiyomi. 15 | -------------------------------------------------------------------------------- /scripts/debian/install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dh-exec 2 | 3 | data opt/tachidesk-sorayomi/ 4 | lib opt/tachidesk-sorayomi/ 5 | tachidesk_sorayomi opt/tachidesk-sorayomi/ 6 | tachidesk-sorayomi.png usr/share/pixmaps/ 7 | tachidesk-sorayomi.desktop usr/share/applications/ 8 | -------------------------------------------------------------------------------- /scripts/debian/package.dirs: -------------------------------------------------------------------------------- 1 | opt/tachidesk-sorayomi/ 2 | 3 | -------------------------------------------------------------------------------- /scripts/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | #export DH_VERBOSE = 1 5 | 6 | %: 7 | dh $@ 8 | 9 | override_dh_strip_nondeterminism: 10 | true 11 | -------------------------------------------------------------------------------- /scripts/debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /scripts/debian/source/include-binaries: -------------------------------------------------------------------------------- 1 | tachidesk-sorayomi.png 2 | -------------------------------------------------------------------------------- /scripts/debian/tachidesk-sorayomi.links: -------------------------------------------------------------------------------- 1 | opt/tachidesk-sorayomi/tachidesk_sorayomi usr/bin/tachidesk-sorayomi 2 | -------------------------------------------------------------------------------- /scripts/tachidesk-sorayomi.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Sorayomi 3 | Comment=Tachidesk UI flutter 4 | Exec=tachidesk-sorayomi 5 | Icon=tachidesk-sorayomi 6 | Terminal=false 7 | Type=Application 8 | Categories=Network; 9 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tachidesk_sorayomi", 3 | "short_name": "tachidesk_sorayomi", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#54C5F8", 7 | "theme_color": "#54C5F8", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /web/splash/img/branding-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-1x.png -------------------------------------------------------------------------------- /web/splash/img/branding-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-2x.png -------------------------------------------------------------------------------- /web/splash/img/branding-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-3x.png -------------------------------------------------------------------------------- /web/splash/img/branding-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-4x.png -------------------------------------------------------------------------------- /web/splash/img/branding-dark-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-dark-1x.png -------------------------------------------------------------------------------- /web/splash/img/branding-dark-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-dark-2x.png -------------------------------------------------------------------------------- /web/splash/img/branding-dark-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-dark-3x.png -------------------------------------------------------------------------------- /web/splash/img/branding-dark-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/branding-dark-4x.png -------------------------------------------------------------------------------- /web/splash/img/dark-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/dark-1x.png -------------------------------------------------------------------------------- /web/splash/img/dark-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/dark-2x.png -------------------------------------------------------------------------------- /web/splash/img/dark-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/dark-3x.png -------------------------------------------------------------------------------- /web/splash/img/dark-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/dark-4x.png -------------------------------------------------------------------------------- /web/splash/img/light-1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/light-1x.png -------------------------------------------------------------------------------- /web/splash/img/light-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/light-2x.png -------------------------------------------------------------------------------- /web/splash/img/light-3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/light-3x.png -------------------------------------------------------------------------------- /web/splash/img/light-4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/web/splash/img/light-4x.png -------------------------------------------------------------------------------- /web/splash/splash.js: -------------------------------------------------------------------------------- 1 | function removeSplashFromWeb() { 2 | document.getElementById("splash")?.remove(); 3 | document.getElementById("splash-branding")?.remove(); 4 | document.body.style.background = "transparent"; 5 | } 6 | -------------------------------------------------------------------------------- /web/splash/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | height: 100% 3 | } 4 | 5 | body { 6 | margin: 0; 7 | min-height: 100%; 8 | background-color: #54C5F8; 9 | background-size: 100% 100%; 10 | } 11 | 12 | .center { 13 | margin: 0; 14 | position: absolute; 15 | top: 50%; 16 | left: 50%; 17 | -ms-transform: translate(-50%, -50%); 18 | transform: translate(-50%, -50%); 19 | } 20 | 21 | .contain { 22 | display:block; 23 | width:100%; height:100%; 24 | object-fit: contain; 25 | } 26 | 27 | .stretch { 28 | display:block; 29 | width:100%; height:100%; 30 | } 31 | 32 | .cover { 33 | display:block; 34 | width:100%; height:100%; 35 | object-fit: cover; 36 | } 37 | 38 | .bottom { 39 | position: absolute; 40 | bottom: 0; 41 | left: 50%; 42 | -ms-transform: translate(-50%, 0); 43 | transform: translate(-50%, 0); 44 | } 45 | 46 | .bottomLeft { 47 | position: absolute; 48 | bottom: 0; 49 | left: 0; 50 | } 51 | 52 | .bottomRight { 53 | position: absolute; 54 | bottom: 0; 55 | right: 0; 56 | } 57 | 58 | @media (prefers-color-scheme: dark) { 59 | body { 60 | background-color: #01579B; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void RegisterPlugins(flutter::PluginRegistry* registry) { 14 | ConnectivityPlusWindowsPluginRegisterWithRegistrar( 15 | registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); 16 | PermissionHandlerWindowsPluginRegisterWithRegistrar( 17 | registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); 18 | UrlLauncherWindowsRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 20 | } 21 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | connectivity_plus 7 | permission_handler_windows 8 | url_launcher_windows 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.CreateAndShow(L"Sorayomi", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Suwayomi/Tachidesk-Sorayomi/c4a3fd23cb90fa5c928e20e6e2ad10323134c767/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------