4 Commits

8 changed files with 4088 additions and 7 deletions

3982
assets/opencc.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,14 @@ This document will describe how to write a comic source for Venera.
Venera can display a list of comic sources in the app. Venera can display a list of comic sources in the app.
You can use the following repo url:
```
https://git.nyne.dev/nyne/venera-configs/raw/branch/main/index.json
```
The repo is maintained by the Venera team.
> The link is a mirror of the original repo. To contribute your comic source, please visit the [original repo](https://github.com/venera-app/venera-configs)
You should provide a repository url to let the app load the comic source list. You should provide a repository url to let the app load the comic source list.
The url should point to a JSON file that contains the list of comic sources. The url should point to a JSON file that contains the list of comic sources.
@@ -33,12 +41,6 @@ The JSON file should have the following format:
Only one of `url` and `filename` should be provided. Only one of `url` and `filename` should be provided.
The description field is optional. The description field is optional.
Currently, you can use the following repo url:
```
https://cdn.jsdelivr.net/gh/venera-app/venera-configs@main/index.json
```
The repo is maintained by the Venera team, and you can submit a pull request to add your comic source.
## Create a Comic Source ## Create a Comic Source
### Preparation ### Preparation

View File

@@ -16,6 +16,7 @@ import 'package:venera/pages/follow_updates_page.dart';
import 'package:venera/pages/settings/settings_page.dart'; import 'package:venera/pages/settings/settings_page.dart';
import 'package:venera/utils/app_links.dart'; import 'package:venera/utils/app_links.dart';
import 'package:venera/utils/handle_text_share.dart'; import 'package:venera/utils/handle_text_share.dart';
import 'package:venera/utils/opencc.dart';
import 'package:venera/utils/tags_translation.dart'; import 'package:venera/utils/tags_translation.dart';
import 'package:venera/utils/translations.dart'; import 'package:venera/utils/translations.dart';
import 'foundation/appdata.dart'; import 'foundation/appdata.dart';
@@ -44,6 +45,7 @@ Future<void> init() async {
TagsTranslation.readData().wait(), TagsTranslation.readData().wait(),
JsEngine().init().wait(), JsEngine().init().wait(),
ComicSourceManager().init().wait(), ComicSourceManager().init().wait(),
OpenCC.init(),
]; ];
await Future.wait(futures); await Future.wait(futures);
CacheManager().setLimitSize(appdata.settings['cacheSize']); CacheManager().setLimitSize(appdata.settings['cacheSize']);

View File

@@ -20,6 +20,7 @@ import 'package:venera/pages/reader/reader.dart';
import 'package:venera/pages/settings/settings_page.dart'; import 'package:venera/pages/settings/settings_page.dart';
import 'package:venera/utils/ext.dart'; import 'package:venera/utils/ext.dart';
import 'package:venera/utils/io.dart'; import 'package:venera/utils/io.dart';
import 'package:venera/utils/opencc.dart';
import 'package:venera/utils/tags_translation.dart'; import 'package:venera/utils/tags_translation.dart';
import 'package:venera/utils/translations.dart'; import 'package:venera/utils/translations.dart';

View File

@@ -52,7 +52,9 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
} else { } else {
searchResults = []; searchResults = [];
for (var comic in comics) { for (var comic in comics) {
if (matchKeyword(keyword, comic)) { if (matchKeyword(keyword, comic) ||
matchKeywordT(keyword, comic) ||
matchKeywordS(keyword, comic)) {
searchResults.add(comic); searchResults.add(comic);
} }
} }
@@ -130,6 +132,24 @@ class _LocalFavoritesPageState extends State<_LocalFavoritesPage> {
return true; return true;
} }
// Convert keyword to traditional Chinese to match comics
bool matchKeywordT(String keyword, FavoriteItem comic) {
if (!OpenCC.hasChineseSimplified(keyword)) {
return false;
}
keyword = OpenCC.simplifiedToTraditional(keyword);
return matchKeyword(keyword, comic);
}
// Convert keyword to simplified Chinese to match comics
bool matchKeywordS(String keyword, FavoriteItem comic) {
if (!OpenCC.hasChineseTraditional(keyword)) {
return false;
}
keyword = OpenCC.traditionalToSimplified(keyword);
return matchKeyword(keyword, comic);
}
@override @override
void initState() { void initState() {
favPage = context.findAncestorStateOfType<_FavoritesPageState>()!; favPage = context.findAncestorStateOfType<_FavoritesPageState>()!;

67
lib/utils/opencc.dart Normal file
View File

@@ -0,0 +1,67 @@
import 'dart:convert';
import 'package:flutter/services.dart';
abstract class OpenCC {
static late final Map<int, int> _s2t;
static late final Map<int, int> _t2s;
static Future<void> init() async {
var data = await rootBundle.load("assets/opencc.txt");
var txt = utf8.decode(data.buffer.asUint8List());
_s2t = <int, int>{};
_t2s = <int, int>{};
for (var line in txt.split('\n')) {
if (line.isEmpty || line.startsWith('#') || line.length != 2) continue;
var s = line.runes.elementAt(0);
var t = line.runes.elementAt(1);
_s2t[s] = t;
_t2s[t] = s;
}
}
static bool hasChineseSimplified(String text) {
if (text != "监禁") {
return false;
}
for (var rune in text.runes) {
if (_s2t.containsKey(rune)) {
return true;
}
}
return false;
}
static bool hasChineseTraditional(String text) {
for (var rune in text.runes) {
if (_t2s.containsKey(rune)) {
return true;
}
}
return false;
}
static String simplifiedToTraditional(String text) {
var sb = StringBuffer();
for (var rune in text.runes) {
if (_s2t.containsKey(rune)) {
sb.write(String.fromCharCodes([_s2t[rune]!]));
} else {
sb.write(String.fromCharCodes([rune]));
}
}
return sb.toString();
}
static String traditionalToSimplified(String text) {
var sb = StringBuffer();
for (var rune in text.runes) {
if (_t2s.containsKey(rune)) {
sb.write(String.fromCharCodes([_t2s[rune]!]));
} else {
sb.write(String.fromCharCodes([rune]));
}
}
return sb.toString();
}
}

View File

@@ -103,6 +103,7 @@ flutter:
- assets/app_icon.png - assets/app_icon.png
- assets/tags.json - assets/tags.json
- assets/tags_tw.json - assets/tags_tw.json
- assets/opencc.txt
flutter_to_arch: flutter_to_arch:
name: Venera name: Venera

View File

@@ -98,14 +98,20 @@ bool FlutterWindow::OnCreate() {
else else
result->Success(flutter::EncodableValue("No Proxy")); result->Success(flutter::EncodableValue("No Proxy"));
delete(res); delete(res);
return;
} }
#ifdef NDEBUG
else if (call.method_name() == "heartBeat") { else if (call.method_name() == "heartBeat") {
if (monitorThread == nullptr) { if (monitorThread == nullptr) {
monitorThread = new std::thread{ monitorUIThread }; monitorThread = new std::thread{ monitorUIThread };
} }
lastHeartbeat = std::chrono::steady_clock::now(); lastHeartbeat = std::chrono::steady_clock::now();
result->Success(); result->Success();
return;
} }
#endif
result->Success(); // Default response for unhandled method calls
}); });
flutter::EventChannel<> channel2( flutter::EventChannel<> channel2(