add source extension

This commit is contained in:
kodjodevf 2023-04-05 18:01:59 +01:00
parent da1d403c75
commit b433d2fbc6
24 changed files with 1101 additions and 190 deletions

View file

@ -2,4 +2,6 @@ class HiveConstant {
static String get hiveBoxManga => "manga_box";
static String get hiveBoxMangaInfo => "manga_box_info";
static String get hiveBoxMangaHistory => "_manga_box_history_";
static String get hiveBoxMangaSource => "_manga_box_source_";
static String get hiveBoxMangaFilter => "_manga_box_filter_";
}

View file

@ -6,16 +6,19 @@ import 'package:mangayomi/constant.dart';
import 'package:mangayomi/models/manga_history.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/router/router.dart';
import 'package:mangayomi/source/source_model.dart';
void main() async {
await Hive.initFlutter();
Hive.registerAdapter(ModelMangaAdapter());
Hive.registerAdapter(MangaHistoryModelAdapter());
await Hive.openBox<ModelManga>(
HiveConstant.hiveBoxManga,
);
Hive.registerAdapter(SourceModelAdapter());
Hive.registerAdapter(TypeSourceAdapter());
await Hive.openBox<ModelManga>(HiveConstant.hiveBoxManga);
await Hive.openBox<MangaHistoryModel>(HiveConstant.hiveBoxMangaHistory);
await Hive.openBox<SourceModel>(HiveConstant.hiveBoxMangaSource);
await Hive.openBox(HiveConstant.hiveBoxMangaInfo);
await Hive.openBox(HiveConstant.hiveBoxMangaFilter);
runApp(const ProviderScope(child: MyApp()));
}

View file

@ -3,6 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/constant.dart';
import 'package:mangayomi/models/manga_history.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/source/source_model.dart';
final hiveBoxManga = Provider<Box<ModelManga>>((ref) {
return Hive.box<ModelManga>(HiveConstant.hiveBoxManga);
@ -14,3 +15,11 @@ final hiveBoxMangaInfo = Provider<Box>((ref) {
final hiveBoxMangaHistory = Provider<Box<MangaHistoryModel>>((ref) {
return Hive.box<MangaHistoryModel>(HiveConstant.hiveBoxMangaHistory);
});
final hiveBoxMangaFilterProvider = Provider<Box>((ref) {
return Hive.box(HiveConstant.hiveBoxMangaFilter);
});
final hiveBoxMangaSourceProvider = Provider<Box<SourceModel>>((ref) {
return Hive.box<SourceModel>(HiveConstant.hiveBoxMangaSource);
});

View file

@ -5,6 +5,7 @@ import 'package:mangayomi/models/manga_reader.dart';
import 'package:mangayomi/models/manga_type.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/views/browse/browse_screen.dart';
import 'package:mangayomi/views/browse/extension/extension_lang.dart';
import 'package:mangayomi/views/general/general_screen.dart';
import 'package:mangayomi/views/history/history_screen.dart';
import 'package:mangayomi/views/library/library_screen.dart';
@ -135,6 +136,19 @@ class AsyncRouterNotifier extends ChangeNotifier {
);
},
),
GoRoute(
path: "/extensionLang",
name: "extensionLang",
builder: (context, state) {
return const ExtensionsLang();
},
pageBuilder: (context, state) {
return CustomTransition(
key: state.pageKey,
child: const ExtensionsLang(),
);
},
),
];
}

View file

