diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 62dc169f..7b137161 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -38,34 +38,44 @@ jobs:
- os: windows-latest
target: windows
build_path: build\windows\x64\runner\Release
+ - os: windows-latest
+ target: windows-arm64
+ build_path: build\windows\arm64\runner\Release
steps:
- uses: subosito/flutter-action@v2
with:
- channel: 'stable'
+ channel: "stable"
+ # Install Linux dependencies
- name: Install Linux dependencies
if: matrix.target == 'linux'
run: |
sudo apt-get update -y
sudo apt-get install -y libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev
+
+ # Install Android dependencies
- name: Install Android dependencies
if: matrix.target == 'android'
uses: actions/setup-java@v3
with:
- distribution: 'zulu'
+ distribution: "zulu"
java-version: "12.x"
+ # Enable desktop support
- name: Enable desktop support
if: matrix.target != 'android' && matrix.target != 'ios' && matrix.target != 'web'
run: |
flutter config --enable-linux-desktop
flutter config --enable-macos-desktop
flutter config --enable-windows-desktop
+
+ # Recreating the project
- run: flutter doctor -v
- uses: actions/checkout@v3
# - run: flutter create .
- run: flutter pub get
+ # Configure Keystore for Android
- name: Configure Keystore for Android
if: matrix.target == 'android'
working-directory: android
@@ -76,15 +86,19 @@ jobs:
echo "storePassword=${{ secrets.KEYSTORE_STORE_PASSWORD }}" >> key.properties
echo "keyPassword=${{ secrets.KEYSTORE_KEY_PASSWORD }}" >> key.properties
+ # Build the project for the target platform
- if: matrix.target != 'android' && matrix.target != 'ios'
run: flutter build ${{ matrix.target }} --release
+
- if: matrix.target == 'android'
run: |
flutter build apk --release
flutter build apk --split-per-abi --release
+
- if: matrix.target == 'ios'
run: flutter build ios --release --no-codesign
+ # Windows packaging
- name: Copy VC redistributables to release directory for Windows
if: matrix.target == 'windows'
working-directory: ${{ matrix.build_path }}
@@ -92,6 +106,17 @@ jobs:
Copy-Item (vswhere -latest -find 'VC\Redist\MSVC\*\x64\*\msvcp140.dll') .
Copy-Item (vswhere -latest -find 'VC\Redist\MSVC\*\x64\*\vcruntime140.dll') .
Copy-Item (vswhere -latest -find 'VC\Redist\MSVC\*\x64\*\vcruntime140_1.dll') .
+
+ # Windows packaging
+ - name: Copy VC redistributables to release directory for Windows
+ if: matrix.target == 'windows-arm64'
+ working-directory: ${{ matrix.build_path }}
+ run: |
+ Copy-Item (vswhere -latest -find 'VC\Redist\MSVC\*\arm64\*\msvcp140.dll') .
+ Copy-Item (vswhere -latest -find 'VC\Redist\MSVC\*\arm64\*\vcruntime140.dll') .
+ Copy-Item (vswhere -latest -find 'VC\Redist\MSVC\*\arm64\*\vcruntime140_1.dll') .
+
+ # Android packaging
- name: Rename build for Android
if: matrix.target == 'android'
working-directory: ${{ matrix.build_path }}
@@ -126,6 +151,7 @@ jobs:
cp "$GITHUB_WORKSPACE/assets/icons/launcher/sorayomi_icon.png" "./${{ env.pkg_name }}.png"
GZIP=-9 tar czf "$GITHUB_WORKSPACE/${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-x64.tar.gz" *
+ # Debian packaging
- name: Build deb package
if: matrix.target == 'linux'
run: |
@@ -143,14 +169,24 @@ jobs:
debuild --no-lintian -us -uc
cp "../${{ env.pkg_name }}_${{ github.ref_name }}-1_amd64.deb" "$GITHUB_WORKSPACE/"
+ # macOS packaging
- name: Compress build for macOS
if: matrix.target == 'macos'
working-directory: ${{ matrix.build_path }}
- run: ditto -c -k --sequesterRsrc --keepParent "Tachidesk Sorayomi.app" "$GITHUB_WORKSPACE/${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-x64.zip"
+ run: ditto -c -k --sequesterRsrc --keepParent "Sorayomi.app" "$GITHUB_WORKSPACE/${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-x64.zip"
+
+ # Windows packaging
- name: Compress build for Windows
if: matrix.target == 'windows'
working-directory: ${{ matrix.build_path }}
run: compress-archive -Path * -DestinationPath "${env:GITHUB_WORKSPACE}\${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-x64.zip"
+
+ # Windows packaging
+ - name: Compress build for Windows
+ if: matrix.target == 'windows'
+ working-directory: ${{ matrix.build_path }}
+ run: compress-archive -Path * -DestinationPath "${env:GITHUB_WORKSPACE}\${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-arm64.zip"
+
- name: Create MSI Package
if: matrix.target == 'windows' && startsWith(github.ref, 'refs/tags/')
working-directory: ${{ matrix.build_path }}
@@ -164,6 +200,19 @@ jobs:
light -b data "${{ env.pkg_name }}-x64.wixobj" data.wixobj -o "${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-x64.msi"
Move-Item "${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-x64.msi" "${env:GITHUB_WORKSPACE}"
+ - name: Create arm64 MSI Package
+ if: matrix.target == 'windows-arm64' && startsWith(github.ref, 'refs/tags/')
+ working-directory: ${{ matrix.build_path }}
+ run: |
+ $env:Path += ";C:\Program Files (x86)\WiX Toolset v3.11\bin"
+ $upgradeCode = [guid]::NewGuid().Guid
+ Copy-Item "${env:GITHUB_WORKSPACE}\scripts\${{ env.pkg_name }}-arm64.wxs" .
+ Copy-Item "${env:GITHUB_WORKSPACE}\assets\icons\launcher\sorayomi_icon.ico" .
+ heat dir data -cg data -dr data -sreg -sfrag -gg -o data.wxs
+ candle -arch arm64 -dVersion="${{ github.ref_name }}" -dUpgradeCode="$upgradeCode" -dIcon="sorayomi_icon.ico" "${{ env.pkg_name }}-arm64.wxs" data.wxs
+ light -b data "${{ env.pkg_name }}-arm64.wixobj" data.wixobj -o "${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-arm64.msi"
+ Move-Item "${{ env.pkg_name }}-${{ github.ref_name }}-${{ matrix.target }}-arm64.msi" "${env:GITHUB_WORKSPACE}"
+
- name: Add packaged build to release draft
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml
index 25782b44..8e7c9f69 100644
--- a/.github/workflows/web.yml
+++ b/.github/workflows/web.yml
@@ -1,5 +1,5 @@
# Copyright (c) 2022 Contributors to the Suwayomi project
-#
+#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
@@ -23,4 +23,4 @@ jobs:
channel: 'stable'
- uses: bluefireteam/flutter-gh-pages@v7
with:
- baseHref: /Tachidesk-Sorayomi/
\ No newline at end of file
+ baseHref: /Tachidesk-Sorayomi/
diff --git a/.vscode/settings.json b/.vscode/settings.json
index aea96891..b781eac2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -16,6 +16,7 @@
"reddy",
"Scanlator",
"scanlators",
+ "Solverr",
"sublist",
"suwayomi",
"tachidesk",
@@ -27,5 +28,15 @@
],
"licenser.license": "MPLv2",
"licenser.projectName": "Tachidesk-Sorayomi",
- "licenser.author": "Contributors to the Suwayomi project"
+ "licenser.author": "Contributors to the Suwayomi project",
+ "files.exclude": {
+ "**/__generated__": true,
+ "**/*.ast.gql.dart": true,
+ "**/*.data.gql.dart": true,
+ "**/*.freezed.dart": true,
+ "**/*.g.dart": true,
+ "**/*.gql.g.dart": true,
+ "**/*.req.gql.dart": true,
+ "**/*.var.gql.dart": true
+ }
}
diff --git a/.vscode/sorayomi.code-snippets b/.vscode/sorayomi.code-snippets
new file mode 100644
index 00000000..d635a7f3
--- /dev/null
+++ b/.vscode/sorayomi.code-snippets
@@ -0,0 +1,17 @@
+{
+ // Place your sorayomi workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
+ // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
+ // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
+ // used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
+ // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
+ // Placeholders with the same ids are connected.
+ // Example:
+ "Basic Dart Graphql Query Request": {
+ "scope": "dart",
+ "prefix": "req",
+ "body": [
+ "static G${1:query}Req ${1/^(.)(.*)$/${1:/downcase}${2}/}() => G${1:query}Req();"
+ ],
+ "description": "Creates graphql query",
+ },
+}
diff --git a/README.md b/README.md
index 384553fc..dafafb9d 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-
+
- Tachidesk Sorayomi
+ Sorayomi
diff --git a/analysis_options.yaml b/analysis_options.yaml
index bc304fd8..bfeb970f 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -34,6 +34,7 @@ analyzer:
- "**/*.gform.dart"
- "**/*.g.dart"
- "**/*.freezed.dart"
+ - "**/*.gql.dart"
errors:
- invalid_annotation_target: ignore
\ No newline at end of file
+ invalid_annotation_target: ignore
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 84de668d..9f3fea11 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -1,3 +1,9 @@
+plugins {
+ id "com.android.application"
+ id "kotlin-android"
+ id "dev.flutter.flutter-gradle-plugin"
+}
+
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
@@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) {
}
}
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
@@ -21,10 +22,6 @@ if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
@@ -78,6 +75,4 @@ flutter {
source '../..'
}
-dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-}
+dependencies {}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 6cb2f6af..87f10f84 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -2,7 +2,7 @@
package="com.suwayomi.tachidesk_sorayomi">
properties.load(reader) }
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
-def flutterSdkPath = properties.getProperty("flutter.sdk")
-assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
-apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ id "org.jetbrains.kotlin.android" version "1.7.10" apply false
+ id "com.google.gms.google-services" version "4.4.0" apply false
+ id "com.google.firebase.crashlytics" version "2.9.9" apply false
+}
+
+include ":app"
diff --git a/build.yaml b/build.yaml
index aaa6e0dd..aab320fd 100644
--- a/build.yaml
+++ b/build.yaml
@@ -3,4 +3,23 @@ targets:
builders:
json_serializable:
options:
- explicit_to_json: true
\ No newline at end of file
+ explicit_to_json: true
+ ferry_generator|graphql_builder:
+ enabled: true
+ options:
+ schema: tachidesk_sorayomi|lib/src/graphql/schema.graphql
+ type_overrides:
+ Upload:
+ name: MultipartFile
+ import: 'package:http/http.dart'
+ ferry_generator|serializer_builder:
+ enabled: true
+ options:
+ schema: tachidesk_sorayomi|lib/src/graphql/schema.graphql
+ type_overrides:
+ Upload:
+ name: MultipartFile
+ import: 'package:http/http.dart'
+ custom_serializers:
+ - import: 'package:tachidesk_sorayomi/src/utils/misc/upload_serializer.dart'
+ name: UploadSerializer
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 1bde876c..6f5d9a75 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -5,7 +5,7 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
- Tachidesk Sorayomi
+ Sorayomi
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
diff --git a/lib/main.dart b/lib/main.dart
index 8ae01949..06f5431e 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -6,10 +6,12 @@
import 'dart:io';
+import 'package:ferry_hive_store/ferry_hive_store.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:go_router/go_router.dart';
+import 'package:hive_flutter/hive_flutter.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path/path.dart' as path;
@@ -24,37 +26,29 @@ Future main() async {
WidgetsFlutterBinding.ensureInitialized();
final packageInfo = await PackageInfo.fromPlatform();
final sharedPreferences = await SharedPreferences.getInstance();
+ await Hive.initFlutter('Sorayomi');
+ final box = await Hive.openBox("graphql");
+ final store = HiveStore(box);
+ //TODO remove
final Directory? appDirectory;
if (!kIsWeb) {
final appDocDirectory = await getApplicationDocumentsDirectory();
appDirectory = Directory(path.join(appDocDirectory.path, 'Sorayomi'));
-
await appDirectory.create(recursive: true);
-
- final cacheFiles = ['dio_cache.hive', 'dio_cache.lock'];
- for (final cacheFile in cacheFiles) {
- final oldCacheFilePath = path.join(appDocDirectory.path, cacheFile);
- final newCacheFilePath = path.join(appDirectory.path, cacheFile);
-
- if (!(await File(newCacheFilePath).exists()) &&
- await File(oldCacheFilePath).exists()) {
- await File(oldCacheFilePath).rename(newCacheFilePath);
- }
- }
} else {
appDirectory = null;
}
SystemChrome.setPreferredOrientations(DeviceOrientation.values);
GoRouter.optionURLReflectsImperativeAPIs = true;
-
runApp(
ProviderScope(
overrides: [
packageInfoProvider.overrideWithValue(packageInfo),
sharedPreferencesProvider.overrideWithValue(sharedPreferences),
appDirectoryProvider.overrideWithValue(appDirectory),
+ hiveStoreProvider.overrideWithValue(store)
],
child: const Sorayomi(),
),
diff --git a/lib/src/abstracts/locale_enum.dart b/lib/src/abstracts/locale_enum.dart
new file mode 100644
index 00000000..0e1a8ddd
--- /dev/null
+++ b/lib/src/abstracts/locale_enum.dart
@@ -0,0 +1,12 @@
+import 'package:flutter/material.dart';
+
+import 'value_enum.dart';
+
+abstract interface class LocaleEnum implements ValueEnum {
+ LocaleEnum(this.value);
+
+ @override
+ final String value;
+
+ String toLocale(BuildContext context);
+}
diff --git a/lib/src/abstracts/value_enum.dart b/lib/src/abstracts/value_enum.dart
new file mode 100644
index 00000000..7fb0ae30
--- /dev/null
+++ b/lib/src/abstracts/value_enum.dart
@@ -0,0 +1,5 @@
+abstract interface class ValueEnum implements Enum {
+ ValueEnum(this.value);
+
+ final String value;
+}
diff --git a/lib/src/constants/app_sizes.dart b/lib/src/constants/app_sizes.dart
index b3cef0c7..803ceb89 100644
--- a/lib/src/constants/app_sizes.dart
+++ b/lib/src/constants/app_sizes.dart
@@ -12,7 +12,7 @@ import 'app_constants.dart';
import 'db_keys.dart';
const kTabSize = Size.fromHeight(kAppBarBottomHeight);
-const kAppBarBottomHeight = 64.0;
+const kAppBarBottomHeight = 46.0;
const kDrawerWidth = 384.0;
Size kCalculateAppBarBottomSize(List checks) {
diff --git a/lib/src/constants/endpoints.dart b/lib/src/constants/endpoints.dart
index a623c2b6..04609827 100644
--- a/lib/src/constants/endpoints.dart
+++ b/lib/src/constants/endpoints.dart
@@ -13,10 +13,19 @@ abstract class Endpoints {
int? port,
bool addPort = true,
bool appendApiToUrl = true,
- }) =>
- "${baseUrl ?? DBKeys.serverUrl.initial}"
- "${port != null && addPort ? ":$port" : ''}"
- "${appendApiToUrl ? '/api/v1' : ''}";
+ bool isGraphQl = false,
+ }) {
+ Uri url = Uri.tryParse(baseUrl ?? DBKeys.serverUrl.initial) ??
+ Uri.parse(DBKeys.serverUrl.initial);
+ if (port != null && addPort) {
+ url = url.replace(port: port);
+ }
+ if (appendApiToUrl) {
+ final api = ['api', isGraphQl ? 'graphql' : 'v1'];
+ url = url.replace(pathSegments: [...url.pathSegments, ...api]);
+ }
+ return url.toString();
+ }
// receiveTimeout
static const Duration receiveTimeout = Duration(minutes: 1);
diff --git a/lib/src/constants/enum.dart b/lib/src/constants/enum.dart
index ead94888..4abe18b7 100644
--- a/lib/src/constants/enum.dart
+++ b/lib/src/constants/enum.dart
@@ -5,7 +5,9 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
import 'package:flutter/material.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import '../abstracts/value_enum.dart';
import '../utils/extensions/custom_extensions.dart';
enum AuthType {
@@ -13,8 +15,8 @@ enum AuthType {
basic;
String toLocale(BuildContext context) => switch (this) {
- AuthType.none => context.l10n!.authTypeNone,
- AuthType.basic => context.l10n!.authTypeBasic,
+ AuthType.none => context.l10n.authTypeNone,
+ AuthType.basic => context.l10n.authTypeBasic,
};
}
@@ -29,19 +31,19 @@ enum ReaderMode {
webtoon;
String toLocale(BuildContext context) => switch (this) {
- ReaderMode.defaultReader => context.l10n!.readerModeDefaultReader,
+ ReaderMode.defaultReader => context.l10n.readerModeDefaultReader,
ReaderMode.continuousVertical =>
- context.l10n!.readerModeContinuousVertical,
+ context.l10n.readerModeContinuousVertical,
ReaderMode.singleHorizontalLTR =>
- context.l10n!.readerModeSingleHorizontalLTR,
+ context.l10n.readerModeSingleHorizontalLTR,
ReaderMode.singleHorizontalRTL =>
- context.l10n!.readerModeSingleHorizontalRTL,
+ context.l10n.readerModeSingleHorizontalRTL,
ReaderMode.continuousHorizontalLTR =>
- context.l10n!.readerModeContinuousHorizontalLTR,
+ context.l10n.readerModeContinuousHorizontalLTR,
ReaderMode.continuousHorizontalRTL =>
- context.l10n!.readerModeContinuousHorizontalRTL,
- ReaderMode.singleVertical => context.l10n!.readerModeSingleVertical,
- ReaderMode.webtoon => context.l10n!.readerModeWebtoon
+ context.l10n.readerModeContinuousHorizontalRTL,
+ ReaderMode.singleVertical => context.l10n.readerModeSingleVertical,
+ ReaderMode.webtoon => context.l10n.readerModeWebtoon
};
}
@@ -55,30 +57,28 @@ enum ReaderNavigationLayout {
String toLocale(BuildContext context) => switch (this) {
ReaderNavigationLayout.defaultNavigation =>
- context.l10n!.readerNavigationLayoutDefault,
+ context.l10n.readerNavigationLayoutDefault,
ReaderNavigationLayout.lShaped =>
- context.l10n!.readerNavigationLayoutLShaped,
+ context.l10n.readerNavigationLayoutLShaped,
ReaderNavigationLayout.rightAndLeft =>
- context.l10n!.readerNavigationLayoutRightAndLeft,
- ReaderNavigationLayout.edge => context.l10n!.readerNavigationLayoutEdge,
+ context.l10n.readerNavigationLayoutRightAndLeft,
+ ReaderNavigationLayout.edge => context.l10n.readerNavigationLayoutEdge,
ReaderNavigationLayout.kindlish =>
- context.l10n!.readerNavigationLayoutKindlish,
+ context.l10n.readerNavigationLayoutKindlish,
ReaderNavigationLayout.disabled =>
- context.l10n!.readerNavigationLayoutDisabled
+ context.l10n.readerNavigationLayoutDisabled
};
}
enum MangaSort {
alphabetical,
dateAdded,
- unread,
- lastRead;
+ unread;
String toLocale(BuildContext context) => switch (this) {
- MangaSort.alphabetical => context.l10n!.mangaSortAlphabetical,
- MangaSort.dateAdded => context.l10n!.mangaSortDateAdded,
- MangaSort.unread => context.l10n!.mangaSortUnread,
- MangaSort.lastRead => context.l10n!.mangaSortLastRead
+ MangaSort.alphabetical => context.l10n.mangaSortAlphabetical,
+ MangaSort.dateAdded => context.l10n.mangaSortDateAdded,
+ MangaSort.unread => context.l10n.mangaSortUnread,
};
}
@@ -88,9 +88,9 @@ enum ChapterSort {
fetchedDate;
String toLocale(BuildContext context) => switch (this) {
- ChapterSort.source => context.l10n!.chapterSortSource,
- ChapterSort.fetchedDate => context.l10n!.chapterSortFetchedDate,
- ChapterSort.uploadDate => context.l10n!.chapterSortUploadDate
+ ChapterSort.source => context.l10n.chapterSortSource,
+ ChapterSort.fetchedDate => context.l10n.chapterSortFetchedDate,
+ ChapterSort.uploadDate => context.l10n.chapterSortUploadDate
};
}
@@ -109,9 +109,9 @@ enum DisplayMode {
const DisplayMode(this.icon);
String toLocale(BuildContext context) => switch (this) {
- DisplayMode.grid => context.l10n!.displayModeGrid,
- DisplayMode.list => context.l10n!.displayModeList,
- DisplayMode.descriptiveList => context.l10n!.displayModeDescriptiveList
+ DisplayMode.grid => context.l10n.displayModeGrid,
+ DisplayMode.list => context.l10n.displayModeList,
+ DisplayMode.descriptiveList => context.l10n.displayModeDescriptiveList
};
}
@@ -139,14 +139,14 @@ enum MangaStatus {
status?.title ?? MangaStatus.unknown.title;
String toLocale(BuildContext context) => switch (this) {
- MangaStatus.unknown => context.l10n!.mangaStatusUnknown,
- MangaStatus.ongoing => context.l10n!.mangaStatusOngoing,
- MangaStatus.completed => context.l10n!.mangaStatusCompleted,
- MangaStatus.licensed => context.l10n!.mangaStatusLicensed,
+ MangaStatus.unknown => context.l10n.mangaStatusUnknown,
+ MangaStatus.ongoing => context.l10n.mangaStatusOngoing,
+ MangaStatus.completed => context.l10n.mangaStatusCompleted,
+ MangaStatus.licensed => context.l10n.mangaStatusLicensed,
MangaStatus.publishingFinished =>
- context.l10n!.mangaStatusPublishingFinished,
- MangaStatus.cancelled => context.l10n!.mangaStatusCancelled,
- MangaStatus.onHiatus => context.l10n!.mangaStatusOnHiatus
+ context.l10n.mangaStatusPublishingFinished,
+ MangaStatus.cancelled => context.l10n.mangaStatusCancelled,
+ MangaStatus.onHiatus => context.l10n.mangaStatusOnHiatus
};
}
@@ -161,8 +161,20 @@ enum SourceType {
final IconData selectedIcon;
String toLocale(BuildContext context) => switch (this) {
- SourceType.latest => context.l10n!.sourceTypeLatest,
- SourceType.popular => context.l10n!.sourceTypePopular,
- SourceType.filter => context.l10n!.sourceTypeFilter
+ SourceType.latest => context.l10n.sourceTypeLatest,
+ SourceType.popular => context.l10n.sourceTypePopular,
+ SourceType.filter => context.l10n.sourceTypeFilter
};
}
+
+@JsonEnum(valueField: 'value')
+enum IncludeOrExclude implements ValueEnum {
+ include("INCLUDE"),
+ exclude("EXCLUDE"),
+ unset("UNSET");
+
+ const IncludeOrExclude(this.value);
+
+ @override
+ final String value;
+}
diff --git a/lib/src/constants/gen/assets.gen.dart b/lib/src/constants/gen/assets.gen.dart
index ae6da291..487cd9cc 100644
--- a/lib/src/constants/gen/assets.gen.dart
+++ b/lib/src/constants/gen/assets.gen.dart
@@ -16,6 +16,7 @@ class $AssetsIconsGen {
AssetGenImage get darkIcon =>
const AssetGenImage('assets/icons/dark_icon.png');
+ /// Directory path: assets/icons/launcher
$AssetsIconsLauncherGen get launcher => const $AssetsIconsLauncherGen();
/// File path: assets/icons/light_icon.png
@@ -69,10 +70,17 @@ class Assets {
}
class AssetGenImage {
- const AssetGenImage(this._assetName);
+ const AssetGenImage(
+ this._assetName, {
+ this.size,
+ this.flavors = const {},
+ });
final String _assetName;
+ final Size? size;
+ final Set flavors;
+
Image image({
Key? key,
AssetBundle? bundle,
diff --git a/lib/src/constants/navigation_bar_data.dart b/lib/src/constants/navigation_bar_data.dart
index c9c54352..62d9f0c5 100644
--- a/lib/src/constants/navigation_bar_data.dart
+++ b/lib/src/constants/navigation_bar_data.dart
@@ -6,65 +6,43 @@
import 'package:flutter/material.dart';
-import '../routes/router_config.dart';
import '../utils/extensions/custom_extensions.dart';
class NavigationBarData {
final String Function(BuildContext context) label;
- final ValueSetter go;
final IconData icon;
final IconData activeIcon;
- final List activeOn;
-
- static int indexWherePathOrZero(path) {
- final index = navList
- .indexWhere((e) => e.activeOn.any((element) => path.contains(element)));
- return index > 0 ? index : 0;
- }
-
static final navList = [
NavigationBarData(
icon: Icons.collections_bookmark_outlined,
activeIcon: Icons.collections_bookmark_rounded,
- label: (context) => context.l10n!.library,
- go: const LibraryRoute().go,
- activeOn: [const LibraryRoute().location],
+ label: (context) => context.l10n.library,
),
NavigationBarData(
icon: Icons.new_releases_outlined,
activeIcon: Icons.new_releases_rounded,
- label: (context) => context.l10n!.updates,
- go: const UpdatesRoute().go,
- activeOn: [const UpdatesRoute().location],
+ label: (context) => context.l10n.updates,
),
NavigationBarData(
icon: Icons.explore_outlined,
activeIcon: Icons.explore_rounded,
- label: (context) => context.l10n!.browse,
- go: const BrowseRoute().go,
- activeOn: [const BrowseRoute().location],
+ label: (context) => context.l10n.browse,
),
NavigationBarData(
icon: Icons.download_outlined,
activeIcon: Icons.download_rounded,
- label: (context) => context.l10n!.downloads,
- go: const DownloadsRoute().go,
- activeOn: [const DownloadsRoute().location],
+ label: (context) => context.l10n.downloads,
),
NavigationBarData(
icon: Icons.more_horiz_outlined,
activeIcon: Icons.more_horiz_rounded,
- label: (context) => context.l10n!.more,
- go: const MoreRoute().go,
- activeOn: [const MoreRoute().location, const SettingsRoute().location],
+ label: (context) => context.l10n.more,
),
];
NavigationBarData({
required this.label,
- required this.go,
required this.icon,
required this.activeIcon,
- required this.activeOn,
});
}
diff --git a/lib/src/constants/quick_open_help_text.dart b/lib/src/constants/quick_open_help_text.dart
index 565307d6..b3cb5f76 100644
--- a/lib/src/constants/quick_open_help_text.dart
+++ b/lib/src/constants/quick_open_help_text.dart
@@ -14,31 +14,31 @@ List getQuickShowHintTextList(BuildContext context) {
QuickSearchResult.helpText(
prefill: '@S',
pattern: '@',
- hintText: context.l10n!.quickSearchSource,
+ hintText: context.l10n.quickSearchSource,
),
QuickSearchResult.helpText(
prefill: '@S/M',
pattern: '@/',
- hintText: context.l10n!.quickSearchSourceManga,
+ hintText: context.l10n.quickSearchSourceManga,
),
QuickSearchResult.helpText(
prefill: '#C',
pattern: '#',
- hintText: context.l10n!.quickSearchCategory,
+ hintText: context.l10n.quickSearchCategory,
),
QuickSearchResult.helpText(
prefill: '#C/M',
pattern: '#/',
- hintText: context.l10n!.quickSearchCategoryManga,
+ hintText: context.l10n.quickSearchCategoryManga,
),
QuickSearchResult.helpText(
prefill: '#C/M:CN',
pattern: '#/:',
- hintText: context.l10n!.quickSearchCategoryMangaChapter,
+ hintText: context.l10n.quickSearchCategoryMangaChapter,
),
QuickSearchResult.helpText(
prefill: 'X',
- hintText: context.l10n!.quickSearchContext,
+ hintText: context.l10n.quickSearchContext,
),
];
}
diff --git a/lib/src/constants/urls.dart b/lib/src/constants/urls.dart
index f42fcee9..6c5c920d 100644
--- a/lib/src/constants/urls.dart
+++ b/lib/src/constants/urls.dart
@@ -15,7 +15,10 @@ enum AppUrls {
sorayomiLatestReleaseApiUrl(
url:
"https://api.github.com/repos/Suwayomi/Tachidesk-Sorayomi/releases/latest",
- );
+ ),
+ flareSolverr(
+ url:
+ "https://github.com/FlareSolverr/FlareSolverr?tab=readme-ov-file#installation");
const AppUrls({required this.url});
diff --git a/lib/src/domain/meta_data.dart b/lib/src/domain/meta_data.dart
new file mode 100644
index 00000000..6db7721d
--- /dev/null
+++ b/lib/src/domain/meta_data.dart
@@ -0,0 +1,15 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'meta_data.freezed.dart';
+part 'meta_data.g.dart';
+
+@freezed
+class MetaData with _$MetaData {
+ factory MetaData({
+ String? key,
+ String? value,
+ }) = _MetaData;
+
+ factory MetaData.fromJson(Map json) =>
+ _$MetaDataFromJson(json);
+}
diff --git a/lib/src/domain/meta_data.freezed.dart b/lib/src/domain/meta_data.freezed.dart
new file mode 100644
index 00000000..31692026
--- /dev/null
+++ b/lib/src/domain/meta_data.freezed.dart
@@ -0,0 +1,179 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'meta_data.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
+
+MetaData _$MetaDataFromJson(Map json) {
+ return _MetaData.fromJson(json);
+}
+
+/// @nodoc
+mixin _$MetaData {
+ String? get key => throw _privateConstructorUsedError;
+ String? get value => throw _privateConstructorUsedError;
+
+ /// Serializes this MetaData to a JSON map.
+ Map toJson() => throw _privateConstructorUsedError;
+
+ /// Create a copy of MetaData
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $MetaDataCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $MetaDataCopyWith<$Res> {
+ factory $MetaDataCopyWith(MetaData value, $Res Function(MetaData) then) =
+ _$MetaDataCopyWithImpl<$Res, MetaData>;
+ @useResult
+ $Res call({String? key, String? value});
+}
+
+/// @nodoc
+class _$MetaDataCopyWithImpl<$Res, $Val extends MetaData>
+ implements $MetaDataCopyWith<$Res> {
+ _$MetaDataCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of MetaData
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? key = freezed,
+ Object? value = freezed,
+ }) {
+ return _then(_value.copyWith(
+ key: freezed == key
+ ? _value.key
+ : key // ignore: cast_nullable_to_non_nullable
+ as String?,
+ value: freezed == value
+ ? _value.value
+ : value // ignore: cast_nullable_to_non_nullable
+ as String?,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$MetaDataImplCopyWith<$Res>
+ implements $MetaDataCopyWith<$Res> {
+ factory _$$MetaDataImplCopyWith(
+ _$MetaDataImpl value, $Res Function(_$MetaDataImpl) then) =
+ __$$MetaDataImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call({String? key, String? value});
+}
+
+/// @nodoc
+class __$$MetaDataImplCopyWithImpl<$Res>
+ extends _$MetaDataCopyWithImpl<$Res, _$MetaDataImpl>
+ implements _$$MetaDataImplCopyWith<$Res> {
+ __$$MetaDataImplCopyWithImpl(
+ _$MetaDataImpl _value, $Res Function(_$MetaDataImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of MetaData
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? key = freezed,
+ Object? value = freezed,
+ }) {
+ return _then(_$MetaDataImpl(
+ key: freezed == key
+ ? _value.key
+ : key // ignore: cast_nullable_to_non_nullable
+ as String?,
+ value: freezed == value
+ ? _value.value
+ : value // ignore: cast_nullable_to_non_nullable
+ as String?,
+ ));
+ }
+}
+
+/// @nodoc
+@JsonSerializable()
+class _$MetaDataImpl implements _MetaData {
+ _$MetaDataImpl({this.key, this.value});
+
+ factory _$MetaDataImpl.fromJson(Map json) =>
+ _$$MetaDataImplFromJson(json);
+
+ @override
+ final String? key;
+ @override
+ final String? value;
+
+ @override
+ String toString() {
+ return 'MetaData(key: $key, value: $value)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$MetaDataImpl &&
+ (identical(other.key, key) || other.key == key) &&
+ (identical(other.value, value) || other.value == value));
+ }
+
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ int get hashCode => Object.hash(runtimeType, key, value);
+
+ /// Create a copy of MetaData
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$MetaDataImplCopyWith<_$MetaDataImpl> get copyWith =>
+ __$$MetaDataImplCopyWithImpl<_$MetaDataImpl>(this, _$identity);
+
+ @override
+ Map toJson() {
+ return _$$MetaDataImplToJson(
+ this,
+ );
+ }
+}
+
+abstract class _MetaData implements MetaData {
+ factory _MetaData({final String? key, final String? value}) = _$MetaDataImpl;
+
+ factory _MetaData.fromJson(Map json) =
+ _$MetaDataImpl.fromJson;
+
+ @override
+ String? get key;
+ @override
+ String? get value;
+
+ /// Create a copy of MetaData
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$MetaDataImplCopyWith<_$MetaDataImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/lib/src/domain/meta_data.g.dart b/lib/src/domain/meta_data.g.dart
new file mode 100644
index 00000000..c767db2e
--- /dev/null
+++ b/lib/src/domain/meta_data.g.dart
@@ -0,0 +1,19 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'meta_data.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+_$MetaDataImpl _$$MetaDataImplFromJson(Map json) =>
+ _$MetaDataImpl(
+ key: json['key'] as String?,
+ value: json['value'] as String?,
+ );
+
+Map _$$MetaDataImplToJson(_$MetaDataImpl instance) =>
+ {
+ 'key': instance.key,
+ 'value': instance.value,
+ };
diff --git a/lib/src/domain/page.dart b/lib/src/domain/page.dart
new file mode 100644
index 00000000..7f457307
--- /dev/null
+++ b/lib/src/domain/page.dart
@@ -0,0 +1,26 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+import 'page_info.dart';
+
+part 'page.freezed.dart';
+
+@freezed
+class Page with _$Page {
+ Page._();
+ factory Page({
+ List? nodes,
+ PageInfo? pageInfo,
+ int? totalCount,
+ }) = _Page;
+
+ Page appendPage(Page newPage) {
+ return copyWith(
+ nodes: [...?nodes, ...?newPage.nodes],
+ pageInfo: pageInfo?.copyWith(
+ endCursor: newPage.pageInfo?.endCursor ?? pageInfo?.endCursor,
+ hasNextPage: newPage.pageInfo?.hasNextPage ?? pageInfo?.hasNextPage,
+ ),
+ totalCount: newPage.totalCount ?? totalCount,
+ );
+ }
+}
diff --git a/lib/src/domain/page.freezed.dart b/lib/src/domain/page.freezed.dart
new file mode 100644
index 00000000..b601e6b8
--- /dev/null
+++ b/lib/src/domain/page.freezed.dart
@@ -0,0 +1,208 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'page.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
+
+/// @nodoc
+mixin _$Page {
+ List? get nodes => throw _privateConstructorUsedError;
+ PageInfo? get pageInfo => throw _privateConstructorUsedError;
+ int? get totalCount => throw _privateConstructorUsedError;
+
+ /// Create a copy of Page
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $PageCopyWith> get copyWith => throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $PageCopyWith {
+ factory $PageCopyWith(Page value, $Res Function(Page) then) =
+ _$PageCopyWithImpl>;
+ @useResult
+ $Res call({List? nodes, PageInfo? pageInfo, int? totalCount});
+
+ $PageInfoCopyWith<$Res>? get pageInfo;
+}
+
+/// @nodoc
+class _$PageCopyWithImpl>
+ implements $PageCopyWith {
+ _$PageCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of Page
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? nodes = freezed,
+ Object? pageInfo = freezed,
+ Object? totalCount = freezed,
+ }) {
+ return _then(_value.copyWith(
+ nodes: freezed == nodes
+ ? _value.nodes
+ : nodes // ignore: cast_nullable_to_non_nullable
+ as List?,
+ pageInfo: freezed == pageInfo
+ ? _value.pageInfo
+ : pageInfo // ignore: cast_nullable_to_non_nullable
+ as PageInfo?,
+ totalCount: freezed == totalCount
+ ? _value.totalCount
+ : totalCount // ignore: cast_nullable_to_non_nullable
+ as int?,
+ ) as $Val);
+ }
+
+ /// Create a copy of Page
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @pragma('vm:prefer-inline')
+ $PageInfoCopyWith<$Res>? get pageInfo {
+ if (_value.pageInfo == null) {
+ return null;
+ }
+
+ return $PageInfoCopyWith<$Res>(_value.pageInfo!, (value) {
+ return _then(_value.copyWith(pageInfo: value) as $Val);
+ });
+ }
+}
+
+/// @nodoc
+abstract class _$$PageImplCopyWith implements $PageCopyWith {
+ factory _$$PageImplCopyWith(
+ _$PageImpl value, $Res Function(_$PageImpl) then) =
+ __$$PageImplCopyWithImpl;
+ @override
+ @useResult
+ $Res call({List? nodes, PageInfo? pageInfo, int? totalCount});
+
+ @override
+ $PageInfoCopyWith<$Res>? get pageInfo;
+}
+
+/// @nodoc
+class __$$PageImplCopyWithImpl
+ extends _$PageCopyWithImpl>
+ implements _$$PageImplCopyWith {
+ __$$PageImplCopyWithImpl(
+ _$PageImpl _value, $Res Function(_$PageImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of Page
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? nodes = freezed,
+ Object? pageInfo = freezed,
+ Object? totalCount = freezed,
+ }) {
+ return _then(_$PageImpl(
+ nodes: freezed == nodes
+ ? _value._nodes
+ : nodes // ignore: cast_nullable_to_non_nullable
+ as List?,
+ pageInfo: freezed == pageInfo
+ ? _value.pageInfo
+ : pageInfo // ignore: cast_nullable_to_non_nullable
+ as PageInfo?,
+ totalCount: freezed == totalCount
+ ? _value.totalCount
+ : totalCount // ignore: cast_nullable_to_non_nullable
+ as int?,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$PageImpl extends _Page {
+ _$PageImpl({final List? nodes, this.pageInfo, this.totalCount})
+ : _nodes = nodes,
+ super._();
+
+ final List? _nodes;
+ @override
+ List? get nodes {
+ final value = _nodes;
+ if (value == null) return null;
+ if (_nodes is EqualUnmodifiableListView) return _nodes;
+ // ignore: implicit_dynamic_type
+ return EqualUnmodifiableListView(value);
+ }
+
+ @override
+ final PageInfo? pageInfo;
+ @override
+ final int? totalCount;
+
+ @override
+ String toString() {
+ return 'Page<$T>(nodes: $nodes, pageInfo: $pageInfo, totalCount: $totalCount)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$PageImpl &&
+ const DeepCollectionEquality().equals(other._nodes, _nodes) &&
+ (identical(other.pageInfo, pageInfo) ||
+ other.pageInfo == pageInfo) &&
+ (identical(other.totalCount, totalCount) ||
+ other.totalCount == totalCount));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType,
+ const DeepCollectionEquality().hash(_nodes), pageInfo, totalCount);
+
+ /// Create a copy of Page
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$PageImplCopyWith> get copyWith =>
+ __$$PageImplCopyWithImpl>(this, _$identity);
+}
+
+abstract class _Page extends Page {
+ factory _Page(
+ {final List? nodes,
+ final PageInfo? pageInfo,
+ final int? totalCount}) = _$PageImpl;
+ _Page._() : super._();
+
+ @override
+ List? get nodes;
+ @override
+ PageInfo? get pageInfo;
+ @override
+ int? get totalCount;
+
+ /// Create a copy of Page
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$PageImplCopyWith> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/lib/src/domain/page_info.dart b/lib/src/domain/page_info.dart
new file mode 100644
index 00000000..56ed99a7
--- /dev/null
+++ b/lib/src/domain/page_info.dart
@@ -0,0 +1,13 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'page_info.freezed.dart';
+
+@freezed
+class PageInfo with _$PageInfo {
+ factory PageInfo({
+ String? endCursor,
+ bool? hasNextPage,
+ bool? hasPreviousPage,
+ String? startCursor,
+ }) = _PageInfo;
+}
diff --git a/lib/src/domain/page_info.freezed.dart b/lib/src/domain/page_info.freezed.dart
new file mode 100644
index 00000000..18456ed0
--- /dev/null
+++ b/lib/src/domain/page_info.freezed.dart
@@ -0,0 +1,211 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'page_info.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
+
+/// @nodoc
+mixin _$PageInfo {
+ String? get endCursor => throw _privateConstructorUsedError;
+ bool? get hasNextPage => throw _privateConstructorUsedError;
+ bool? get hasPreviousPage => throw _privateConstructorUsedError;
+ String? get startCursor => throw _privateConstructorUsedError;
+
+ /// Create a copy of PageInfo
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $PageInfoCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $PageInfoCopyWith<$Res> {
+ factory $PageInfoCopyWith(PageInfo value, $Res Function(PageInfo) then) =
+ _$PageInfoCopyWithImpl<$Res, PageInfo>;
+ @useResult
+ $Res call(
+ {String? endCursor,
+ bool? hasNextPage,
+ bool? hasPreviousPage,
+ String? startCursor});
+}
+
+/// @nodoc
+class _$PageInfoCopyWithImpl<$Res, $Val extends PageInfo>
+ implements $PageInfoCopyWith<$Res> {
+ _$PageInfoCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of PageInfo
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? endCursor = freezed,
+ Object? hasNextPage = freezed,
+ Object? hasPreviousPage = freezed,
+ Object? startCursor = freezed,
+ }) {
+ return _then(_value.copyWith(
+ endCursor: freezed == endCursor
+ ? _value.endCursor
+ : endCursor // ignore: cast_nullable_to_non_nullable
+ as String?,
+ hasNextPage: freezed == hasNextPage
+ ? _value.hasNextPage
+ : hasNextPage // ignore: cast_nullable_to_non_nullable
+ as bool?,
+ hasPreviousPage: freezed == hasPreviousPage
+ ? _value.hasPreviousPage
+ : hasPreviousPage // ignore: cast_nullable_to_non_nullable
+ as bool?,
+ startCursor: freezed == startCursor
+ ? _value.startCursor
+ : startCursor // ignore: cast_nullable_to_non_nullable
+ as String?,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$PageInfoImplCopyWith<$Res>
+ implements $PageInfoCopyWith<$Res> {
+ factory _$$PageInfoImplCopyWith(
+ _$PageInfoImpl value, $Res Function(_$PageInfoImpl) then) =
+ __$$PageInfoImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call(
+ {String? endCursor,
+ bool? hasNextPage,
+ bool? hasPreviousPage,
+ String? startCursor});
+}
+
+/// @nodoc
+class __$$PageInfoImplCopyWithImpl<$Res>
+ extends _$PageInfoCopyWithImpl<$Res, _$PageInfoImpl>
+ implements _$$PageInfoImplCopyWith<$Res> {
+ __$$PageInfoImplCopyWithImpl(
+ _$PageInfoImpl _value, $Res Function(_$PageInfoImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of PageInfo
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? endCursor = freezed,
+ Object? hasNextPage = freezed,
+ Object? hasPreviousPage = freezed,
+ Object? startCursor = freezed,
+ }) {
+ return _then(_$PageInfoImpl(
+ endCursor: freezed == endCursor
+ ? _value.endCursor
+ : endCursor // ignore: cast_nullable_to_non_nullable
+ as String?,
+ hasNextPage: freezed == hasNextPage
+ ? _value.hasNextPage
+ : hasNextPage // ignore: cast_nullable_to_non_nullable
+ as bool?,
+ hasPreviousPage: freezed == hasPreviousPage
+ ? _value.hasPreviousPage
+ : hasPreviousPage // ignore: cast_nullable_to_non_nullable
+ as bool?,
+ startCursor: freezed == startCursor
+ ? _value.startCursor
+ : startCursor // ignore: cast_nullable_to_non_nullable
+ as String?,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$PageInfoImpl implements _PageInfo {
+ _$PageInfoImpl(
+ {this.endCursor,
+ this.hasNextPage,
+ this.hasPreviousPage,
+ this.startCursor});
+
+ @override
+ final String? endCursor;
+ @override
+ final bool? hasNextPage;
+ @override
+ final bool? hasPreviousPage;
+ @override
+ final String? startCursor;
+
+ @override
+ String toString() {
+ return 'PageInfo(endCursor: $endCursor, hasNextPage: $hasNextPage, hasPreviousPage: $hasPreviousPage, startCursor: $startCursor)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$PageInfoImpl &&
+ (identical(other.endCursor, endCursor) ||
+ other.endCursor == endCursor) &&
+ (identical(other.hasNextPage, hasNextPage) ||
+ other.hasNextPage == hasNextPage) &&
+ (identical(other.hasPreviousPage, hasPreviousPage) ||
+ other.hasPreviousPage == hasPreviousPage) &&
+ (identical(other.startCursor, startCursor) ||
+ other.startCursor == startCursor));
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ runtimeType, endCursor, hasNextPage, hasPreviousPage, startCursor);
+
+ /// Create a copy of PageInfo
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$PageInfoImplCopyWith<_$PageInfoImpl> get copyWith =>
+ __$$PageInfoImplCopyWithImpl<_$PageInfoImpl>(this, _$identity);
+}
+
+abstract class _PageInfo implements PageInfo {
+ factory _PageInfo(
+ {final String? endCursor,
+ final bool? hasNextPage,
+ final bool? hasPreviousPage,
+ final String? startCursor}) = _$PageInfoImpl;
+
+ @override
+ String? get endCursor;
+ @override
+ bool? get hasNextPage;
+ @override
+ bool? get hasPreviousPage;
+ @override
+ String? get startCursor;
+
+ /// Create a copy of PageInfo
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$PageInfoImplCopyWith<_$PageInfoImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/lib/src/domain/page_request.dart b/lib/src/domain/page_request.dart
new file mode 100644
index 00000000..98134075
--- /dev/null
+++ b/lib/src/domain/page_request.dart
@@ -0,0 +1,15 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'page_request.freezed.dart';
+
+@freezed
+class PageRequest with _$PageRequest {
+ factory PageRequest({
+ required int page,
+ @Default(10) int size,
+ }) = _PageRequest;
+
+ const PageRequest._();
+
+ int get offset => page * size;
+}
diff --git a/lib/src/domain/page_request.freezed.dart b/lib/src/domain/page_request.freezed.dart
new file mode 100644
index 00000000..5eeade5a
--- /dev/null
+++ b/lib/src/domain/page_request.freezed.dart
@@ -0,0 +1,162 @@
+// coverage:ignore-file
+// GENERATED CODE - DO NOT MODIFY BY HAND
+// ignore_for_file: type=lint
+// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
+
+part of 'page_request.dart';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity(T value) => value;
+
+final _privateConstructorUsedError = UnsupportedError(
+ 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
+
+/// @nodoc
+mixin _$PageRequest {
+ int get page => throw _privateConstructorUsedError;
+ int get size => throw _privateConstructorUsedError;
+
+ /// Create a copy of PageRequest
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ $PageRequestCopyWith get copyWith =>
+ throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $PageRequestCopyWith<$Res> {
+ factory $PageRequestCopyWith(
+ PageRequest value, $Res Function(PageRequest) then) =
+ _$PageRequestCopyWithImpl<$Res, PageRequest>;
+ @useResult
+ $Res call({int page, int size});
+}
+
+/// @nodoc
+class _$PageRequestCopyWithImpl<$Res, $Val extends PageRequest>
+ implements $PageRequestCopyWith<$Res> {
+ _$PageRequestCopyWithImpl(this._value, this._then);
+
+ // ignore: unused_field
+ final $Val _value;
+ // ignore: unused_field
+ final $Res Function($Val) _then;
+
+ /// Create a copy of PageRequest
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? page = null,
+ Object? size = null,
+ }) {
+ return _then(_value.copyWith(
+ page: null == page
+ ? _value.page
+ : page // ignore: cast_nullable_to_non_nullable
+ as int,
+ size: null == size
+ ? _value.size
+ : size // ignore: cast_nullable_to_non_nullable
+ as int,
+ ) as $Val);
+ }
+}
+
+/// @nodoc
+abstract class _$$PageRequestImplCopyWith<$Res>
+ implements $PageRequestCopyWith<$Res> {
+ factory _$$PageRequestImplCopyWith(
+ _$PageRequestImpl value, $Res Function(_$PageRequestImpl) then) =
+ __$$PageRequestImplCopyWithImpl<$Res>;
+ @override
+ @useResult
+ $Res call({int page, int size});
+}
+
+/// @nodoc
+class __$$PageRequestImplCopyWithImpl<$Res>
+ extends _$PageRequestCopyWithImpl<$Res, _$PageRequestImpl>
+ implements _$$PageRequestImplCopyWith<$Res> {
+ __$$PageRequestImplCopyWithImpl(
+ _$PageRequestImpl _value, $Res Function(_$PageRequestImpl) _then)
+ : super(_value, _then);
+
+ /// Create a copy of PageRequest
+ /// with the given fields replaced by the non-null parameter values.
+ @pragma('vm:prefer-inline')
+ @override
+ $Res call({
+ Object? page = null,
+ Object? size = null,
+ }) {
+ return _then(_$PageRequestImpl(
+ page: null == page
+ ? _value.page
+ : page // ignore: cast_nullable_to_non_nullable
+ as int,
+ size: null == size
+ ? _value.size
+ : size // ignore: cast_nullable_to_non_nullable
+ as int,
+ ));
+ }
+}
+
+/// @nodoc
+
+class _$PageRequestImpl extends _PageRequest {
+ _$PageRequestImpl({required this.page, this.size = 10}) : super._();
+
+ @override
+ final int page;
+ @override
+ @JsonKey()
+ final int size;
+
+ @override
+ String toString() {
+ return 'PageRequest(page: $page, size: $size)';
+ }
+
+ @override
+ bool operator ==(Object other) {
+ return identical(this, other) ||
+ (other.runtimeType == runtimeType &&
+ other is _$PageRequestImpl &&
+ (identical(other.page, page) || other.page == page) &&
+ (identical(other.size, size) || other.size == size));
+ }
+
+ @override
+ int get hashCode => Object.hash(runtimeType, page, size);
+
+ /// Create a copy of PageRequest
+ /// with the given fields replaced by the non-null parameter values.
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ @override
+ @pragma('vm:prefer-inline')
+ _$$PageRequestImplCopyWith<_$PageRequestImpl> get copyWith =>
+ __$$PageRequestImplCopyWithImpl<_$PageRequestImpl>(this, _$identity);
+}
+
+abstract class _PageRequest extends PageRequest {
+ factory _PageRequest({required final int page, final int size}) =
+ _$PageRequestImpl;
+ _PageRequest._() : super._();
+
+ @override
+ int get page;
+ @override
+ int get size;
+
+ /// Create a copy of PageRequest
+ /// with the given fields replaced by the non-null parameter values.
+ @override
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ _$$PageRequestImplCopyWith<_$PageRequestImpl> get copyWith =>
+ throw _privateConstructorUsedError;
+}
diff --git a/lib/src/features/about/data/about_repository.dart b/lib/src/features/about/data/about_repository.dart
index 1739018c..6bf43fc0 100644
--- a/lib/src/features/about/data/about_repository.dart
+++ b/lib/src/features/about/data/about_repository.dart
@@ -4,56 +4,54 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+import 'dart:convert';
+
import 'package:dio/dio.dart';
+import 'package:ferry/ferry.dart';
+import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
-import '../../../constants/endpoints.dart';
import '../../../constants/urls.dart';
import '../../../global_providers/global_providers.dart';
import '../../../utils/extensions/custom_extensions.dart';
-import '../../../utils/storage/dio/dio_client.dart';
-import '../domain/about/about_model.dart';
-import '../domain/server_update/server_update_model.dart';
+import '../domain/about/about_dto.dart';
+import '../domain/server_update/graphql/fragment.dart';
+import '../domain/server_update/server_update.dart';
import '../presentation/about/controllers/about_controller.dart';
+import 'graphql/__generated__/query.data.gql.dart';
+import 'graphql/query.dart';
part 'about_repository.g.dart';
class AboutRepository {
- final DioClient dioClient;
+ final Client ferryClient;
final PackageInfo packageInfo;
AboutRepository({
- required this.dioClient,
+ required this.ferryClient,
required this.packageInfo,
});
- Future getAbout({CancelToken? cancelToken}) async {
- return (await dioClient.get(
- SettingsUrl.about,
- decoder: (e) => e is Map ? About.fromJson(e) : null,
- cancelToken: cancelToken,
- ))
- .data;
- }
+ Stream getAbout() => ferryClient.fetch(
+ AboutQuery.getAboutQuery,
+ (GAboutData data) => data.aboutServer,
+ );
- Future?> checkServerUpdate() async {
- return (await dioClient.get, ServerUpdate>(
- SettingsUrl.checkServerUpdate,
- decoder: (e) =>
- e is Map ? ServerUpdate.fromJson(e) : ServerUpdate(),
- ))
- .data;
- }
+ Future?> checkServerUpdate() => ferryClient
+ .fetch(
+ AboutQuery.serverUpdateQuery,
+ (data) =>
+ data.checkForServerUpdates.map((update) => update.toDto).toList(),
+ )
+ .first;
Future> checkUpdate({CancelToken? cancelToken}) async {
final gitResponse = await AsyncValue.guard