Initial Flutter booru app

This commit is contained in:
2026-05-23 11:46:52 +03:00
commit 63da741295
187 changed files with 24113 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
class Collection {
const Collection({
required this.id,
required this.name,
this.description,
this.coverUrl,
required this.createdAt,
required this.updatedAt,
});
final String id;
final String name;
final String? description;
final String? coverUrl;
final DateTime createdAt;
final DateTime updatedAt;
Collection copyWith({
String? id,
String? name,
String? description,
String? coverUrl,
DateTime? createdAt,
DateTime? updatedAt,
}) =>
Collection(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
coverUrl: coverUrl ?? this.coverUrl,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
@@ -0,0 +1,81 @@
class ContentProviderConfig {
const ContentProviderConfig({
required this.id,
required this.name,
required this.baseUrl,
required this.apiType,
required this.enabled,
required this.priority,
required this.timeoutSeconds,
required this.customHeaders,
required this.createdAt,
required this.updatedAt,
});
final String id;
final String name;
final String baseUrl;
final String apiType;
final bool enabled;
final int priority;
final int timeoutSeconds;
final Map<String, String> customHeaders;
final DateTime createdAt;
final DateTime updatedAt;
ContentProviderConfig copyWith({
String? id,
String? name,
String? baseUrl,
String? apiType,
bool? enabled,
int? priority,
int? timeoutSeconds,
Map<String, String>? customHeaders,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return ContentProviderConfig(
id: id ?? this.id,
name: name ?? this.name,
baseUrl: baseUrl ?? this.baseUrl,
apiType: apiType ?? this.apiType,
enabled: enabled ?? this.enabled,
priority: priority ?? this.priority,
timeoutSeconds: timeoutSeconds ?? this.timeoutSeconds,
customHeaders: customHeaders ?? this.customHeaders,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'baseUrl': baseUrl,
'apiType': apiType,
'enabled': enabled,
'priority': priority,
'timeoutSeconds': timeoutSeconds,
'customHeaders': customHeaders,
'createdAt': createdAt.toIso8601String(),
'updatedAt': updatedAt.toIso8601String(),
};
factory ContentProviderConfig.fromJson(Map<String, dynamic> json) {
return ContentProviderConfig(
id: json['id'] as String,
name: json['name'] as String,
baseUrl: json['baseUrl'] as String,
apiType: json['apiType'] as String,
enabled: (json['enabled'] as bool?) ?? true,
priority: (json['priority'] as num?)?.toInt() ?? 0,
timeoutSeconds: (json['timeoutSeconds'] as num?)?.toInt() ?? 20,
customHeaders: Map<String, String>.from(
(json['customHeaders'] as Map?) ?? const {},
),
createdAt: DateTime.parse(json['createdAt'] as String),
updatedAt: DateTime.parse(json['updatedAt'] as String),
);
}
}
+13
View File
@@ -0,0 +1,13 @@
class Favorite {
const Favorite({
required this.id,
required this.postId,
required this.providerId,
required this.savedAt,
});
final String id;
final String postId;
final String providerId;
final DateTime savedAt;
}
+70
View File
@@ -0,0 +1,70 @@
class Post {
const Post({
required this.id,
required this.providerId,
required this.providerName,
required this.previewUrl,
required this.sampleUrl,
required this.fileUrl,
required this.tags,
required this.rating,
required this.width,
required this.height,
this.source,
required this.createdAt,
required this.fileType,
required this.score,
});
final String id;
final String providerId;
final String providerName;
final String previewUrl;
final String sampleUrl;
final String fileUrl;
final List<String> tags;
final String rating;
final int width;
final int height;
final String? source;
final DateTime createdAt;
final String fileType;
final int score;
String get cacheKey => '$providerId:$id';
Map<String, dynamic> toJson() => {
'id': id,
'providerId': providerId,
'providerName': providerName,
'previewUrl': previewUrl,
'sampleUrl': sampleUrl,
'fileUrl': fileUrl,
'tags': tags,
'rating': rating,
'width': width,
'height': height,
'source': source,
'createdAt': createdAt.toIso8601String(),
'fileType': fileType,
'score': score,
};
factory Post.fromJson(Map<String, dynamic> json) => Post(
id: json['id'].toString(),
providerId: json['providerId'] as String,
providerName: json['providerName'] as String,
previewUrl: (json['previewUrl'] as String?) ?? '',
sampleUrl: (json['sampleUrl'] as String?) ?? '',
fileUrl: (json['fileUrl'] as String?) ?? '',
tags: List<String>.from((json['tags'] as List?) ?? const []),
rating: (json['rating'] as String?) ?? 'unknown',
width: (json['width'] as num?)?.toInt() ?? 0,
height: (json['height'] as num?)?.toInt() ?? 0,
source: json['source'] as String?,
createdAt: DateTime.tryParse((json['createdAt'] as String?) ?? '') ??
DateTime.fromMillisecondsSinceEpoch(0),
fileType: (json['fileType'] as String?) ?? 'unknown',
score: (json['score'] as num?)?.toInt() ?? 0,
);
}
+42
View File
@@ -0,0 +1,42 @@
enum ProviderStatus { unknown, online, offline }
class ProviderHealth {
const ProviderHealth({
required this.providerId,
required this.status,
required this.pingMs,
required this.lastCheckedAt,
this.errorMessage,
this.apiVersion,
});
final String providerId;
final ProviderStatus status;
final int pingMs;
final DateTime lastCheckedAt;
final String? errorMessage;
final String? apiVersion;
bool get isOnline => status == ProviderStatus.online;
Map<String, dynamic> toJson() => {
'providerId': providerId,
'status': status.name,
'pingMs': pingMs,
'lastCheckedAt': lastCheckedAt.toIso8601String(),
'errorMessage': errorMessage,
'apiVersion': apiVersion,
};
factory ProviderHealth.fromJson(Map<String, dynamic> json) => ProviderHealth(
providerId: json['providerId'] as String,
status: ProviderStatus.values.firstWhere(
(value) => value.name == json['status'],
orElse: () => ProviderStatus.unknown,
),
pingMs: (json['pingMs'] as num?)?.toInt() ?? 0,
lastCheckedAt: DateTime.parse(json['lastCheckedAt'] as String),
errorMessage: json['errorMessage'] as String?,
apiVersion: json['apiVersion'] as String?,
);
}
+15
View File
@@ -0,0 +1,15 @@
class SearchHistory {
const SearchHistory({
required this.id,
required this.query,
required this.tags,
required this.searchedAt,
required this.resultCount,
});
final String id;
final String query;
final List<String> tags;
final DateTime searchedAt;
final int resultCount;
}
+15
View File
@@ -0,0 +1,15 @@
enum TopPeriodFilter {
none,
month,
year,
allTime;
String get label {
return switch (this) {
TopPeriodFilter.none => 'Latest',
TopPeriodFilter.month => 'Top month',
TopPeriodFilter.year => 'Top year',
TopPeriodFilter.allTime => 'Top all time',
};
}
}