@ -38,8 +38,9 @@ Future<GetMangaChapterUrlModel> getMangaChapterUrl(
if (hiveUrl.isNotEmpty) {
urll = hiveUrl;
}
//mangahere
/***********/
/*mangahere*/
/***********/
else if (source == 'mangahere') {
JavascriptRuntime? flutterJs;
flutterJs = getJavascriptRuntime();

View file

@ -7,7 +7,7 @@ part of 'get_manga_chapter_url.dart';
// **************************************************************************
String _$getMangaChapterUrlHash() =>
r'acdd4b5eacb246441b55d1c19614032b789b0828';
r'bd696a5ff424b0396cd67d08325945f6ac00093b';
/// Copied from Dart SDK
class _SystemHash {

383
lib/source/source_list.dart Normal file
View file

@ -0,0 +1,383 @@
import 'package:mangayomi/source/source_model.dart';
List<SourceModel> sourcesList = [
SourceModel(
sourceName: "MangaHere",
url: "http://www.mangahere.cc",
lang: "en",
typeSource: TypeSource.single),
SourceModel(
sourceName: "MangaSee",
url: "https://mangasee123.com",
lang: "en",
typeSource: TypeSource.single),
SourceModel(
sourceName: "ManngaKawaii",
url: "https://www.mangakawaii.io",
lang: "fr",
typeSource: TypeSource.single),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'en',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'ar',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'pt',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'pt-br',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'it',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'ru',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'es',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'es-419',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'id',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'hi',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'de',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'ja',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'tr',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'pl',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'zh',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'zh-hk',
typeSource: TypeSource.comick),
SourceModel(
sourceName: 'Comick',
url: 'https://api.comick.fun/',
lang: 'fr',
typeSource: TypeSource.comick),
SourceModel(
sourceName: "KomikLab",
url: "https://komiklab.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "AnimatedGlitchedScans",
url: "https://anigliscans.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "ArenaScans",
url: "https://arenascans.net",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "AzureScans",
url: "https://azuremanga.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "Boosei",
url: "https://boosei.net",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "Clayrer",
url: "https://clayrer.net",
lang: "es",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "CosmicScans",
url: "https://cosmicscans.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "DiskusScan",
url: "https://diskusscan.com",
lang: "pt-BR",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "DuniaKomik",
url: "https://duniakomik.id",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "FlameScans",
url: "https://flamescans.fr",
lang: "fr",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "GremoryMangas",
url: "https://gremorymangas.com",
lang: "es",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "InfernalVoidScans",
url: "https://void-scans.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "Kiryuu",
url: "https://kiryuu.id",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "KomikIndo",
url: "https://komikindo.co",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "KomikMama",
url: "https://komikmama.co",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "Komiku",
url: "https://komiku.com",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "KumaScans (Kuma Translation)",
url: "https://kumascans.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "MangaKita",
url: "https://mangakita.net",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "MangaTale",
url: "https://mangatale.co",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "MangaYaro",
url: "https://mangayaro.net",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "MangKomik",
url: "https://mangkomik.net",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "MangásChan",
url: "https://mangaschan.com",
lang: "pt-BR",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "ManhwaFreak",
url: "https://manhwafreak.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "ManhwaList",
url: "https://manhwalist.in",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "MasterKomik",
url: "https://masterkomik.com",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "Nekomik",
url: "https://nekomik.com",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "PhantomScans",
url: "https://phantomscans.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "PhoenixFansub",
url: "https://phoenixfansub.com",
lang: "es",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "PiScans",
url: "https://piscans.in",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "Rawkuma",
url: "https://rawkuma.com/",
lang: "ja",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "Readkomik",
url: "https://readkomik.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "SuryaScans",
url: "https://suryascans.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "SushiScan",
url: "https://sushiscan.ru",
lang: "fr",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "TsundokuTraduções",
url: "https://tsundoku.com.br",
lang: "pt-BR",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "TukangKomik",
url: "https://tukangkomik.id",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "WestManga",
url: "https://westmanga.info",
lang: "id",
typeSource: TypeSource.mangathemesia),
SourceModel(
sourceName: "xCaliBRScans",
url: "https://xcalibrscans.com",
lang: "en",
typeSource: TypeSource.mangathemesia),
];
// List<SourceModel> sourcesList = [
// SourceModel(Source("MangaHere", "http://www.mangahere.cc", "en", TypeSource.single)),
// SourceModel(Source("MangaSee", "https://mangasee123.com", "en", typeSource:TypeSource.single)),
// SourceModel(Source(
// "ManngaKawaii", "https://www.mangakawaii.io", "fr", typeSource:TypeSource.single)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'en', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'ar', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'pt', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'pt-br', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'it', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'ru', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'es', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'es-419', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'id', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'hi', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'de', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'ja', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'tr', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'pl', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'zh', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'zh-hk', TypeSource.comick)),
// SourceModel(Source('Comick', 'https://api.comick.fun/', 'fr', TypeSource.comick)),
// SourceModel(Source(
// "KomikLab", "https://komiklab.com", "en", TypeSource.mangathemesia)),
// SourceModel(Source("AnimatedGlitchedScans", "https://anigliscans.com", "en",
// TypeSource.mangathemesia)),
// SourceModel(Source(
// "ArenaScans", "https://arenascans.net", "en", TypeSource.mangathemesia)),
// SourceModel(Source(
// "AzureScans", "https://azuremanga.com", "en", TypeSource.mangathemesia)),
// SourceModel(Source("Boosei", "https://boosei.net", "id", TypeSource.mangathemesia)),
// SourceModel(Source("Clayrer", "https://clayrer.net", "es", TypeSource.mangathemesia)),
// SourceModel(Source(
// "CosmicScans", "https://cosmicscans.com", "en", TypeSource.mangathemesia)),
// SourceModel(Source("DiskusScan", "https://diskusscan.com", "pt-BR",
// TypeSource.mangathemesia)),
// SourceModel(Source(
// "DuniaKomik", "https://duniakomik.id", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "FlameScans", "https://flamescans.fr", "fr", TypeSource.mangathemesia)),
// SourceModel(Source("GremoryMangas", "https://gremorymangas.com", "es",
// TypeSource.mangathemesia)),
// SourceModel(Source("InfernalVoidScans", "https://void-scans.com", "en",
// TypeSource.mangathemesia)),
// SourceModel(Source("Kiryuu", "https://kiryuu.id", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "KomikIndo", "https://komikindo.co", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "KomikMama", "https://komikmama.co", "id", TypeSource.mangathemesia)),
// SourceModel(Source("Komiku", "https://komiku.com", "id", TypeSource.mangathemesia)),
// SourceModel(Source("KumaScans (Kuma Translation)", "https://kumascans.com", "en",
// TypeSource.mangathemesia)),
// SourceModel(Source(
// "MangaKita", "https://mangakita.net", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "MangaTale", "https://mangatale.co", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "MangaYaro", "https://mangayaro.net", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "MangKomik", "https://mangkomik.net", "id", TypeSource.mangathemesia)),
// SourceModel(Source("MangásChan", "https://mangaschan.com", "pt-BR",
// TypeSource.mangathemesia)),
// SourceModel(Source(
// "ManhwaFreak", "https://manhwafreak.com", "en", TypeSource.mangathemesia)),
// SourceModel(Source(
// "ManhwaList", "https://manhwalist.in", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "MasterKomik", "https://masterkomik.com", "id", TypeSource.mangathemesia)),
// SourceModel(Source("Nekomik", "https://nekomik.com", "id", TypeSource.mangathemesia)),
// SourceModel(Source("PhantomScans", "https://phantomscans.com", "en",
// TypeSource.mangathemesia)),
// SourceModel(Source("PhoenixFansub", "https://phoenixfansub.com", "es",
// TypeSource.mangathemesia)),
// SourceModel(Source("PiScans", "https://piscans.in", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "Rawkuma", "https://rawkuma.com/", "ja", TypeSource.mangathemesia)),
// SourceModel(Source(
// "Readkomik", "https://readkomik.com", "en", TypeSource.mangathemesia)),
// SourceModel(Source(
// "SuryaScans", "https://suryascans.com", "en", TypeSource.mangathemesia)),
// SourceModel(Source(
// "SushiScan", "https://sushiscan.ru", "fr", TypeSource.mangathemesia)),
// SourceModel(Source("TsundokuTraduções", "https://tsundoku.com.br", "pt-BR",
// TypeSource.mangathemesia)),
// SourceModel(Source(
// "TukangKomik", "https://tukangkomik.id", "id", TypeSource.mangathemesia)),
// SourceModel(Source(
// "WestManga", "https://westmanga.info", "id", TypeSource.mangathemesia)),
// SourceModel(Source("xCaliBRScans", "https://xcalibrscans.com", "en",
// TypeSource.mangathemesia)),
// ];

View file

@ -0,0 +1,78 @@
import 'package:hive/hive.dart';
part 'source_model.g.dart';
@HiveType(typeId: 3)
class SourceModel extends HiveObject {
@HiveField(0)
final String sourceName;
@HiveField(1)
final String url;
@HiveField(2)
final String lang;
@HiveField(3, defaultValue: true)
final bool isActive;
@HiveField(4, defaultValue: false)
final bool isAdded;
@HiveField(5, defaultValue: false)
final bool isNsfw;
@HiveField(6)
final TypeSource typeSource;
SourceModel({
required this.sourceName,
required this.url,
required this.lang,
required this.typeSource,
this.isActive = true,
this.isAdded = false,
this.isNsfw = false,
});
}
@HiveType(typeId: 4)
enum TypeSource {
@HiveField(1)
single,
@HiveField(2)
mangathemesia,
@HiveField(3)
comick
}
// @HiveType(typeId: 3)
// class SourceModel {
// @HiveField(0, defaultValue: true)
// final bool isActive;
// @HiveField(1)
// final Source source;
// @HiveField(2)
// SourceModel(
// this.source, {
// this.isActive = true,
// });
// }
// @HiveType(typeId: 4)
// class Source {
// @HiveField(0)
// final String sourceName;
// @HiveField(1)
// final String url;
// @HiveField(2)
// final String lang;
// @HiveField(3, defaultValue: true)
// final bool isActive;
// @HiveField(4, defaultValue: false)
// final bool isAdded;
// @HiveField(5, defaultValue: false)
// final bool isNsfw;
// final TypeSource typeSource;
// Source(
// this.sourceName,
// this.url,
// this.lang,
// this.typeSource, {
// this.isActive = true,
// this.isAdded = false,
// this.isNsfw = false,
// });
// }

View file

@ -0,0 +1,103 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'source_model.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class SourceModelAdapter extends TypeAdapter<SourceModel> {
@override
final int typeId = 3;
@override
SourceModel read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return SourceModel(
sourceName: fields[0] as String,
url: fields[1] as String,
lang: fields[2] as String,
typeSource: fields[6] as TypeSource,
isActive: fields[3] == null ? true : fields[3] as bool,
isAdded: fields[4] == null ? false : fields[4] as bool,
isNsfw: fields[5] == null ? false : fields[5] as bool,
);
}
@override
void write(BinaryWriter writer, SourceModel obj) {
writer
..writeByte(7)
..writeByte(0)
..write(obj.sourceName)
..writeByte(1)
..write(obj.url)
..writeByte(2)
..write(obj.lang)
..writeByte(3)
..write(obj.isActive)
..writeByte(4)
..write(obj.isAdded)
..writeByte(5)
..write(obj.isNsfw)
..writeByte(6)
..write(obj.typeSource);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is SourceModelAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
class TypeSourceAdapter extends TypeAdapter<TypeSource> {
@override
final int typeId = 4;
@override
TypeSource read(BinaryReader reader) {
switch (reader.readByte()) {
case 1:
return TypeSource.single;
case 2:
return TypeSource.mangathemesia;
case 3:
return TypeSource.comick;
default:
return TypeSource.single;
}
}
@override
void write(BinaryWriter writer, TypeSource obj) {
switch (obj) {
case TypeSource.single:
writer.writeByte(1);
break;
case TypeSource.mangathemesia:
writer.writeByte(2);
break;
case TypeSource.comick:
writer.writeByte(3);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TypeSourceAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

93
lib/utils/lang.dart Normal file
View file

@ -0,0 +1,93 @@
lang(String lang) {
if (lang == 'Français') {
return 'fr';
} else if (lang == 'English') {
return 'en';
} else if (lang == 'العربية') {
return 'ar';
} else if (lang == 'Português') {
return 'pt';
} else if (lang == 'Português do brasil') {
return 'pt-br';
} else if (lang == 'Italiano') {
return 'it';
} else if (lang == 'Pусский язык') {
return 'ru';
} else if (lang == 'Español') {
return 'es';
} else if (lang == 'Español latinoamericano') {
return 'es-419';
} else if (lang == 'Bahasa Indonesia') {
return 'id';
} else if (lang == 'हिन्दी, हिंदी') {
return 'hi';
} else if (lang == 'Deutsch') {
return 'de';
} else if (lang == '日本語') {
return 'ja';
} else if (lang == 'Türkçe') {
return 'tr';
} else if (lang == 'Polski') {
return 'pl';
} else if (lang == '中文') {
return 'zh';
} else if (lang == '(Hong Kong) 繁體中文') {
return 'zh-hk';
}
}
completeLang(String lang) {
if (lang == 'fr') {
return 'Français';
} else if (lang == 'en') {
return 'English';
} else if (lang == 'ar') {
return 'العربية';
} else if (lang == 'pt') {
return 'Português';
} else if (lang == 'pt-br') {
return 'Português do brasil';
} else if (lang == 'it') {
return 'Italiano';
} else if (lang == 'ru') {
return 'Pусский язык';
} else if (lang == 'es') {
return 'Español';
} else if (lang == 'es-419') {
return 'Español latinoamericano';
} else if (lang == 'id') {
return 'Bahasa Indonesia';
} else if (lang == 'hi') {
return 'हिन्दी, हिंदी';
} else if (lang == 'de') {
return 'Deutsch';
} else if (lang == 'ja') {
return '日本語';
} else if (lang == 'tr') {
return 'Türkçe';
} else if (lang == 'pl') {
return 'Polski';
} else if (lang == 'zh') {
return '中文';
} else if (lang == 'zh-hk') {
return '(Hong Kong) 繁體中文';
}
}
final List<String> language = [
"Français",
"English",
"العربية",
'Português',
'Português do brasil',
'Italiano',
'Pусский язык',
'Español',
'Español latinoamericano',
'Bahasa Indonesia',
'हिन्दी, हिंदी',
'日本語',
'Deutsch',
'中文',
'(Hong Kong) 繁體中文'
];

View file

@ -1,12 +1,30 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mangayomi/views/browse/extension.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/views/browse/extension/extension.dart';
import 'package:mangayomi/views/browse/migrate.dart';
import 'package:mangayomi/views/browse/sources.dart';
class BrowseScreen extends StatelessWidget {
class BrowseScreen extends StatefulWidget {
const BrowseScreen({super.key});
@override
State<BrowseScreen> createState() => _BrowseScreenState();
}
class _BrowseScreenState extends State<BrowseScreen>
with TickerProviderStateMixin {
late TabController _tabBarController;
@override
void initState() {
_tabBarController = TabController(length: 3, vsync: this);
_tabBarController.animateTo(0);
_tabBarController.addListener(() {
setState(() {});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
@ -20,19 +38,33 @@ class BrowseScreen extends StatelessWidget {
style: TextStyle(color: Theme.of(context).hintColor),
),
actions: [
if (_tabBarController.index != 2)
IconButton(
splashRadius: 20,
onPressed: () {},
icon: Icon(
_tabBarController.index == 0
? Icons.travel_explore_rounded
: Icons.search_rounded,
color: Theme.of(context).hintColor)),
IconButton(
splashRadius: 20,
onPressed: () {},
icon: Icon(Icons.travel_explore,
color: Theme.of(context).hintColor)),
IconButton(
splashRadius: 20,
onPressed: () {},
icon: Icon(Icons.filter_list_sharp,
onPressed: () {
if (_tabBarController.index == 0) {
} else if (_tabBarController.index == 1) {
context.push('/extensionLang');
} else {}
},
icon: Icon(
_tabBarController.index == 0
? Icons.filter_list_sharp
: _tabBarController.index == 1
? Icons.translate_rounded
: Icons.help_outline_outlined,
color: Theme.of(context).hintColor)),
],
bottom: TabBar(
labelColor: Theme.of(context).hintColor,
controller: _tabBarController,
isScrollable: true,
tabs: const [
Tab(text: "Sources"),
@ -41,8 +73,11 @@ class BrowseScreen extends StatelessWidget {
],
),
),
body: const TabBarView(
children: [SourcesScreen(), ExtensionScreen(), MigrateScreen()]),
body: TabBarView(controller: _tabBarController, children: [
SourcesScreen(),
ExtensionScreen(),
MigrateScreen()
]),
),
);
}

View file

@ -1,30 +0,0 @@
import 'package:flutter/material.dart';
class ExtensionScreen extends StatefulWidget {
const ExtensionScreen({super.key});
@override
State<ExtensionScreen> createState() => _ExtensionScreenState();
}
class _ExtensionScreenState extends State<ExtensionScreen> {
@override
Widget build(BuildContext context) {
return Column(
children: [
ListTile(
onTap: () {},
leading: Container(
height: 37,
width: 37,
decoration: BoxDecoration(
color: Colors.grey, borderRadius: BorderRadius.circular(5)),
),
subtitle: const Text('English'),
title: const Text('MangaHere'),
trailing: TextButton(onPressed: () {}, child: Text("Update")),
)
],
);
}
}

View file

@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/source/source_list.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/utils/lang.dart';
import 'package:mangayomi/views/browse/extension/widgets/extension_list_tile_widget.dart';
class ExtensionScreen extends ConsumerStatefulWidget {
const ExtensionScreen({super.key});
@override
ConsumerState<ExtensionScreen> createState() => _ExtensionScreenState();
}
class _ExtensionScreenState extends ConsumerState<ExtensionScreen> {
_init() {
for (var element in sourcesList) {
if (!ref
.watch(hiveBoxMangaSourceProvider)
.containsKey("${element.sourceName}${element.lang}")) {
ref
.watch(hiveBoxMangaSourceProvider)
.put("${element.sourceName}${element.lang}", element);
}
}
_isLoading = false;
}
bool _isLoading = true;
@override
Widget build(
BuildContext context,
) {
_init();
return _isLoading
? Container()
: ValueListenableBuilder<Box<SourceModel>>(
valueListenable: ref.watch(hiveBoxMangaSourceProvider).listenable(),
builder: (context, value, child) {
final entries = value.values.toList();
return GroupedListView<SourceModel, String>(
elements: entries,
groupBy: (element) => element.lang,
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(bottom: 8, left: 8),
child: Row(
children: [
Text(
completeLang(groupByValue.toLowerCase()),
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
itemBuilder: (context, SourceModel element) {
final source =
value.get("${element.sourceName}${element.lang}")!;
return ExtensionListTileWidget(
lang:
value.get("${element.sourceName}${element.lang}")!.lang,
onChanged: (val) {
value.put(
"${element.sourceName}${element.lang}",
SourceModel(
sourceName: element.sourceName,
url: element.url,
lang: element.lang,
typeSource: element.typeSource,
isAdded: val));
},
sourceName: source.sourceName,
value: source.isAdded,
);
},
order: GroupedListOrder.ASC,
);
});
}
}

View file

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/lang.dart';
import 'package:mangayomi/views/browse/extension/widgets/extension_lang_list_tile_widget.dart';
class ExtensionsLang extends ConsumerWidget {
const ExtensionsLang({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(
title: const Text("Extensions"),
),
body: ValueListenableBuilder<Box<dynamic>>(
valueListenable: ref.watch(hiveBoxMangaFilterProvider).listenable(),
builder: (context, value, child) {
List<dynamic> entries =
value.get("language_filter", defaultValue: []);
return ListView.builder(
itemCount: language.length,
itemBuilder: (context, index) {
return ExtensionLangListTileWidget(
lang: lang(language[index]),
onChanged: (val) {
if (val == true) {
entries.add("${lang(language[index])}_");
value.put("language_filter", entries);
} else {
entries.remove("${lang(language[index])}_");
value.put("language_filter", entries);
}
},
value: entries.contains("${lang(language[index])}_"),
);
},
);
}));
;
}
}

View file

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:mangayomi/utils/lang.dart';
class ExtensionLangListTileWidget extends StatelessWidget {
final String lang;
final bool value;
final Function(bool) onChanged;
const ExtensionLangListTileWidget(
{super.key,
required this.lang,
required this.value,
required this.onChanged});
@override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
onChanged(!value);
},
title: Text(completeLang(lang.toLowerCase())),
trailing: Switch(
value: value,
onChanged: (value) {
onChanged(value);
}));
}
}

View file

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:mangayomi/utils/lang.dart';
class ExtensionListTileWidget extends StatelessWidget {
final String sourceName;
final String lang;
final bool value;
final Function(bool) onChanged;
const ExtensionListTileWidget(
{super.key,
required this.sourceName,
required this.lang,
required this.value,
required this.onChanged});
@override
Widget build(BuildContext context) {
return ListTile(
onTap: () {
onChanged(!value);
},
leading: Container(
height: 37,
width: 37,
decoration: BoxDecoration(
color: Theme.of(context).secondaryHeaderColor,
borderRadius: BorderRadius.circular(5)),
child: const Icon(Icons.source_outlined),
),
title: Text(sourceName),
subtitle: Text(completeLang(lang.toLowerCase())),
trailing: Switch(
value: value,
onChanged: (value) {
onChanged(value);
}));
;
}
}

View file

@ -1,50 +1,131 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/manga_type.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/source/source_list.dart';
import 'package:mangayomi/source/source_model.dart';
import 'package:mangayomi/utils/lang.dart';
class SourcesScreen extends StatefulWidget {
// class SourcesScreen extends StatefulWidget {
// const SourcesScreen({super.key});
// @override
// State<SourcesScreen> createState() => _SourcesScreenState();
// }
// class _SourcesScreenState extends State<SourcesScreen> {
// @override
// Widget build(BuildContext context) {
// return Column(
// children: [
// ListTile(
// onTap: () {
// context.push('/mangaHome',
// extra: MangaType(
// isFullData: true, lang: 'en', source: 'MangaHere'));
// },
// leading: Container(
// height: 37,
// width: 37,
// decoration: BoxDecoration(
// color: Colors.grey, borderRadius: BorderRadius.circular(5)),
// ),
// subtitle: const Text('English'),
// title: const Text('MangaHere'),
// trailing: SizedBox(
// width: 110,
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
// children: const [
// Text(
// "Latest",
// style: TextStyle(fontWeight: FontWeight.bold),
// ),
// Icon(
// Icons.push_pin_outlined,
// color: Colors.black,
// )
// ],
// )),
// )
// ],
// );
// }
// }
class SourcesScreen extends ConsumerStatefulWidget {
const SourcesScreen({super.key});
@override
State<SourcesScreen> createState() => _SourcesScreenState();
ConsumerState<SourcesScreen> createState() => _SourcesScreenState();
}
class _SourcesScreenState extends State<SourcesScreen> {
class _SourcesScreenState extends ConsumerState<SourcesScreen> {
@override
Widget build(BuildContext context) {
return Column(
children: [
ListTile(
onTap: () {
context.push('/mangaHome',
extra: MangaType(
isFullData: true, lang: 'en', source: 'MangaHere'));
},
leading: Container(
height: 37,
width: 37,
decoration: BoxDecoration(
color: Colors.grey, borderRadius: BorderRadius.circular(5)),
),
subtitle: const Text('English'),
title: const Text('MangaHere'),
trailing: SizedBox(
width: 110,
Widget build(
BuildContext context,
) {
return ValueListenableBuilder<Box<SourceModel>>(
valueListenable: ref.watch(hiveBoxMangaSourceProvider).listenable(),
builder: (context, value, child) {
final entries =
value.values.where((element) => element.isAdded == true).toList();
if (entries.isEmpty) {
return const Center(child: Text("Empty"));
}
return GroupedListView<SourceModel, String>(
elements: entries,
groupBy: (element) => element.lang,
groupSeparatorBuilder: (String groupByValue) => Padding(
padding: const EdgeInsets.only(bottom: 8, left: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
children: [
Text(
"Latest",
style: TextStyle(fontWeight: FontWeight.bold),
completeLang(groupByValue.toLowerCase()),
style: const TextStyle(fontWeight: FontWeight.bold),
),
Icon(
Icons.push_pin_outlined,
color: Colors.black,
)
],
)),
)
],
);
),
),
itemBuilder: (context, SourceModel element) {
final source = value.get("${element.sourceName}${element.lang}")!;
return ListTile(
onTap: () {
if (source.sourceName == 'MangaHere') {
context.push('/mangaHome',
extra: MangaType(
isFullData: true, lang: 'en', source: 'MangaHere'));
}
},
leading: Container(
height: 37,
width: 37,
decoration: BoxDecoration(
color: Theme.of(context).secondaryHeaderColor,
borderRadius: BorderRadius.circular(5)),
child: const Icon(Icons.source_outlined),
),
subtitle: Text(completeLang(source.lang)),
title: Text(source.sourceName),
trailing: SizedBox(
width: 110,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: const [
Icon(
Icons.push_pin_outlined,
color: Colors.black,
)
],
)),
);
},
sort: true,
order: GroupedListOrder.ASC,
);
});
}
}

View file

@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:hidable/hidable.dart';
import 'package:mangayomi/views/general/scroll_controller_provider.dart';
import 'package:mangayomi/utils/media_query.dart';
class GeneralScreen extends ConsumerStatefulWidget {

View file

@ -1,5 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final scrollControllerProvider =
Provider<ScrollController>((ref) => ScrollController());

View file

@ -5,7 +5,6 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:mangayomi/models/model_manga.dart';
import 'package:mangayomi/providers/hive_provider.dart';
import 'package:mangayomi/utils/cached_network.dart';
import 'package:mangayomi/views/general/scroll_controller_provider.dart';
import 'package:mangayomi/views/widgets/bottom_text_widget.dart';
import 'package:mangayomi/views/widgets/cover_view_widget.dart';
import 'package:mangayomi/views/widgets/gridview_widget.dart';
@ -24,7 +23,6 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
final _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
final scrollController = ref.watch(scrollControllerProvider);
return Scaffold(
appBar: AppBar(
elevation: 0,
@ -108,90 +106,52 @@ class _LibraryScreenState extends ConsumerState<LibraryScreen> {
}),
],
),
body: _textEditingController.text.isNotEmpty
? GridViewWidget(
controller: scrollController,
itemCount: entriesFilter.length,
body: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref.watch(hiveBoxManga).listenable(),
builder: (context, value, child) {
entries = value.values.where((element) => element.favorite).toList();
final entriesManga =
_textEditingController.text.isNotEmpty ? entriesFilter : entries;
if (entries.isNotEmpty || entriesFilter.isNotEmpty) {
return GridViewWidget(
itemCount: entriesManga.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
final model = ModelManga(
imageUrl: entriesFilter[index].imageUrl,
name: entriesFilter[index].name,
genre: entriesFilter[index].genre,
author: entriesFilter[index].author,
status: entriesFilter[index].status,
chapterDate: entriesFilter[index].chapterDate,
chapterTitle: entriesFilter[index].chapterTitle,
chapterUrl: entriesFilter[index].chapterUrl,
description: entriesFilter[index].description,
favorite: entriesFilter[index].favorite,
link: entriesFilter[index].link,
source: entriesFilter[index].source,
lang: entriesFilter[index].lang);
imageUrl: entriesManga[index].imageUrl,
name: entriesManga[index].name,
genre: entriesManga[index].genre,
author: entriesManga[index].author,
status: entriesManga[index].status,
chapterDate: entriesManga[index].chapterDate,
chapterTitle: entriesManga[index].chapterTitle,
chapterUrl: entriesManga[index].chapterUrl,
description: entriesManga[index].description,
favorite: entriesManga[index].favorite,
link: entriesManga[index].link,
source: entriesManga[index].source,
lang: entriesManga[index].lang);
context.push('/manga-reader/detail', extra: model);
},
child: CoverViewWidget(
children: [
cachedNetworkImage(
imageUrl: entriesFilter[index].imageUrl!,
imageUrl: entriesManga[index].imageUrl!,
width: 200,
height: 270,
fit: BoxFit.cover),
BottomTextWidget(text: entriesFilter[index].name!)
BottomTextWidget(text: entriesManga[index].name!)
],
),
);
},
)
: ValueListenableBuilder<Box<ModelManga>>(
valueListenable: ref.watch(hiveBoxManga).listenable(),
builder: (context, value, child) {
entries =
value.values.where((element) => element.favorite).toList();
if (entries.isNotEmpty) {
return GridViewWidget(
controller: scrollController,
itemCount: entries.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
final model = ModelManga(
imageUrl: entries[index].imageUrl,
name: entries[index].name,
genre: entries[index].genre,
author: entries[index].author,
status: entries[index].status,
chapterDate: entries[index].chapterDate,
chapterTitle: entries[index].chapterTitle,
chapterUrl: entries[index].chapterUrl,
description: entries[index].description,
favorite: entries[index].favorite,
link: entries[index].link,
source: entries[index].source,
lang: entries[index].lang);
context.push('/manga-reader/detail', extra: model);
},
child: CoverViewWidget(
children: [
cachedNetworkImage(
imageUrl: entries[index].imageUrl!,
width: 200,
height: 270,
fit: BoxFit.cover),
BottomTextWidget(text: entries[index].name!)
],
),
);
},
);
}
return const Center(child: Text("Empty Library"));
},
),
);
}
return const Center(child: Text("Empty Library"));
},
),
);
}
}

