Add viewed history, diagnostics, tag chips and SWF viewer
This commit is contained in:
@@ -3,14 +3,22 @@ import '../../core/utils/result.dart';
|
||||
import '../models/post.dart';
|
||||
import '../models/top_period_filter.dart';
|
||||
import '../providers/provider_manager.dart';
|
||||
import '../services/viewed_history_service.dart';
|
||||
import '../utils/smart_blacklist.dart';
|
||||
import 'settings_service.dart';
|
||||
|
||||
class FeedService {
|
||||
FeedService(this._providerManager, this._cacheService, this._settingsService);
|
||||
FeedService(
|
||||
this._providerManager,
|
||||
this._cacheService,
|
||||
this._settingsService,
|
||||
this._viewedHistoryService,
|
||||
);
|
||||
|
||||
final ProviderManager _providerManager;
|
||||
final CacheService _cacheService;
|
||||
final SettingsService _settingsService;
|
||||
final ViewedHistoryService _viewedHistoryService;
|
||||
final Map<String, int> _pages = {};
|
||||
|
||||
String _key(
|
||||
@@ -61,10 +69,65 @@ class FeedService {
|
||||
final posts = (result as Success<List<Post>>).data;
|
||||
_pages[key] = page + 1;
|
||||
final settingsResult = await _settingsService.getSettings();
|
||||
final maxItems = settingsResult is Success<AppSettings>
|
||||
? settingsResult.data.cacheMaxItems
|
||||
: null;
|
||||
await _cacheService.cachePosts(posts, maxItems: maxItems);
|
||||
return Success(posts);
|
||||
final settings = settingsResult is Success<AppSettings>
|
||||
? settingsResult.data
|
||||
: AppSettings.defaults;
|
||||
final viewedKeysResult = settings.hideViewedPosts
|
||||
? await _viewedHistoryService.getViewedKeys()
|
||||
: const Success(<String>{});
|
||||
final viewedKeys = viewedKeysResult is Success<Set<String>>
|
||||
? viewedKeysResult.data
|
||||
: <String>{};
|
||||
final filteredPosts = posts
|
||||
.where((post) => postMatchesRequestedTags(post, tags))
|
||||
.where((post) => postPassesTagFilters(post, settings))
|
||||
.where((post) =>
|
||||
!settings.hideViewedPosts || !viewedKeys.contains(post.cacheKey))
|
||||
.toList(growable: false);
|
||||
await _cacheService.cachePosts(
|
||||
filteredPosts,
|
||||
maxItems: settings.cacheMaxItems,
|
||||
);
|
||||
return Success(filteredPosts);
|
||||
}
|
||||
}
|
||||
|
||||
bool postMatchesRequestedTags(Post post, List<String> requestedTags) {
|
||||
final requested = requestedTags
|
||||
.map((tag) => tag.trim().toLowerCase())
|
||||
.where((tag) => tag.isNotEmpty && !tag.startsWith('-'))
|
||||
.toList(growable: false);
|
||||
if (requested.isEmpty) return true;
|
||||
final postTags = _postTagSet(post);
|
||||
return requested.every(postTags.contains);
|
||||
}
|
||||
|
||||
bool postPassesTagFilters(Post post, AppSettings settings) {
|
||||
final tags = _postTagSet(post);
|
||||
if (tags.isEmpty) return settings.whitelistedTags.isEmpty;
|
||||
|
||||
final blacklist = [
|
||||
...settings.smartBlacklistRules,
|
||||
...settings.blacklistedTags,
|
||||
].map((tag) => tag.trim().toLowerCase()).where((tag) => tag.isNotEmpty);
|
||||
if (blacklist.any((rule) => SmartBlacklistMatcher.matches(post, rule))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final whitelist = settings.whitelistedTags
|
||||
.map((tag) => tag.trim().toLowerCase())
|
||||
.where((tag) => tag.isNotEmpty)
|
||||
.toList(growable: false);
|
||||
if (whitelist.isEmpty) return true;
|
||||
return whitelist.any(tags.contains);
|
||||
}
|
||||
|
||||
Set<String> _postTagSet(Post post) {
|
||||
return <String>{
|
||||
...post.tags,
|
||||
for (final group in post.tagGroups.values) ...group,
|
||||
}
|
||||
.map((tag) => tag.trim().toLowerCase())
|
||||
.where((tag) => tag.isNotEmpty)
|
||||
.toSet();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@ class AppSettings {
|
||||
required this.selectedFeedProviderIds,
|
||||
required this.showPostBadges,
|
||||
required this.defaultTopPeriodFilter,
|
||||
required this.blacklistedTags,
|
||||
required this.whitelistedTags,
|
||||
required this.smartBlacklistRules,
|
||||
required this.hideViewedPosts,
|
||||
this.defaultRatingFilter,
|
||||
});
|
||||
|
||||
@@ -40,6 +44,10 @@ class AppSettings {
|
||||
final List<String> selectedFeedProviderIds;
|
||||
final bool showPostBadges;
|
||||
final String defaultTopPeriodFilter;
|
||||
final List<String> blacklistedTags;
|
||||
final List<String> whitelistedTags;
|
||||
final List<String> smartBlacklistRules;
|
||||
final bool hideViewedPosts;
|
||||
final String? defaultRatingFilter;
|
||||
|
||||
static const defaults = AppSettings(
|
||||
@@ -56,6 +64,10 @@ class AppSettings {
|
||||
selectedFeedProviderIds: [],
|
||||
showPostBadges: true,
|
||||
defaultTopPeriodFilter: 'none',
|
||||
blacklistedTags: [],
|
||||
whitelistedTags: [],
|
||||
smartBlacklistRules: [],
|
||||
hideViewedPosts: false,
|
||||
defaultRatingFilter: null,
|
||||
);
|
||||
|
||||
@@ -73,6 +85,10 @@ class AppSettings {
|
||||
List<String>? selectedFeedProviderIds,
|
||||
bool? showPostBadges,
|
||||
String? defaultTopPeriodFilter,
|
||||
List<String>? blacklistedTags,
|
||||
List<String>? whitelistedTags,
|
||||
List<String>? smartBlacklistRules,
|
||||
bool? hideViewedPosts,
|
||||
String? defaultRatingFilter,
|
||||
}) {
|
||||
return AppSettings(
|
||||
@@ -91,6 +107,10 @@ class AppSettings {
|
||||
showPostBadges: showPostBadges ?? this.showPostBadges,
|
||||
defaultTopPeriodFilter:
|
||||
defaultTopPeriodFilter ?? this.defaultTopPeriodFilter,
|
||||
blacklistedTags: blacklistedTags ?? this.blacklistedTags,
|
||||
whitelistedTags: whitelistedTags ?? this.whitelistedTags,
|
||||
smartBlacklistRules: smartBlacklistRules ?? this.smartBlacklistRules,
|
||||
hideViewedPosts: hideViewedPosts ?? this.hideViewedPosts,
|
||||
defaultRatingFilter: defaultRatingFilter ?? this.defaultRatingFilter,
|
||||
);
|
||||
}
|
||||
@@ -109,6 +129,10 @@ class AppSettings {
|
||||
'selectedFeedProviderIds': selectedFeedProviderIds,
|
||||
'showPostBadges': showPostBadges,
|
||||
'defaultTopPeriodFilter': defaultTopPeriodFilter,
|
||||
'blacklistedTags': blacklistedTags,
|
||||
'whitelistedTags': whitelistedTags,
|
||||
'smartBlacklistRules': smartBlacklistRules,
|
||||
'hideViewedPosts': hideViewedPosts,
|
||||
'defaultRatingFilter': defaultRatingFilter,
|
||||
};
|
||||
|
||||
@@ -141,6 +165,19 @@ class AppSettings {
|
||||
(json['showPostBadges'] as bool?) ?? defaults.showPostBadges,
|
||||
defaultTopPeriodFilter: (json['defaultTopPeriodFilter'] as String?) ??
|
||||
defaults.defaultTopPeriodFilter,
|
||||
blacklistedTags: List<String>.from(
|
||||
(json['blacklistedTags'] as List?) ?? defaults.blacklistedTags,
|
||||
),
|
||||
whitelistedTags: List<String>.from(
|
||||
(json['whitelistedTags'] as List?) ?? defaults.whitelistedTags,
|
||||
),
|
||||
smartBlacklistRules: List<String>.from(
|
||||
(json['smartBlacklistRules'] as List?) ??
|
||||
(json['blacklistedTags'] as List?) ??
|
||||
defaults.smartBlacklistRules,
|
||||
),
|
||||
hideViewedPosts:
|
||||
(json['hideViewedPosts'] as bool?) ?? defaults.hideViewedPosts,
|
||||
defaultRatingFilter: json['defaultRatingFilter'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import '../../core/utils/result.dart';
|
||||
import '../models/post.dart';
|
||||
import '../models/viewed_post.dart';
|
||||
import '../repositories/post_repository.dart';
|
||||
import '../repositories/viewed_post_repository.dart';
|
||||
|
||||
class ViewedHistoryService {
|
||||
ViewedHistoryService(this._repository, this._postRepository);
|
||||
|
||||
final ViewedPostRepository _repository;
|
||||
final PostRepository _postRepository;
|
||||
|
||||
Future<Result<void>> markViewed(Post post) async {
|
||||
final cacheResult = await _postRepository.cachePosts([post]);
|
||||
if (cacheResult is Error<void>) return cacheResult;
|
||||
return _repository.markViewed(post.providerId, post.id);
|
||||
}
|
||||
|
||||
Future<Result<bool>> isViewed(String postId, String providerId) {
|
||||
return _repository.exists(providerId, postId);
|
||||
}
|
||||
|
||||
Future<Result<Set<String>>> getViewedKeys() {
|
||||
return _repository.keys();
|
||||
}
|
||||
|
||||
Future<Result<List<Post>>> getViewedPosts() async {
|
||||
final viewedResult = await _repository.recent();
|
||||
if (viewedResult is Error<List<ViewedPost>>) {
|
||||
return Error(viewedResult.failure);
|
||||
}
|
||||
final viewed = (viewedResult as Success<List<ViewedPost>>).data;
|
||||
return _postRepository.getCachedPostsByKeys(
|
||||
viewed.map((item) => item.viewedKey),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Result<void>> clearHistory() {
|
||||
return _repository.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user