mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-26 11:17:22 +08:00
allow marking stories as read from homepage. (#319)
This commit is contained in:
@ -76,6 +76,15 @@ final class SharedPrefsCore {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fileprivate func remove(key: String?) -> Bool{
|
||||
if let key = key {
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
keyStore.removeObject(forKey: key)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public class SwiftSyncedSharedPreferencesPlugin: NSObject, FlutterPlugin {
|
||||
@ -87,6 +96,14 @@ public class SwiftSyncedSharedPreferencesPlugin: NSObject, FlutterPlugin {
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "remove":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let key = params[keyKey] as? String
|
||||
|
||||
let res = SharedPrefsCore.shared.remove(key: key)
|
||||
result(res)
|
||||
}
|
||||
|
||||
case "setBool":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let val = params[valKey] as? Bool
|
||||
|
@ -15,6 +15,14 @@ class SyncedSharedPreferences {
|
||||
const MethodChannel(channel),
|
||||
);
|
||||
|
||||
Future<bool?> remove({
|
||||
required String key,
|
||||
}) async {
|
||||
return _channel.invokeMethod('remove', <String, dynamic>{
|
||||
'key': key,
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool?> setBool({
|
||||
required String key,
|
||||
required bool val,
|
||||
|
@ -11,13 +11,13 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
AuthBloc({
|
||||
AuthRepository? authRepository,
|
||||
PreferenceRepository? preferenceRepository,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
SembastRepository? sembastRepository,
|
||||
}) : _authRepository = authRepository ?? locator.get<AuthRepository>(),
|
||||
_preferenceRepository =
|
||||
preferenceRepository ?? locator.get<PreferenceRepository>(),
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
_sembastRepository =
|
||||
sembastRepository ?? locator.get<SembastRepository>(),
|
||||
super(const AuthState.init()) {
|
||||
@ -31,7 +31,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
|
||||
final AuthRepository _authRepository;
|
||||
final PreferenceRepository _preferenceRepository;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
final SembastRepository _sembastRepository;
|
||||
|
||||
Future<void> onInitialize(
|
||||
@ -41,7 +41,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
await _authRepository.loggedIn.then((bool loggedIn) async {
|
||||
if (loggedIn) {
|
||||
final String? username = await _authRepository.username;
|
||||
User? user = await _storiesRepository.fetchUser(id: username!);
|
||||
User? user = await _hackerNewsRepository.fetchUser(id: username!);
|
||||
|
||||
/// According to Hacker News' API documentation,
|
||||
/// if user has no public activity (posting a comment or story),
|
||||
@ -89,7 +89,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
);
|
||||
|
||||
if (successful) {
|
||||
final User? user = await _storiesRepository.fetchUser(id: event.username);
|
||||
final User? user =
|
||||
await _hackerNewsRepository.fetchUser(id: event.username);
|
||||
emit(
|
||||
state.copyWith(
|
||||
user: user ?? User.emptyWithId(event.username),
|
||||
|
@ -19,15 +19,15 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
required PreferenceCubit preferenceCubit,
|
||||
required FilterCubit filterCubit,
|
||||
OfflineRepository? offlineRepository,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
PreferenceRepository? preferenceRepository,
|
||||
Logger? logger,
|
||||
}) : _preferenceCubit = preferenceCubit,
|
||||
_filterCubit = filterCubit,
|
||||
_offlineRepository =
|
||||
offlineRepository ?? locator.get<OfflineRepository>(),
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
_preferenceRepository =
|
||||
preferenceRepository ?? locator.get<PreferenceRepository>(),
|
||||
_logger = logger ?? locator.get<Logger>(),
|
||||
@ -37,6 +37,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
on<StoriesLoadMore>(onLoadMore);
|
||||
on<StoryLoaded>(onStoryLoaded);
|
||||
on<StoryRead>(onStoryRead);
|
||||
on<StoryUnread>(onStoryUnread);
|
||||
on<StoriesLoaded>(onStoriesLoaded);
|
||||
on<StoriesDownload>(onDownload);
|
||||
on<StoriesCancelDownload>(onCancelDownload);
|
||||
@ -49,7 +50,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
final PreferenceCubit _preferenceCubit;
|
||||
final FilterCubit _filterCubit;
|
||||
final OfflineRepository _offlineRepository;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
final PreferenceRepository _preferenceRepository;
|
||||
final Logger _logger;
|
||||
DeviceScreenType? deviceScreenType;
|
||||
@ -113,13 +114,14 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
add(StoriesLoaded(type: type));
|
||||
});
|
||||
} else {
|
||||
final List<int> ids = await _storiesRepository.fetchStoryIds(type: type);
|
||||
final List<int> ids =
|
||||
await _hackerNewsRepository.fetchStoryIds(type: type);
|
||||
emit(
|
||||
state
|
||||
.copyWithStoryIdsUpdated(type: type, to: ids)
|
||||
.copyWithCurrentPageUpdated(type: type, to: 0),
|
||||
);
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchStoriesStream(ids: ids.sublist(0, state.currentPageSize))
|
||||
.listen((Story story) {
|
||||
add(StoryLoaded(story: story, type: type));
|
||||
@ -196,7 +198,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
add(StoriesLoaded(type: event.type));
|
||||
});
|
||||
} else {
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchStoriesStream(
|
||||
ids: state.storyIdsByType[event.type]!.sublist(
|
||||
lower,
|
||||
@ -273,7 +275,8 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
..remove(StoryType.latest);
|
||||
|
||||
for (final StoryType type in prioritizedTypes) {
|
||||
final List<int> ids = await _storiesRepository.fetchStoryIds(type: type);
|
||||
final List<int> ids =
|
||||
await _hackerNewsRepository.fetchStoryIds(type: type);
|
||||
await _offlineRepository.cacheStoryIds(type: type, ids: ids);
|
||||
prioritizedIds.addAll(ids);
|
||||
}
|
||||
@ -293,7 +296,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
);
|
||||
|
||||
final Set<int> latestIds = <int>{};
|
||||
final List<int> ids = await _storiesRepository.fetchStoryIds(
|
||||
final List<int> ids = await _hackerNewsRepository.fetchStoryIds(
|
||||
type: StoryType.latest,
|
||||
);
|
||||
await _offlineRepository.cacheStoryIds(type: StoryType.latest, ids: ids);
|
||||
@ -347,7 +350,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
}
|
||||
|
||||
_logger.d('fetching story $id');
|
||||
final Story? story = await _storiesRepository.fetchStory(id: id);
|
||||
final Story? story = await _hackerNewsRepository.fetchStory(id: id);
|
||||
|
||||
if (story == null) {
|
||||
if (isPrioritized) {
|
||||
@ -377,7 +380,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
/// In other words, we are prioritizing the story itself instead of
|
||||
/// the comments in the story.
|
||||
late final StreamSubscription<Comment>? downloadStream;
|
||||
downloadStream = _storiesRepository
|
||||
downloadStream = _hackerNewsRepository
|
||||
.fetchAllChildrenComments(ids: story.kids)
|
||||
.whereType<Comment>()
|
||||
.listen(
|
||||
@ -460,7 +463,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
StoryRead event,
|
||||
Emitter<StoriesState> emit,
|
||||
) async {
|
||||
unawaited(_preferenceRepository.updateHasRead(event.story.id));
|
||||
unawaited(_preferenceRepository.addHasRead(event.story.id));
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
@ -469,6 +472,19 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onStoryUnread(
|
||||
StoryUnread event,
|
||||
Emitter<StoriesState> emit,
|
||||
) async {
|
||||
unawaited(_preferenceRepository.removeHasRead(event.story.id));
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
readStoriesIds: <int>{...state.readStoriesIds}..remove(event.story.id),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> onClearAllReadStories(
|
||||
ClearAllReadStories event,
|
||||
Emitter<StoriesState> emit,
|
||||
|
@ -95,6 +95,15 @@ class StoryRead extends StoriesEvent {
|
||||
List<Object?> get props => <Object?>[story];
|
||||
}
|
||||
|
||||
class StoryUnread extends StoriesEvent {
|
||||
StoryUnread({required this.story});
|
||||
|
||||
final Story story;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[story];
|
||||
}
|
||||
|
||||
class ClearAllReadStories extends StoriesEvent {
|
||||
@override
|
||||
List<Object?> get props => <Object?>[];
|
||||
|
@ -24,7 +24,7 @@ Future<void> setUpLocator() async {
|
||||
),
|
||||
)
|
||||
..registerSingleton<SembastRepository>(SembastRepository())
|
||||
..registerSingleton<StoriesRepository>(StoriesRepository())
|
||||
..registerSingleton<HackerNewsRepository>(HackerNewsRepository())
|
||||
..registerSingleton<PreferenceRepository>(PreferenceRepository())
|
||||
..registerSingleton<SearchRepository>(SearchRepository())
|
||||
..registerSingleton<AuthRepository>(AuthRepository())
|
||||
|
@ -32,7 +32,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
required CommentsOrder defaultCommentsOrder,
|
||||
CommentCache? commentCache,
|
||||
OfflineRepository? offlineRepository,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
SembastRepository? sembastRepository,
|
||||
Logger? logger,
|
||||
}) : _filterCubit = filterCubit,
|
||||
@ -40,8 +40,8 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
_commentCache = commentCache ?? locator.get<CommentCache>(),
|
||||
_offlineRepository =
|
||||
offlineRepository ?? locator.get<OfflineRepository>(),
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
_sembastRepository =
|
||||
sembastRepository ?? locator.get<SembastRepository>(),
|
||||
_logger = logger ?? locator.get<Logger>(),
|
||||
@ -58,7 +58,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
final CollapseCache _collapseCache;
|
||||
final CommentCache _commentCache;
|
||||
final OfflineRepository _offlineRepository;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
final SembastRepository _sembastRepository;
|
||||
final Logger _logger;
|
||||
|
||||
@ -96,7 +96,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
),
|
||||
);
|
||||
|
||||
_streamSubscription = _storiesRepository
|
||||
_streamSubscription = _hackerNewsRepository
|
||||
.fetchAllCommentsRecursivelyStream(
|
||||
ids: targetAncestors!.last.kids,
|
||||
level: targetAncestors.last.level + 1,
|
||||
@ -122,7 +122,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
final Item item = state.item;
|
||||
final Item updatedItem = state.isOfflineReading
|
||||
? item
|
||||
: await _storiesRepository
|
||||
: await _hackerNewsRepository
|
||||
.fetchItem(id: item.id)
|
||||
.then(_toBuildable)
|
||||
.onError((_, __) => item) ??
|
||||
@ -138,12 +138,13 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
} else {
|
||||
switch (state.fetchMode) {
|
||||
case FetchMode.lazy:
|
||||
commentStream = _storiesRepository.fetchCommentsStream(
|
||||
commentStream = _hackerNewsRepository.fetchCommentsStream(
|
||||
ids: kids,
|
||||
getFromCache: useCommentCache ? _commentCache.getComment : null,
|
||||
);
|
||||
case FetchMode.eager:
|
||||
commentStream = _storiesRepository.fetchAllCommentsRecursivelyStream(
|
||||
commentStream =
|
||||
_hackerNewsRepository.fetchAllCommentsRecursivelyStream(
|
||||
ids: kids,
|
||||
getFromCache: useCommentCache ? _commentCache.getComment : null,
|
||||
);
|
||||
@ -190,16 +191,16 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
|
||||
final Item item = state.item;
|
||||
final Item updatedItem =
|
||||
await _storiesRepository.fetchItem(id: item.id) ?? item;
|
||||
await _hackerNewsRepository.fetchItem(id: item.id) ?? item;
|
||||
final List<int> kids = _sortKids(updatedItem.kids);
|
||||
|
||||
late final Stream<Comment> commentStream;
|
||||
if (state.fetchMode == FetchMode.lazy) {
|
||||
commentStream = _storiesRepository.fetchCommentsStream(
|
||||
commentStream = _hackerNewsRepository.fetchCommentsStream(
|
||||
ids: kids,
|
||||
);
|
||||
} else {
|
||||
commentStream = _storiesRepository.fetchAllCommentsRecursivelyStream(
|
||||
commentStream = _hackerNewsRepository.fetchAllCommentsRecursivelyStream(
|
||||
ids: kids,
|
||||
);
|
||||
}
|
||||
@ -248,7 +249,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
/// Ignoring because the subscription will be cancelled in close()
|
||||
// ignore: cancel_subscriptions
|
||||
final StreamSubscription<Comment> streamSubscription =
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchCommentsStream(ids: comment.kids)
|
||||
.asyncMap(_toBuildableComment)
|
||||
.whereNotNull()
|
||||
@ -297,7 +298,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
HapticFeedbackUtil.light();
|
||||
emit(state.copyWith(fetchParentStatus: CommentsStatus.inProgress));
|
||||
final Item? parent =
|
||||
await _storiesRepository.fetchItem(id: state.item.parent);
|
||||
await _hackerNewsRepository.fetchItem(id: state.item.parent);
|
||||
|
||||
if (parent == null) {
|
||||
return;
|
||||
@ -318,7 +319,7 @@ class CommentsCubit extends Cubit<CommentsState> {
|
||||
Future<void> loadRootThread() async {
|
||||
HapticFeedbackUtil.light();
|
||||
emit(state.copyWith(fetchRootStatus: CommentsStatus.inProgress));
|
||||
final Story? parent = await _storiesRepository
|
||||
final Story? parent = await _hackerNewsRepository
|
||||
.fetchParentStory(id: state.item.id)
|
||||
.then(_toBuildableStory);
|
||||
|
||||
|
@ -12,13 +12,13 @@ class FavCubit extends Cubit<FavState> {
|
||||
required AuthBloc authBloc,
|
||||
AuthRepository? authRepository,
|
||||
PreferenceRepository? preferenceRepository,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
}) : _authBloc = authBloc,
|
||||
_authRepository = authRepository ?? locator.get<AuthRepository>(),
|
||||
_preferenceRepository =
|
||||
preferenceRepository ?? locator.get<PreferenceRepository>(),
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
super(FavState.init()) {
|
||||
init();
|
||||
}
|
||||
@ -26,7 +26,7 @@ class FavCubit extends Cubit<FavState> {
|
||||
final AuthBloc _authBloc;
|
||||
final AuthRepository _authRepository;
|
||||
final PreferenceRepository _preferenceRepository;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
static const int _pageSize = 20;
|
||||
String? _username;
|
||||
|
||||
@ -43,7 +43,7 @@ class FavCubit extends Cubit<FavState> {
|
||||
currentPage: 0,
|
||||
),
|
||||
);
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchItemsStream(
|
||||
ids: favIds.sublist(0, _pageSize.clamp(0, favIds.length)),
|
||||
)
|
||||
@ -73,7 +73,7 @@ class FavCubit extends Cubit<FavState> {
|
||||
),
|
||||
);
|
||||
|
||||
final Item? item = await _storiesRepository.fetchItem(id: id);
|
||||
final Item? item = await _hackerNewsRepository.fetchItem(id: id);
|
||||
|
||||
if (item == null) return;
|
||||
|
||||
@ -119,7 +119,7 @@ class FavCubit extends Cubit<FavState> {
|
||||
upper = len;
|
||||
}
|
||||
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchItemsStream(
|
||||
ids: state.favIds.sublist(
|
||||
lower,
|
||||
@ -149,7 +149,7 @@ class FavCubit extends Cubit<FavState> {
|
||||
|
||||
_preferenceRepository.favList(of: username).then((List<int> favIds) {
|
||||
emit(state.copyWith(favIds: favIds));
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchItemsStream(
|
||||
ids: favIds.sublist(0, _pageSize.clamp(0, favIds.length)),
|
||||
)
|
||||
|
@ -10,16 +10,16 @@ part 'history_state.dart';
|
||||
class HistoryCubit extends Cubit<HistoryState> {
|
||||
HistoryCubit({
|
||||
required AuthBloc authBloc,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
}) : _authBloc = authBloc,
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
super(HistoryState.init()) {
|
||||
init();
|
||||
}
|
||||
|
||||
final AuthBloc _authBloc;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
static const int _pageSize = 20;
|
||||
|
||||
void init() {
|
||||
@ -27,7 +27,7 @@ class HistoryCubit extends Cubit<HistoryState> {
|
||||
if (authState.isLoggedIn) {
|
||||
final String username = authState.username;
|
||||
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchSubmitted(userId: username)
|
||||
.then((List<int>? submittedIds) {
|
||||
emit(
|
||||
@ -38,7 +38,7 @@ class HistoryCubit extends Cubit<HistoryState> {
|
||||
),
|
||||
);
|
||||
if (submittedIds != null) {
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchItemsStream(
|
||||
ids: submittedIds.sublist(
|
||||
0,
|
||||
@ -66,7 +66,7 @@ class HistoryCubit extends Cubit<HistoryState> {
|
||||
upper = len;
|
||||
}
|
||||
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchItemsStream(
|
||||
ids: state.submittedIds.sublist(
|
||||
lower,
|
||||
@ -93,12 +93,12 @@ class HistoryCubit extends Cubit<HistoryState> {
|
||||
),
|
||||
);
|
||||
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchSubmitted(userId: username)
|
||||
.then((List<int>? submittedIds) {
|
||||
emit(state.copyWith(submittedIds: submittedIds));
|
||||
if (submittedIds != null) {
|
||||
_storiesRepository
|
||||
_hackerNewsRepository
|
||||
.fetchItemsStream(
|
||||
ids: submittedIds.sublist(
|
||||
0,
|
||||
|
@ -16,13 +16,13 @@ class NotificationCubit extends Cubit<NotificationState> {
|
||||
NotificationCubit({
|
||||
required AuthBloc authBloc,
|
||||
required PreferenceCubit preferenceCubit,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
PreferenceRepository? preferenceRepository,
|
||||
SembastRepository? sembastRepository,
|
||||
}) : _authBloc = authBloc,
|
||||
_preferenceCubit = preferenceCubit,
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
_preferenceRepository =
|
||||
preferenceRepository ?? locator.get<PreferenceRepository>(),
|
||||
_sembastRepository =
|
||||
@ -54,7 +54,7 @@ class NotificationCubit extends Cubit<NotificationState> {
|
||||
|
||||
final AuthBloc _authBloc;
|
||||
final PreferenceCubit _preferenceCubit;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
final PreferenceRepository _preferenceRepository;
|
||||
final SembastRepository _sembastRepository;
|
||||
String? _username;
|
||||
@ -82,7 +82,7 @@ class NotificationCubit extends Cubit<NotificationState> {
|
||||
|
||||
for (final int id in commentsToBeLoaded) {
|
||||
Comment? comment = await _sembastRepository.getComment(id: id);
|
||||
comment ??= await _storiesRepository.fetchComment(id: id);
|
||||
comment ??= await _hackerNewsRepository.fetchComment(id: id);
|
||||
if (comment != null) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
@ -160,7 +160,7 @@ class NotificationCubit extends Cubit<NotificationState> {
|
||||
|
||||
for (final int id in commentsToBeLoaded) {
|
||||
Comment? comment = await _sembastRepository.getComment(id: id);
|
||||
comment ??= await _storiesRepository.fetchComment(id: id);
|
||||
comment ??= await _hackerNewsRepository.fetchComment(id: id);
|
||||
if (comment != null) {
|
||||
emit(state.copyWith(comments: <Comment>[...state.comments, comment]));
|
||||
}
|
||||
@ -184,7 +184,7 @@ class NotificationCubit extends Cubit<NotificationState> {
|
||||
}
|
||||
|
||||
Future<void> _fetchReplies() {
|
||||
return _storiesRepository
|
||||
return _hackerNewsRepository
|
||||
.fetchSubmitted(userId: _authBloc.state.username)
|
||||
.then((List<int>? submittedItems) async {
|
||||
if (submittedItems != null) {
|
||||
@ -194,7 +194,9 @@ class NotificationCubit extends Cubit<NotificationState> {
|
||||
);
|
||||
|
||||
for (final int id in subscribedItems) {
|
||||
await _storiesRepository.fetchItem(id: id).then((Item? item) async {
|
||||
await _hackerNewsRepository
|
||||
.fetchItem(id: id)
|
||||
.then((Item? item) async {
|
||||
final List<int> kids = item?.kids ?? <int>[];
|
||||
final List<int> previousKids =
|
||||
(await _sembastRepository.kids(of: id)) ?? <int>[];
|
||||
@ -216,7 +218,7 @@ class NotificationCubit extends Cubit<NotificationState> {
|
||||
...state.unreadCommentsIds,
|
||||
]..sort((int lhs, int rhs) => rhs.compareTo(lhs)),
|
||||
);
|
||||
await _storiesRepository
|
||||
await _hackerNewsRepository
|
||||
.fetchComment(id: newCommentId)
|
||||
.then((Comment? comment) {
|
||||
if (comment != null && !comment.dead && !comment.deleted) {
|
||||
|
@ -10,24 +10,26 @@ part 'pin_state.dart';
|
||||
class PinCubit extends Cubit<PinState> {
|
||||
PinCubit({
|
||||
PreferenceRepository? preferenceRepository,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
}) : _preferenceRepository =
|
||||
preferenceRepository ?? locator.get<PreferenceRepository>(),
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
super(PinState.init()) {
|
||||
init();
|
||||
}
|
||||
|
||||
final PreferenceRepository _preferenceRepository;
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
|
||||
void init() {
|
||||
emit(PinState.init());
|
||||
_preferenceRepository.pinnedStoriesIds.then((List<int> ids) {
|
||||
emit(state.copyWith(pinnedStoriesIds: ids));
|
||||
|
||||
_storiesRepository.fetchStoriesStream(ids: ids).listen(_onStoryFetched);
|
||||
_hackerNewsRepository
|
||||
.fetchStoriesStream(ids: ids)
|
||||
.listen(_onStoryFetched);
|
||||
}).whenComplete(() => emit(state.copyWith(status: Status.success)));
|
||||
}
|
||||
|
||||
|
@ -11,13 +11,13 @@ part 'poll_state.dart';
|
||||
class PollCubit extends Cubit<PollState> {
|
||||
PollCubit({
|
||||
required Story story,
|
||||
StoriesRepository? storiesRepository,
|
||||
HackerNewsRepository? hackerNewsRepository,
|
||||
}) : _story = story,
|
||||
_storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
_hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
super(PollState.init());
|
||||
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
final Story _story;
|
||||
|
||||
Future<void> init({
|
||||
@ -33,7 +33,7 @@ class PollCubit extends Cubit<PollState> {
|
||||
|
||||
if (pollOptionsIds.isEmpty || refresh) {
|
||||
final Story? updatedStory =
|
||||
await _storiesRepository.fetchStory(id: _story.id);
|
||||
await _hackerNewsRepository.fetchStory(id: _story.id);
|
||||
|
||||
if (updatedStory != null) {
|
||||
pollOptionsIds = updatedStory.parts;
|
||||
@ -47,7 +47,7 @@ class PollCubit extends Cubit<PollState> {
|
||||
}
|
||||
|
||||
if (pollOptionsIds.isNotEmpty) {
|
||||
final List<PollOption> pollOptions = (await _storiesRepository
|
||||
final List<PollOption> pollOptions = (await _hackerNewsRepository
|
||||
.fetchPollOptionsStream(ids: pollOptionsIds)
|
||||
.toSet())
|
||||
.toList();
|
||||
|
@ -7,16 +7,16 @@ import 'package:hacki/repositories/repositories.dart';
|
||||
part 'user_state.dart';
|
||||
|
||||
class UserCubit extends Cubit<UserState> {
|
||||
UserCubit({StoriesRepository? storiesRepository})
|
||||
: _storiesRepository =
|
||||
storiesRepository ?? locator.get<StoriesRepository>(),
|
||||
UserCubit({HackerNewsRepository? hackerNewsRepository})
|
||||
: _hackerNewsRepository =
|
||||
hackerNewsRepository ?? locator.get<HackerNewsRepository>(),
|
||||
super(const UserState.init());
|
||||
|
||||
final StoriesRepository _storiesRepository;
|
||||
final HackerNewsRepository _hackerNewsRepository;
|
||||
|
||||
void init({required String userId}) {
|
||||
emit(state.copyWith(status: Status.inProgress));
|
||||
_storiesRepository.fetchUser(id: userId).then((User? user) {
|
||||
_hackerNewsRepository.fetchUser(id: userId).then((User? user) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
user: user ?? User.emptyWithId(userId),
|
||||
|
@ -6,13 +6,13 @@ import 'package:hacki/services/services.dart';
|
||||
import 'package:hacki/utils/utils.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
|
||||
/// [StoriesRepository] is for fetching
|
||||
/// [HackerNewsRepository] is for fetching
|
||||
/// [Item] such as [Story], [PollOption], [Comment] or [User].
|
||||
///
|
||||
/// You can learn more about the Hacker News API at
|
||||
/// https://github.com/HackerNews/API.
|
||||
class StoriesRepository {
|
||||
StoriesRepository({
|
||||
class HackerNewsRepository {
|
||||
HackerNewsRepository({
|
||||
FirebaseClient? firebaseClient,
|
||||
SembastRepository? sembastRepository,
|
||||
Logger? logger,
|
||||
@ -239,7 +239,9 @@ class StoriesRepository {
|
||||
return comment;
|
||||
}).onError((Object? error, StackTrace stackTrace) {
|
||||
_logger.e(error, stackTrace: stackTrace);
|
||||
return _sembastRepository.getCachedComment(id: id);
|
||||
return _sembastRepository
|
||||
.getCachedComment(id: id)
|
||||
.then((Comment? value) => value?.copyWith(level: level));
|
||||
});
|
||||
|
||||
if (comment != null) {
|
||||
@ -267,7 +269,9 @@ class StoriesRepository {
|
||||
return comment;
|
||||
}).onError((Object? error, StackTrace stackTrace) {
|
||||
_logger.e(error, stackTrace: stackTrace);
|
||||
return _sembastRepository.getCachedComment(id: id);
|
||||
return _sembastRepository
|
||||
.getCachedComment(id: id)
|
||||
.then((Comment? value) => value?.copyWith(level: level));
|
||||
});
|
||||
|
||||
if (comment != null) {
|
@ -384,7 +384,7 @@ class PreferenceRepository {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> updateHasRead(int storyId) async {
|
||||
Future<void> addHasRead(int storyId) async {
|
||||
final String key = _getHasReadKey(storyId);
|
||||
if (Platform.isIOS) {
|
||||
await _syncedPrefs.setBool(key: key, val: true);
|
||||
@ -398,6 +398,17 @@ class PreferenceRepository {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeHasRead(int storyId) async {
|
||||
final String key = _getHasReadKey(storyId);
|
||||
if (Platform.isIOS) {
|
||||
await _syncedPrefs.remove(key: key);
|
||||
} else {
|
||||
final SharedPreferences prefs = await _prefs;
|
||||
|
||||
await prefs.remove(_getHasReadKey(storyId));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> clearAllReadStories() async {
|
||||
if (Platform.isIOS) {
|
||||
await _syncedPrefs.clearAll();
|
||||
|
@ -1,7 +1,7 @@
|
||||
export 'auth_repository.dart';
|
||||
export 'hacker_news_repository.dart';
|
||||
export 'offline_repository.dart';
|
||||
export 'post_repository.dart';
|
||||
export 'preference_repository.dart';
|
||||
export 'search_repository.dart';
|
||||
export 'sembast_repository.dart';
|
||||
export 'stories_repository.dart';
|
||||
|
@ -253,7 +253,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
final int? id = event.itemId;
|
||||
|
||||
if (id != null) {
|
||||
locator.get<StoriesRepository>().fetchItem(id: id).then((Item? item) {
|
||||
locator.get<HackerNewsRepository>().fetchItem(id: id).then((Item? item) {
|
||||
if (mounted) {
|
||||
if (item != null) {
|
||||
goToItemScreen(
|
||||
@ -272,7 +272,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
if (storyId == null) return;
|
||||
|
||||
await locator
|
||||
.get<StoriesRepository>()
|
||||
.get<HackerNewsRepository>()
|
||||
.fetchStory(id: storyId)
|
||||
.then((Story? story) {
|
||||
if (story == null) {
|
||||
@ -297,7 +297,7 @@ class _HomeScreenState extends State<HomeScreen>
|
||||
context.read<NotificationCubit>().markAsRead(commentId);
|
||||
|
||||
await locator
|
||||
.get<StoriesRepository>()
|
||||
.get<HackerNewsRepository>()
|
||||
.fetchStory(id: storyId)
|
||||
.then((Story? story) {
|
||||
if (story == null) {
|
||||
|
@ -33,8 +33,8 @@ class ItemScreenArgs extends Equatable {
|
||||
final List<Comment>? targetComments;
|
||||
|
||||
/// when the user is trying to view a sub-thread from a main thread, we don't
|
||||
/// need to fetch comments from [StoriesRepository] since we have some, if not
|
||||
/// all, comments cached in [CommentCache].
|
||||
/// need to fetch comments from [HackerNewsRepository] since we have some,
|
||||
/// if not all, comments cached in [CommentCache].
|
||||
final bool useCommentCache;
|
||||
|
||||
@override
|
||||
|
@ -378,7 +378,7 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
void onCommentTapped(Comment comment, {VoidCallback? then}) {
|
||||
throttle.run(() {
|
||||
locator
|
||||
.get<StoriesRepository>()
|
||||
.get<HackerNewsRepository>()
|
||||
.fetchParentStoryWithComments(id: comment.parent)
|
||||
.then(((Story, List<Comment>)? res) {
|
||||
if (res != null && mounted) {
|
||||
|
@ -317,6 +317,9 @@ class _SearchScreenState extends State<SearchScreen> with ItemActionMixin {
|
||||
label: filter.query,
|
||||
),
|
||||
],
|
||||
const SizedBox(
|
||||
width: Dimens.pt8,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -107,7 +107,7 @@ class _CountDownReminderState extends State<CountdownReminder>
|
||||
onTap: () {
|
||||
if (state.storyId != null) {
|
||||
locator
|
||||
.get<StoriesRepository>()
|
||||
.get<HackerNewsRepository>()
|
||||
.fetchStory(id: state.storyId!)
|
||||
.then((Story? story) {
|
||||
if (story == null) {
|
||||
|
@ -27,26 +27,35 @@ class CodeLinkifier extends Linkifier {
|
||||
list.add(element);
|
||||
} else {
|
||||
final String matchedText = match.group(0)!;
|
||||
final num pos = element.text.indexOf(matchedText);
|
||||
final List<String> splitTexts = element.text.split(matchedText);
|
||||
|
||||
int curPos = 0;
|
||||
bool added = false;
|
||||
final String preceding = splitTexts[0];
|
||||
|
||||
for (final String text in splitTexts) {
|
||||
list.addAll(parse(<LinkifyElement>[TextElement(text)], options));
|
||||
list.addAll(
|
||||
parse(
|
||||
<LinkifyElement>[
|
||||
TextElement(preceding == '\n\n' ? '' : preceding),
|
||||
],
|
||||
options,
|
||||
),
|
||||
);
|
||||
|
||||
curPos += text.length;
|
||||
|
||||
if (!added && curPos >= pos) {
|
||||
added = true;
|
||||
final String trimmedText = matchedText
|
||||
String trimmedText = matchedText
|
||||
.replaceFirst(_openTag, '')
|
||||
.replaceFirst(_closeTag, '')
|
||||
.replaceAll('\n\n', '\n');
|
||||
list.add(CodeElement(trimmedText));
|
||||
}
|
||||
}
|
||||
trimmedText = '$trimmedText\n\n';
|
||||
|
||||
list
|
||||
..add(CodeElement(trimmedText))
|
||||
..addAll(
|
||||
parse(
|
||||
<LinkifyElement>[
|
||||
TextElement(splitTexts[1]),
|
||||
],
|
||||
options,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
list.add(element);
|
||||
|
@ -146,6 +146,7 @@ class _StoriesListViewState extends State<StoriesListView>
|
||||
onMoreTapped: onMoreTapped,
|
||||
itemBuilder: (Widget child, Story story) {
|
||||
return Slidable(
|
||||
key: ValueKey<Story>(story),
|
||||
enabled: !preferenceState.swipeGestureEnabled,
|
||||
startActionPane: ActionPane(
|
||||
motion: const BehindMotion(),
|
||||
@ -179,6 +180,31 @@ class _StoriesListViewState extends State<StoriesListView>
|
||||
),
|
||||
],
|
||||
),
|
||||
endActionPane: ActionPane(
|
||||
motion: const BehindMotion(),
|
||||
dismissible: DismissiblePane(
|
||||
closeOnCancel: true,
|
||||
confirmDismiss: () async {
|
||||
mark(story);
|
||||
return false;
|
||||
},
|
||||
onDismissed: () {},
|
||||
),
|
||||
children: <Widget>[
|
||||
SlidableAction(
|
||||
onPressed: (_) {
|
||||
HapticFeedbackUtil.light();
|
||||
mark(story);
|
||||
},
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onPrimary,
|
||||
icon: state.readStoriesIds.contains(story.id)
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: OptionalWrapper(
|
||||
enabled: context
|
||||
.read<PreferenceCubit>()
|
||||
@ -212,6 +238,15 @@ class _StoriesListViewState extends State<StoriesListView>
|
||||
);
|
||||
}
|
||||
|
||||
void mark(Story story) {
|
||||
final StoriesBloc storiesBloc = context.read<StoriesBloc>();
|
||||
if (storiesBloc.state.readStoriesIds.contains(story.id)) {
|
||||
context.read<StoriesBloc>().add(StoryUnread(story: story));
|
||||
} else {
|
||||
context.read<StoriesBloc>().add(StoryRead(story: story));
|
||||
}
|
||||
}
|
||||
|
||||
void loadMoreStories() =>
|
||||
context.read<StoriesBloc>().add(StoriesLoadMore(type: widget.storyType));
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ abstract class Fetcher {
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
final StoriesRepository storiesRepository = StoriesRepository();
|
||||
final HackerNewsRepository hackerNewsRepository = HackerNewsRepository();
|
||||
final SembastRepository sembastRepository = SembastRepository();
|
||||
|
||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
@ -57,7 +57,7 @@ abstract class Fetcher {
|
||||
|
||||
Comment? newReply;
|
||||
|
||||
await storiesRepository
|
||||
await hackerNewsRepository
|
||||
.fetchSubmitted(userId: username)
|
||||
.then((List<int>? submittedItems) async {
|
||||
if (submittedItems != null) {
|
||||
@ -67,7 +67,9 @@ abstract class Fetcher {
|
||||
);
|
||||
|
||||
for (final int id in subscribedItems) {
|
||||
await storiesRepository.fetchRawItem(id: id).then((Item? item) async {
|
||||
await hackerNewsRepository
|
||||
.fetchRawItem(id: id)
|
||||
.then((Item? item) async {
|
||||
final List<int> kids = item?.kids ?? <int>[];
|
||||
final List<int> previousKids =
|
||||
(await sembastRepository.kids(of: id)) ?? <int>[];
|
||||
@ -81,7 +83,7 @@ abstract class Fetcher {
|
||||
for (final int newCommentId in diff) {
|
||||
if (unreadIds.contains(newCommentId)) continue;
|
||||
|
||||
await storiesRepository
|
||||
await hackerNewsRepository
|
||||
.fetchRawComment(id: newCommentId)
|
||||
.then((Comment? comment) async {
|
||||
final bool hasPushedBefore =
|
||||
@ -113,7 +115,7 @@ abstract class Fetcher {
|
||||
// pushed before.
|
||||
if (newReply != null) {
|
||||
final Story? story =
|
||||
await storiesRepository.fetchRawParentStory(id: newReply!.id);
|
||||
await hackerNewsRepository.fetchRawParentStory(id: newReply!.id);
|
||||
final String text = HtmlUtil.parseHtml(newReply!.text);
|
||||
|
||||
if (story != null) {
|
||||
|
@ -288,14 +288,14 @@ class WebAnalyzer {
|
||||
}
|
||||
|
||||
static Future<String?> _fetchInfoFromStory(List<int> meta) async {
|
||||
final StoriesRepository storiesRepository = StoriesRepository();
|
||||
final HackerNewsRepository hackerNewsRepository = HackerNewsRepository();
|
||||
final int storyId = meta.first;
|
||||
List<int> kids = meta.sublist(1, meta.length);
|
||||
|
||||
// Kids of stories from search results are always empty, so here we try
|
||||
// to fetch the story itself first and see if the kids are still empty.
|
||||
if (kids.isEmpty) {
|
||||
final Story? story = await storiesRepository.fetchStory(id: storyId);
|
||||
final Story? story = await hackerNewsRepository.fetchStory(id: storyId);
|
||||
|
||||
if (story == null) return null;
|
||||
|
||||
@ -305,7 +305,7 @@ class WebAnalyzer {
|
||||
}
|
||||
|
||||
final Comment? comment =
|
||||
await storiesRepository.fetchComment(id: kids.first);
|
||||
await hackerNewsRepository.fetchComment(id: kids.first);
|
||||
|
||||
return comment != null ? '${comment.by}: ${comment.text}' : null;
|
||||
}
|
||||
|
@ -34,7 +34,11 @@ abstract class HtmlUtil {
|
||||
static String parseHtml(String text) {
|
||||
return HtmlUnescape()
|
||||
.convert(text)
|
||||
.replaceAll('<p>', '\n')
|
||||
.replaceAll('\n', '')
|
||||
.replaceAllMapped(
|
||||
RegExp(r'\<p\>(.*?)\<p\>', dotAll: true),
|
||||
(Match match) => '\n${match[1]?.replaceAll('\n', ' ')}\n',
|
||||
)
|
||||
.replaceAllMapped(
|
||||
RegExp(r'\<i\>(.*?)\<\/i\>'),
|
||||
(Match match) => '*${match[1]}*',
|
||||
@ -43,6 +47,7 @@ abstract class HtmlUtil {
|
||||
RegExp(r'\<a href=\"(.*?)\".*?\>.*?\<\/a\>'),
|
||||
(Match match) => match[1] ?? '',
|
||||
)
|
||||
.replaceAll('\n', '\n\n');
|
||||
.replaceAll('\n', '\n\n')
|
||||
.replaceAll('<p>', '\n\n');
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,10 @@ abstract class LinkUtil {
|
||||
if (useHackiForHnLink && link.isStoryLink) {
|
||||
final int? id = link.itemId;
|
||||
if (id != null) {
|
||||
locator.get<StoriesRepository>().fetchItem(id: id).then((Item? item) {
|
||||
locator
|
||||
.get<HackerNewsRepository>()
|
||||
.fetchItem(id: id)
|
||||
.then((Item? item) {
|
||||
if (item != null) {
|
||||
router.push(
|
||||
'/${ItemScreen.routeName}',
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: hacki
|
||||
description: A Hacker News reader.
|
||||
version: 2.3.2+131
|
||||
version: 2.4.0+131
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
|
@ -9,7 +9,7 @@ class MockAuthRepository extends Mock implements AuthRepository {}
|
||||
|
||||
class MockPreferenceRepository extends Mock implements PreferenceRepository {}
|
||||
|
||||
class MockStoriesRepository extends Mock implements StoriesRepository {}
|
||||
class MockHackerNewsRepository extends Mock implements HackerNewsRepository {}
|
||||
|
||||
class MockSembastRepository extends Mock implements SembastRepository {}
|
||||
|
||||
@ -17,7 +17,8 @@ void main() {
|
||||
final MockAuthRepository mockAuthRepository = MockAuthRepository();
|
||||
final MockPreferenceRepository mockPreferenceRepository =
|
||||
MockPreferenceRepository();
|
||||
final MockStoriesRepository mockStoriesRepository = MockStoriesRepository();
|
||||
final MockHackerNewsRepository mockHackerNewsRepository =
|
||||
MockHackerNewsRepository();
|
||||
final MockSembastRepository mockSembastRepository = MockSembastRepository();
|
||||
|
||||
const int created = 0;
|
||||
@ -49,7 +50,7 @@ void main() {
|
||||
AuthBloc(
|
||||
authRepository: mockAuthRepository,
|
||||
preferenceRepository: mockPreferenceRepository,
|
||||
storiesRepository: mockStoriesRepository,
|
||||
hackerNewsRepository: mockHackerNewsRepository,
|
||||
sembastRepository: mockSembastRepository,
|
||||
).state,
|
||||
equals(const AuthState.init()),
|
||||
@ -67,7 +68,7 @@ void main() {
|
||||
.thenAnswer((_) => Future<String?>.value(username));
|
||||
when(() => mockAuthRepository.password)
|
||||
.thenAnswer((_) => Future<String>.value(password));
|
||||
when(() => mockStoriesRepository.fetchUser(id: username))
|
||||
when(() => mockHackerNewsRepository.fetchUser(id: username))
|
||||
.thenAnswer((_) => Future<User>.value(tUser));
|
||||
when(() => mockAuthRepository.loggedIn)
|
||||
.thenAnswer((_) => Future<bool>.value(false));
|
||||
@ -79,7 +80,7 @@ void main() {
|
||||
return AuthBloc(
|
||||
authRepository: mockAuthRepository,
|
||||
preferenceRepository: mockPreferenceRepository,
|
||||
storiesRepository: mockStoriesRepository,
|
||||
hackerNewsRepository: mockHackerNewsRepository,
|
||||
sembastRepository: mockSembastRepository,
|
||||
);
|
||||
},
|
||||
@ -91,7 +92,7 @@ void main() {
|
||||
verify: (_) {
|
||||
verify(() => mockAuthRepository.loggedIn).called(2);
|
||||
verifyNever(() => mockAuthRepository.username);
|
||||
verifyNever(() => mockStoriesRepository.fetchUser(id: username));
|
||||
verifyNever(() => mockHackerNewsRepository.fetchUser(id: username));
|
||||
},
|
||||
);
|
||||
|
||||
@ -107,7 +108,7 @@ void main() {
|
||||
return AuthBloc(
|
||||
authRepository: mockAuthRepository,
|
||||
preferenceRepository: mockPreferenceRepository,
|
||||
storiesRepository: mockStoriesRepository,
|
||||
hackerNewsRepository: mockHackerNewsRepository,
|
||||
sembastRepository: mockSembastRepository,
|
||||
);
|
||||
},
|
||||
@ -154,7 +155,8 @@ void main() {
|
||||
password: password,
|
||||
),
|
||||
).called(1);
|
||||
verify(() => mockStoriesRepository.fetchUser(id: username)).called(1);
|
||||
verify(() => mockHackerNewsRepository.fetchUser(id: username))
|
||||
.called(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
Reference in New Issue
Block a user