View file

@ -116,6 +116,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView> {
_listView() {
return DraggableScrollbar.rrect(
alwaysVisibleScrollThumb: true,
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
@ -135,14 +136,17 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView> {
modelManga: widget.modelManga!,
index: finalIndex);
},
trailing: const Icon(FontAwesomeIcons.circleDown),
trailing: const Icon(
FontAwesomeIcons.circleDown,
size: 20,
),
subtitle: Text(
widget.modelManga!.chapterDate![finalIndex],
style: const TextStyle(fontSize: 13),
style: const TextStyle(fontSize: 12),
),
title: Text(
widget.modelManga!.chapterTitle![finalIndex],
style: const TextStyle(fontSize: 15),
style: const TextStyle(fontSize: 13),
),
);
}));
@ -170,7 +174,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView> {
Column(
children: [
SizedBox(
height: AppBar().preferredSize.height,
height: AppBar().preferredSize.height * 1.5,
),
SizedBox(
height: 180,
@ -236,7 +240,7 @@ class _MangaDetailViewState extends ConsumerState<MangaDetailView> {
padding:
const EdgeInsets.symmetric(horizontal: 8),
child: Text(
'${widget.modelManga!.chapterTitle!.length.toString()} chapter(s)',
'${widget.modelManga!.chapterTitle!.length.toString()} chapters',
style: const TextStyle(
fontWeight: FontWeight.bold),
),

View file

@ -14,9 +14,10 @@ class MoreScreen extends StatelessWidget {
flex: 3,
child: Column(
children: [
const Divider(),
const Divider(
color: Colors.grey,
),
ListTile(
dense: true,
onTap: () {},
leading:
const SizedBox(height: 40, child: Icon(Icons.cloud_off)),
@ -28,7 +29,6 @@ class MoreScreen extends StatelessWidget {
),
),
ListTile(
dense: true,
onTap: () {},
leading: const SizedBox(
height: 40, child: Icon(CupertinoIcons.eyeglasses)),
@ -39,16 +39,16 @@ class MoreScreen extends StatelessWidget {
onChanged: (value) {},
),
),
const Divider(),
const Divider(
color: Colors.grey,
),
ListTile(
dense: true,
onTap: () {},
leading: const SizedBox(
height: 40, child: Icon(Icons.download_outlined)),
title: const Text('Donwload queue'),
),
ListTile(
dense: true,
onTap: () {},
leading: Container(
height: 20,
@ -58,7 +58,6 @@ class MoreScreen extends StatelessWidget {
title: const Text('Categories'),
),
ListTile(
dense: true,
onTap: () {},
leading: Container(
height: 20,
@ -68,30 +67,28 @@ class MoreScreen extends StatelessWidget {
title: const Text('Statistics'),
),
ListTile(
dense: true,
onTap: () {},
leading: const SizedBox(
height: 40,
child: Icon(Icons.settings_backup_restore_sharp)),
title: const Text('Backup and restore'),
),
const Divider(),
const Divider(
color: Colors.grey,
),
ListTile(
dense: true,
onTap: () {},
leading: const SizedBox(
height: 40, child: Icon(Icons.settings_outlined)),
title: const Text('Backup and restore'),
),
ListTile(
dense: true,
onTap: () {},
leading: const SizedBox(
height: 40, child: Icon(Icons.info_outline)),
title: const Text('About'),
),
ListTile(
dense: true,
onTap: () {},
leading: const SizedBox(
height: 40, child: Icon(Icons.help_outline)),

View file

@ -12,12 +12,13 @@ class BottomTextWidget extends StatelessWidget {
right: 0,
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3), //New
offset: const Offset(0.5, 0.9),
blurRadius: 3.0)
],
color: Theme.of(context).scaffoldBackgroundColor,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black.withOpacity(0.4)],
stops: const [0, 1],
),
),
child: Text(
text,

View file

@ -432,14 +432,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.2"
hidable:
dependency: "direct main"
description:
name: hidable
sha256: abe6f3ce1037a8de18e996d1e6417306949217010daea56799d6203a9d8f1c48
url: "https://pub.dev"
source: hosted
version: "1.0.3"
hive:
dependency: "direct main"
description: