feat: swipeable media pager, favorite artists, optimized video preview, Gitea updates and v2.0.2 release

This commit is contained in:
2026-09-03 18:09:38 +03:00
parent cc5a1a4f82
commit e419c7ad78
23 changed files with 837 additions and 266 deletions
+6 -1
View File
@@ -9,6 +9,7 @@ class AppUpdateInfo {
this.apkUrl,
this.windowsInstallerUrl,
this.portableZipUrl,
this.linuxTarGzUrl,
});
final String version;
@@ -20,7 +21,11 @@ class AppUpdateInfo {
final String? apkUrl;
final String? windowsInstallerUrl;
final String? portableZipUrl;
final String? linuxTarGzUrl;
bool get hasAssets =>
apkUrl != null || windowsInstallerUrl != null || portableZipUrl != null;
apkUrl != null ||
windowsInstallerUrl != null ||
portableZipUrl != null ||
linuxTarGzUrl != null;
}
+11
View File
@@ -114,3 +114,14 @@ class Post {
const {},
);
}
class PostNavigationContext {
const PostNavigationContext({
required this.currentPost,
required this.posts,
});
final Post currentPost;
final List<Post> posts;
}
+20 -103
View File
@@ -40,10 +40,10 @@ class ProviderRepository {
updatedAt: now,
),
ContentProviderConfig(
id: 'safebooru',
name: 'Safebooru',
baseUrl: 'https://safebooru.org',
apiType: 'gelbooru',
id: 'realbooru',
name: 'Realbooru',
baseUrl: 'https://realbooru.com',
apiType: 'realbooru_html',
enabled: true,
priority: 2,
timeoutSeconds: 20,
@@ -51,44 +51,16 @@ class ProviderRepository {
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'konachan',
name: 'Konachan',
baseUrl: 'https://konachan.com',
apiType: 'moebooru',
enabled: true,
priority: 3,
timeoutSeconds: 20,
customHeaders: const {},
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'yandere',
name: 'Yande.re',
baseUrl: 'https://yande.re',
apiType: 'moebooru',
enabled: true,
priority: 4,
timeoutSeconds: 20,
customHeaders: const {
'query.api_version': '2',
'query.include_tags': '1',
'query.filter': '1',
},
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'e621',
name: 'e621',
baseUrl: 'https://e621.net',
apiType: 'e621',
enabled: true,
priority: 5,
priority: 3,
timeoutSeconds: 20,
customHeaders: const {
'User-Agent': 'Lunaris/2.0.1 Flutter local booru browser',
'User-Agent': 'Lunaris/2.0.2 Flutter local booru browser',
},
createdAt: now,
updatedAt: now,
@@ -99,76 +71,10 @@ class ProviderRepository {
baseUrl: 'https://e926.net',
apiType: 'e621',
enabled: true,
priority: 6,
priority: 4,
timeoutSeconds: 20,
customHeaders: const {
'User-Agent': 'Lunaris/2.0.1 Flutter local booru browser',
},
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'realbooru',
name: 'Realbooru',
baseUrl: 'https://realbooru.com',
apiType: 'realbooru_html',
enabled: true,
priority: 7,
timeoutSeconds: 20,
customHeaders: const {},
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'xbooru',
name: 'Xbooru',
baseUrl: 'https://xbooru.com',
apiType: 'gelbooru',
enabled: true,
priority: 8,
timeoutSeconds: 20,
customHeaders: const {},
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'paheal',
name: 'Rule34 Paheal',
baseUrl: 'https://rule34.paheal.net',
apiType: 'paheal',
enabled: true,
priority: 9,
timeoutSeconds: 20,
customHeaders: const {
'User-Agent': 'Lunaris/2.0.1 Flutter local booru browser',
},
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'kemono',
name: 'Kemono',
baseUrl: 'https://kemono.su',
apiType: 'kemono',
enabled: true,
priority: 10,
timeoutSeconds: 20,
customHeaders: const {
'query.creator_api_base': 'https://kemono-api.mbaharip.com',
},
createdAt: now,
updatedAt: now,
),
ContentProviderConfig(
id: 'coomer',
name: 'Coomer',
baseUrl: 'https://coomer.su',
apiType: 'coomer',
enabled: false,
priority: 11,
timeoutSeconds: 20,
customHeaders: const {
'query.creator_api_base': 'https://kemono-api.mbaharip.com',
'User-Agent': 'Lunaris/2.0.2 Flutter local booru browser',
},
createdAt: now,
updatedAt: now,
@@ -179,7 +85,7 @@ class ProviderRepository {
baseUrl: 'https://pawchive.pw',
apiType: 'pawchive',
enabled: true,
priority: 12,
priority: 5,
timeoutSeconds: 20,
customHeaders: const {},
createdAt: now,
@@ -192,11 +98,22 @@ class ProviderRepository {
return _databaseService.safeWrite((isar) async {
final existingProviders =
await isar.providerConfigEntitys.where().findAll();
const removedIds = {
'safebooru',
'konachan',
'yandere',
'xbooru',
'paheal',
'kemono',
'coomer',
};
for (final provider in existingProviders) {
if (provider.providerId == 'cosbooru') {
provider.enabled = false;
provider.updatedAt = DateTime.now();
await isar.providerConfigEntitys.put(provider);
} else if (removedIds.contains(provider.providerId)) {
await isar.providerConfigEntitys.delete(provider.isarId);
}
}
final seeds = seedProviders();
+11 -2
View File
@@ -48,12 +48,14 @@ class AppSettings {
required this.lastFeedProviderIds,
required this.lastFeedTopPeriod,
required this.lastFeedScrollOffset,
this.favoriteArtists = const [],
this.skippedUpdateVersion,
this.lastUpdateCheckAt,
this.defaultRatingFilter,
this.lastFeedRating,
});
final List<String> favoriteArtists;
final List<String> enabledProviderIds;
final bool nsfwEnabled;
final int cacheTtlHours;
@@ -96,11 +98,12 @@ class AppSettings {
final String? lastFeedRating;
static const defaults = AppSettings(
enabledProviderIds: ['gelbooru', 'rule34', 'safebooru'],
enabledProviderIds: ['gelbooru', 'rule34', 'realbooru'],
nsfwEnabled: true,
cacheTtlHours: 24,
cacheMaxItems: 2000,
providerPriority: {'gelbooru': 0, 'rule34': 1, 'safebooru': 2},
providerPriority: {'gelbooru': 0, 'rule34': 1, 'realbooru': 2},
favoriteArtists: [],
themeMode: 'dark',
languageCode: 'ru',
appSeedColor: 0xFFE84D8A,
@@ -180,8 +183,10 @@ class AppSettings {
String? defaultRatingFilter,
String? lastFeedRating,
bool clearLastFeedRating = false,
List<String>? favoriteArtists,
}) {
return AppSettings(
favoriteArtists: favoriteArtists ?? this.favoriteArtists,
enabledProviderIds: enabledProviderIds ?? this.enabledProviderIds,
nsfwEnabled: nsfwEnabled ?? this.nsfwEnabled,
cacheTtlHours: cacheTtlHours ?? this.cacheTtlHours,
@@ -233,6 +238,7 @@ class AppSettings {
}
Map<String, dynamic> toJson() => {
'favoriteArtists': favoriteArtists,
'enabledProviderIds': enabledProviderIds,
'nsfwEnabled': nsfwEnabled,
'cacheTtlHours': cacheTtlHours,
@@ -365,6 +371,9 @@ class AppSettings {
lastFeedScrollOffset:
(json['lastFeedScrollOffset'] as num?)?.toDouble() ??
defaults.lastFeedScrollOffset,
favoriteArtists: List<String>.from(
(json['favoriteArtists'] as List?) ?? defaults.favoriteArtists,
),
skippedUpdateVersion: json['skippedUpdateVersion'] as String?,
lastUpdateCheckAt: json['lastUpdateCheckAt'] as String?,
defaultRatingFilter: json['defaultRatingFilter'] as String?,
+11 -2
View File
@@ -11,9 +11,9 @@ class UpdateService {
UpdateService(this._dio, this._settingsService);
static const latestReleaseUrl =
'https://gitea.rardogsynapse.online/api/v1/repos/RarDog/RuleGelApp/releases/latest';
'https://gitea.rardogsynapse.online/api/v1/repos/RarDog/Lunaris/releases/latest';
static const releasesUrl =
'https://gitea.rardogsynapse.online/api/v1/repos/RarDog/RuleGelApp/releases';
'https://gitea.rardogsynapse.online/api/v1/repos/RarDog/Lunaris/releases';
final Dio _dio;
final SettingsService _settingsService;
@@ -100,6 +100,7 @@ class UpdateService {
String? apkUrl;
String? installerUrl;
String? portableUrl;
String? linuxTarGzUrl;
for (final item in assets.whereType<Map>()) {
final name = (item['name'] ?? '').toString().toLowerCase();
final url = (item['browser_download_url'] ?? '').toString();
@@ -107,6 +108,9 @@ class UpdateService {
if (name.endsWith('.apk')) apkUrl = url;
if (name.endsWith('.exe')) installerUrl = url;
if (name.endsWith('.zip')) portableUrl = url;
if (name.endsWith('.tar.gz') || name.endsWith('.tgz')) {
linuxTarGzUrl = url;
}
}
return AppUpdateInfo(
version: _versionFromTag(tag),
@@ -118,6 +122,7 @@ class UpdateService {
apkUrl: apkUrl,
windowsInstallerUrl: installerUrl,
portableZipUrl: portableUrl,
linuxTarGzUrl: linuxTarGzUrl,
);
}
@@ -125,6 +130,9 @@ class UpdateService {
if (Platform.isAndroid) {
return info.apkUrl;
}
if (Platform.isLinux) {
return info.linuxTarGzUrl ?? info.portableZipUrl;
}
if (Platform.isWindows) {
return info.windowsInstallerUrl ?? info.portableZipUrl;
}
@@ -137,6 +145,7 @@ class UpdateService {
parsed?.pathSegments.isEmpty ?? true ? '' : parsed!.pathSegments.last;
if (fromUrl.contains('.')) return fromUrl;
if (Platform.isAndroid) return 'Lunaris-v${info.version}.apk';
if (Platform.isLinux) return 'Lunaris-v${info.version}-linux-x64.tar.gz';
if (Platform.isWindows) return 'LunarisSetup-v${info.version}.exe';
return 'LunarisPortable-v${info.version}.zip';
}