Compare commits

...

4 Commits

Author SHA1 Message Date
381c99b353 fix crashing. (#248) 2023-09-08 09:07:48 -07:00
39ee3137f8 fix reply box in full screen. (#247) 2023-09-05 15:17:23 -07:00
0d76be8634 bump flutter version. (#243) 2023-08-22 06:54:59 -07:00
9986f72e11 improve shortcut buttons. (#242) 2023-07-19 21:09:24 -07:00
55 changed files with 413 additions and 556 deletions

View File

@ -9,13 +9,14 @@ on:
jobs:
releases:
name: Check commit
runs-on: ubuntu-latest
runs-on: macos-latest
timeout-minutes: 30
steps:
- name: checkout all the submodules
uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0
- run: submodules/flutter/bin/flutter doctor
- run: submodules/flutter/bin/flutter pub get
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test

View File

@ -23,6 +23,7 @@ jobs:
uses: actions/checkout@v3
with:
submodules: recursive
fetch-depth: 0
- run: submodules/flutter/bin/flutter doctor
- run: submodules/flutter/bin/flutter pub get
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test

View File

@ -1,121 +0,0 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:in_app_review_platform_interface/method_channel_in_app_review.dart';
import 'package:platform/platform.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
late MethodChannelInAppReview methodChannelInAppReview;
late List<MethodCall> log = <MethodCall>[];
const MethodChannel channel = MethodChannel('dev.britannio.in_app_review');
setUp(() {
methodChannelInAppReview = MethodChannelInAppReview();
methodChannelInAppReview.channel = channel;
log = <MethodCall>[];
});
tearDown(() {
log.clear();
});
group('isAvailable', () {
test(
'should invoke the isAvailable method channel',
() async {
// ACT
final bool result = await methodChannelInAppReview.isAvailable();
// ASSERT
expect(log, <Matcher>[isMethodCall('isAvailable', arguments: null)]);
expect(result, isTrue);
},
);
});
group('requestReview', () {
test(
'should invoke the requestReview method channel',
() async {
// ACT
await methodChannelInAppReview.requestReview();
// ASSERT
expect(log, <Matcher>[isMethodCall('requestReview', arguments: null)]);
},
);
});
group('openStoreListing', () {
test(
'should invoke the openStoreListing method channel on Android',
() async {
// ARRANGE
methodChannelInAppReview.platform =
FakePlatform(operatingSystem: 'android');
// ACT
await methodChannelInAppReview.openStoreListing();
// ASSERT
expect(
log,
<Matcher>[isMethodCall('openStoreListing', arguments: null)],
);
},
);
test(
'should invoke the openStoreListing method channel on iOS',
() async {
// ARRANGE
methodChannelInAppReview.platform =
FakePlatform(operatingSystem: 'ios');
final String appStoreId = "store_id";
// ACT
await methodChannelInAppReview.openStoreListing(appStoreId: appStoreId);
// ASSERT
expect(log,
<Matcher>[isMethodCall('openStoreListing', arguments: appStoreId)]);
},
);
test(
'should invoke the openStoreListing method channel on MacOS',
() async {
// ARRANGE
methodChannelInAppReview.platform =
FakePlatform(operatingSystem: 'macos');
final String appStoreId = "store_id";
// ACT
await methodChannelInAppReview.openStoreListing(appStoreId: appStoreId);
// ASSERT
expect(log,
<Matcher>[isMethodCall('openStoreListing', arguments: appStoreId)]);
},
);
test(
'should invoke the openStoreListing method channel on Windows',
() async {
// ARRANGE
methodChannelInAppReview.platform =
FakePlatform(operatingSystem: 'windows');
final String microsoftStoreId = 'store_id';
// ACT
await methodChannelInAppReview.openStoreListing(
microsoftStoreId: microsoftStoreId,
);
// ASSERT
expect(log, <Matcher>[
isMethodCall('openStoreListing', arguments: microsoftStoreId)
]);
},
skip:
'The windows uwp implementation still uses the url_launcher package',
);
});
}

View File

@ -151,12 +151,12 @@ SPEC CHECKSUMS:
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
receive_sharing_intent: c0d87310754e74c0f9542947e7cbdf3a0335a3b1
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
synced_shared_preferences: f722742b06d65c7315b8e9f56b794c9fbd5597f7
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4

View File

@ -291,7 +291,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1330;
LastUpgradeCheck = 1300;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -77,8 +77,10 @@
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
<key>io.flutter.embedded_views_preview</key>
<true/>
<string>This app needs camera access to scan QR codes</string>
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>FLTEnableWideGamut</key>
<false/>
</dict>
</plist>

View File

@ -52,14 +52,14 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
state.copyWith(
isLoggedIn: true,
user: user,
status: AuthStatus.loaded,
status: Status.success,
),
);
} else {
emit(
state.copyWith(
isLoggedIn: false,
status: AuthStatus.loaded,
status: Status.success,
),
);
}
@ -81,7 +81,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
}
Future<void> onLogin(AuthLogin event, Emitter<AuthState> emit) async {
emit(state.copyWith(status: AuthStatus.loading));
emit(state.copyWith(status: Status.inProgress));
final bool successful = await _authRepository.login(
username: event.username,
@ -94,11 +94,11 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
state.copyWith(
user: user ?? User.emptyWithId(event.username),
isLoggedIn: true,
status: AuthStatus.loaded,
status: Status.success,
),
);
} else {
emit(state.copyWith(status: AuthStatus.failure));
emit(state.copyWith(status: Status.failure));
}
}

View File

@ -1,11 +1,5 @@
part of 'auth_bloc.dart';
enum AuthStatus {
loading,
loaded,
failure,
}
class AuthState extends Equatable {
const AuthState({
required this.user,
@ -17,13 +11,13 @@ class AuthState extends Equatable {
const AuthState.init()
: user = const User.empty(),
isLoggedIn = false,
status = AuthStatus.loaded,
status = Status.success,
agreedToEULA = false;
final User user;
final bool isLoggedIn;
final bool agreedToEULA;
final AuthStatus status;
final Status status;
String get username => user.id;
@ -31,7 +25,7 @@ class AuthState extends Equatable {
User? user,
bool? isLoggedIn,
bool? agreedToEULA,
AuthStatus? status,
Status? status,
}) {
return AuthState(
user: user ?? this.user,

View File

@ -79,7 +79,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
const StoriesState.init().copyWith(
isOfflineReading: hasCachedStories &&
// Only go into offline mode in the next session.
state.downloadStatus == StoriesDownloadStatus.initial,
state.downloadStatus == StoriesDownloadStatus.idle,
currentPageSize: pageSize,
downloadStatus: state.downloadStatus,
storiesDownloaded: state.storiesDownloaded,
@ -133,12 +133,12 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
StoriesRefresh event,
Emitter<StoriesState> emit,
) async {
if (state.statusByType[event.type] == StoriesStatus.loading) return;
if (state.statusByType[event.type] == Status.inProgress) return;
emit(
state.copyWithStatusUpdated(
type: event.type,
to: StoriesStatus.loading,
to: Status.inProgress,
),
);
@ -146,7 +146,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
emit(
state.copyWithStatusUpdated(
type: event.type,
to: StoriesStatus.loaded,
to: Status.success,
),
);
} else {
@ -159,7 +159,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
emit(
state.copyWithStatusUpdated(
type: event.type,
to: StoriesStatus.loading,
to: Status.inProgress,
),
);
@ -218,7 +218,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
emit(
state.copyWithStatusUpdated(
type: event.type,
to: StoriesStatus.loaded,
to: Status.success,
),
);
}
@ -245,7 +245,7 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
void onStoriesLoaded(StoriesLoaded event, Emitter<StoriesState> emit) {
emit(
state.copyWithStatusUpdated(type: event.type, to: StoriesStatus.loaded),
state.copyWithStatusUpdated(type: event.type, to: Status.success),
);
}

View File

@ -1,13 +1,7 @@
part of 'stories_bloc.dart';
enum StoriesStatus {
initial,
loading,
loaded,
}
enum StoriesDownloadStatus {
initial,
idle,
downloading,
finished,
failure,
@ -43,12 +37,12 @@ class StoriesState extends Equatable {
StoryType.ask: <int>[],
StoryType.show: <int>[],
},
this.statusByType = const <StoryType, StoriesStatus>{
StoryType.top: StoriesStatus.initial,
StoryType.best: StoriesStatus.initial,
StoryType.latest: StoriesStatus.initial,
StoryType.ask: StoriesStatus.initial,
StoryType.show: StoriesStatus.initial,
this.statusByType = const <StoryType, Status>{
StoryType.top: Status.idle,
StoryType.best: Status.idle,
StoryType.latest: Status.idle,
StoryType.ask: Status.idle,
StoryType.show: Status.idle,
},
this.currentPageByType = const <StoryType, int>{
StoryType.top: 0,
@ -58,7 +52,7 @@ class StoriesState extends Equatable {
StoryType.show: 0,
},
}) : isOfflineReading = false,
downloadStatus = StoriesDownloadStatus.initial,
downloadStatus = StoriesDownloadStatus.idle,
currentPageSize = 0,
readStoriesIds = const <int>{},
storiesDownloaded = 0,
@ -66,7 +60,7 @@ class StoriesState extends Equatable {
final Map<StoryType, List<Story>> storiesByType;
final Map<StoryType, List<int>> storyIdsByType;
final Map<StoryType, StoriesStatus> statusByType;
final Map<StoryType, Status> statusByType;
final Map<StoryType, int> currentPageByType;
final Set<int> readStoriesIds;
final StoriesDownloadStatus downloadStatus;
@ -78,7 +72,7 @@ class StoriesState extends Equatable {
StoriesState copyWith({
Map<StoryType, List<Story>>? storiesByType,
Map<StoryType, List<int>>? storyIdsByType,
Map<StoryType, StoriesStatus>? statusByType,
Map<StoryType, Status>? statusByType,
Map<StoryType, int>? currentPageByType,
Set<int>? readStoriesIds,
StoriesDownloadStatus? downloadStatus,
@ -133,10 +127,10 @@ class StoriesState extends Equatable {
StoriesState copyWithStatusUpdated({
required StoryType type,
required StoriesStatus to,
required Status to,
}) {
final Map<StoryType, StoriesStatus> newMap =
Map<StoryType, StoriesStatus>.from(statusByType);
final Map<StoryType, Status> newMap =
Map<StoryType, Status>.from(statusByType);
newMap[type] = to;
return copyWith(
statusByType: newMap,
@ -162,9 +156,9 @@ class StoriesState extends Equatable {
final Map<StoryType, List<int>> newStoryIdsMap =
Map<StoryType, List<int>>.from(storyIdsByType);
newStoryIdsMap[type] = <int>[];
final Map<StoryType, StoriesStatus> newStatusMap =
Map<StoryType, StoriesStatus>.from(statusByType);
newStatusMap[type] = StoriesStatus.loading;
final Map<StoryType, Status> newStatusMap =
Map<StoryType, Status>.from(statusByType);
newStatusMap[type] = Status.inProgress;
final Map<StoryType, int> newCurrentPageMap =
Map<StoryType, int>.from(currentPageByType);
newCurrentPageMap[type] = 0;

View File

@ -39,8 +39,9 @@ abstract class Constants {
static const String featureOpenStoryInWebView = 'open_story_in_web_view';
static const String featureLogIn = 'log_in';
static const String featurePinToTop = 'pin_to_top';
static const String featureJumpUpButton = 'jump_up_button';
static const String featureJumpDownButton = 'jump_down_button';
static const String featureJumpUpButton = 'jump_up_button_with_long_press';
static const String featureJumpDownButton =
'jump_down_button_with_long_press';
static final String happyFace = <String>[
'(๑•̀ㅂ•́)و✧',

View File

@ -2,7 +2,7 @@ import 'package:logger/logger.dart';
class CustomLogFilter extends LogFilter {
@override
Level? get level => Level.verbose;
Level? get level => Level.trace;
/// The minimal level allowed in production.
static const Level _minimalLevel = Level.info;

View File

@ -20,7 +20,7 @@ class CustomFileOutput extends LogOutput {
IOSink? _sink;
@override
void init() {
Future<void> init() async {
_sink = file.openWrite(
mode: overrideExisting ? FileMode.writeOnly : FileMode.writeOnlyAppend,
encoding: encoding,

View File

@ -106,7 +106,7 @@ class CommentsCubit extends Cubit<CommentsState> {
emit(
state.copyWith(
status: CommentsStatus.loading,
status: CommentsStatus.inProgress,
comments: <Comment>[],
currentPage: 0,
),
@ -150,7 +150,7 @@ class CommentsCubit extends Cubit<CommentsState> {
Future<void> refresh() async {
emit(
state.copyWith(
status: CommentsStatus.loading,
status: CommentsStatus.inProgress,
),
);
@ -224,7 +224,7 @@ class CommentsCubit extends Cubit<CommentsState> {
void Function(Comment)? onCommentFetched,
VoidCallback? onDone,
}) {
if (comment == null && state.status == CommentsStatus.loading) return;
if (comment == null && state.status == CommentsStatus.inProgress) return;
switch (state.fetchMode) {
case FetchMode.lazy:
@ -269,7 +269,7 @@ class CommentsCubit extends Cubit<CommentsState> {
_streamSubscriptions[comment.id] = streamSubscription;
case FetchMode.eager:
if (_streamSubscription != null) {
emit(state.copyWith(status: CommentsStatus.loading));
emit(state.copyWith(status: CommentsStatus.inProgress));
_streamSubscription
?..resume()
..onData(onCommentFetched);
@ -279,7 +279,7 @@ class CommentsCubit extends Cubit<CommentsState> {
Future<void> loadParentThread() async {
HapticFeedbackUtil.light();
emit(state.copyWith(fetchParentStatus: CommentsStatus.loading));
emit(state.copyWith(fetchParentStatus: CommentsStatus.inProgress));
final Item? parent =
await _storiesRepository.fetchItem(id: state.item.parent);
@ -301,7 +301,7 @@ class CommentsCubit extends Cubit<CommentsState> {
Future<void> loadRootThread() async {
HapticFeedbackUtil.light();
emit(state.copyWith(fetchRootStatus: CommentsStatus.loading));
emit(state.copyWith(fetchRootStatus: CommentsStatus.inProgress));
final Story? parent = await _storiesRepository
.fetchParentStory(id: state.item.id)
.then(_toBuildableStory);
@ -349,8 +349,8 @@ class CommentsCubit extends Cubit<CommentsState> {
init(useCommentCache: true);
}
/// Jump to next root level comment.
void jump(
/// Scroll to next root level comment.
void scrollToNextRoot(
ItemScrollController itemScrollController,
ItemPositionsListener itemPositionsListener,
) {
@ -387,8 +387,8 @@ class CommentsCubit extends Cubit<CommentsState> {
}
}
/// Jump to previous root level comment.
void jumpUp(
/// Scroll to previous root level comment.
void scrollToPreviousRoot(
ItemScrollController itemScrollController,
ItemPositionsListener itemPositionsListener,
) {

View File

@ -1,11 +1,11 @@
part of 'comments_cubit.dart';
enum CommentsStatus {
init,
loading,
idle,
inProgress,
loaded,
allLoaded,
failure,
error,
}
class CommentsState extends Equatable {
@ -28,9 +28,9 @@ class CommentsState extends Equatable {
required this.fetchMode,
required this.order,
}) : comments = <Comment>[],
status = CommentsStatus.init,
fetchParentStatus = CommentsStatus.init,
fetchRootStatus = CommentsStatus.init,
status = CommentsStatus.idle,
fetchParentStatus = CommentsStatus.idle,
fetchRootStatus = CommentsStatus.idle,
onlyShowTargetComment = false,
currentPage = 0;

View File

@ -51,7 +51,7 @@ class FavCubit extends Cubit<FavState> {
.onDone(() {
emit(
state.copyWith(
status: FavStatus.loaded,
status: Status.success,
),
);
});
@ -107,7 +107,7 @@ class FavCubit extends Cubit<FavState> {
}
void loadMore() {
emit(state.copyWith(status: FavStatus.loading));
emit(state.copyWith(status: Status.inProgress));
final int currentPage = state.currentPage;
final int len = state.favIds.length;
emit(state.copyWith(currentPage: currentPage + 1));
@ -128,10 +128,10 @@ class FavCubit extends Cubit<FavState> {
)
.listen(_onItemLoaded)
.onDone(() {
emit(state.copyWith(status: FavStatus.loaded));
emit(state.copyWith(status: Status.success));
});
} else {
emit(state.copyWith(status: FavStatus.loaded));
emit(state.copyWith(status: Status.success));
}
}
@ -140,7 +140,7 @@ class FavCubit extends Cubit<FavState> {
emit(
state.copyWith(
status: FavStatus.loading,
status: Status.inProgress,
currentPage: 0,
favItems: <Item>[],
favIds: <int>[],
@ -155,7 +155,7 @@ class FavCubit extends Cubit<FavState> {
)
.listen(_onItemLoaded)
.onDone(() {
emit(state.copyWith(status: FavStatus.loaded));
emit(state.copyWith(status: Status.success));
});
});
}

View File

@ -1,12 +1,5 @@
part of 'fav_cubit.dart';
enum FavStatus {
init,
loading,
loaded,
failure,
}
class FavState extends Equatable {
const FavState({
required this.favIds,
@ -18,18 +11,18 @@ class FavState extends Equatable {
FavState.init()
: favIds = <int>[],
favItems = <Item>[],
status = FavStatus.init,
status = Status.idle,
currentPage = 0;
final List<int> favIds;
final List<Item> favItems;
final FavStatus status;
final Status status;
final int currentPage;
FavState copyWith({
List<int>? favIds,
List<Item>? favItems,
FavStatus? status,
Status? status,
int? currentPage,
}) {
return FavState(

View File

@ -54,7 +54,7 @@ class HistoryCubit extends Cubit<HistoryState> {
}
void loadMore() {
emit(state.copyWith(status: HistoryStatus.loading));
emit(state.copyWith(status: Status.inProgress));
final int currentPage = state.currentPage;
final int len = state.submittedIds.length;
emit(state.copyWith(currentPage: currentPage + 1));
@ -75,10 +75,10 @@ class HistoryCubit extends Cubit<HistoryState> {
)
.listen(_onItemLoaded)
.onDone(() {
emit(state.copyWith(status: HistoryStatus.loaded));
emit(state.copyWith(status: Status.success));
});
} else {
emit(state.copyWith(status: HistoryStatus.loaded));
emit(state.copyWith(status: Status.success));
}
}
@ -86,7 +86,7 @@ class HistoryCubit extends Cubit<HistoryState> {
final String username = _authBloc.state.username;
emit(
state.copyWith(
status: HistoryStatus.loading,
status: Status.inProgress,
currentPage: 0,
submittedIds: <int>[],
submittedItems: <Item>[],
@ -107,7 +107,7 @@ class HistoryCubit extends Cubit<HistoryState> {
)
.listen(_onItemLoaded)
.onDone(() {
emit(state.copyWith(status: HistoryStatus.loaded));
emit(state.copyWith(status: Status.success));
});
}
});

View File

@ -1,12 +1,5 @@
part of 'history_cubit.dart';
enum HistoryStatus {
init,
loading,
loaded,
failure,
}
class HistoryState extends Equatable {
const HistoryState({
required this.submittedIds,
@ -18,18 +11,18 @@ class HistoryState extends Equatable {
HistoryState.init()
: submittedIds = <int>[],
submittedItems = <Item>[],
status = HistoryStatus.init,
status = Status.idle,
currentPage = 0;
final List<int> submittedIds;
final List<Item> submittedItems;
final HistoryStatus status;
final Status status;
final int currentPage;
HistoryState copyWith({
List<int>? submittedIds,
List<Item>? submittedItems,
HistoryStatus? status,
Status? status,
int? currentPage,
}) {
return HistoryState(

View File

@ -100,7 +100,7 @@ class NotificationCubit extends Cubit<NotificationState> {
void markAsRead(int id) {
Future.doWhile(() {
if (state.status != NotificationStatus.loading) {
if (state.status != Status.inProgress) {
if (state.unreadCommentsIds.contains(id)) {
final List<int> updatedUnreadIds = <int>[...state.unreadCommentsIds]
..remove(id);
@ -116,7 +116,7 @@ class NotificationCubit extends Cubit<NotificationState> {
void markAllAsRead() {
Future.doWhile(() {
if (state.status != NotificationStatus.loading) {
if (state.status != Status.inProgress) {
emit(state.copyWith(unreadCommentsIds: <int>[]));
_preferenceRepository.updateUnreadCommentsIds(<int>[]);
return false;
@ -131,7 +131,7 @@ class NotificationCubit extends Cubit<NotificationState> {
_preferenceCubit.state.notificationEnabled) {
emit(
state.copyWith(
status: NotificationStatus.loading,
status: Status.inProgress,
),
);
@ -141,14 +141,14 @@ class NotificationCubit extends Cubit<NotificationState> {
} else {
emit(
state.copyWith(
status: NotificationStatus.loaded,
status: Status.success,
),
);
}
}
Future<void> loadMore() async {
emit(state.copyWith(status: NotificationStatus.loading));
emit(state.copyWith(status: Status.inProgress));
final int currentPage = state.currentPage + 1;
final int lower = currentPage * _pageSize + state.offset;
@ -169,7 +169,7 @@ class NotificationCubit extends Cubit<NotificationState> {
emit(
state.copyWith(
status: NotificationStatus.loaded,
status: Status.success,
currentPage: currentPage,
),
);
@ -237,7 +237,7 @@ class NotificationCubit extends Cubit<NotificationState> {
}
}).whenComplete(
() => emit(
state.copyWith(status: NotificationStatus.loaded),
state.copyWith(status: Status.success),
),
);
}

View File

@ -1,12 +1,5 @@
part of 'notification_cubit.dart';
enum NotificationStatus {
initial,
loading,
loaded,
failure,
}
class NotificationState extends Equatable {
const NotificationState({
required this.comments,
@ -23,14 +16,14 @@ class NotificationState extends Equatable {
allCommentsIds = <int>[],
currentPage = 0,
offset = 0,
status = NotificationStatus.initial;
status = Status.idle;
final List<Comment> comments;
final List<int> allCommentsIds;
final List<int> unreadCommentsIds;
final int currentPage;
final int offset;
final NotificationStatus status;
final Status status;
NotificationState copyWith({
List<Comment>? comments,
@ -38,7 +31,7 @@ class NotificationState extends Equatable {
List<int>? unreadCommentsIds,
int? currentPage,
int? offset,
NotificationStatus? status,
Status? status,
}) {
return NotificationState(
comments: comments ?? this.comments,

View File

@ -27,7 +27,7 @@ class PinCubit extends Cubit<PinState> {
emit(state.copyWith(pinnedStoriesIds: ids));
_storiesRepository.fetchStoriesStream(ids: ids).listen(_onStoryFetched);
}).whenComplete(() => emit(state.copyWith(status: Status.loaded)));
}).whenComplete(() => emit(state.copyWith(status: Status.success)));
}
void pinStory(Story story) {
@ -53,7 +53,7 @@ class PinCubit extends Cubit<PinState> {
}
void refresh() {
if (state.status == Status.loading) return;
if (state.status.isLoading) return;
init();
}

View File

@ -27,7 +27,7 @@ class PollCubit extends Cubit<PollState> {
emit(PollState.init());
}
emit(state.copyWith(status: PollStatus.loading));
emit(state.copyWith(status: Status.inProgress));
List<int> pollOptionsIds = _story.parts;
@ -42,7 +42,7 @@ class PollCubit extends Cubit<PollState> {
// If pollOptionsIds is still empty, exit loading state.
if (pollOptionsIds.isEmpty) {
emit(state.copyWith(status: PollStatus.loaded));
emit(state.copyWith(status: Status.success));
return;
}
@ -72,7 +72,7 @@ class PollCubit extends Cubit<PollState> {
);
}
emit(state.copyWith(status: PollStatus.loaded));
emit(state.copyWith(status: Status.success));
}
}

View File

@ -1,12 +1,5 @@
part of 'poll_cubit.dart';
enum PollStatus {
initial,
loading,
loaded,
failure,
}
class PollState extends Equatable {
const PollState({
required this.totalVotes,
@ -19,18 +12,18 @@ class PollState extends Equatable {
: totalVotes = 0,
selections = <int>{},
pollOptions = <PollOption>[],
status = PollStatus.initial;
status = Status.idle;
final int totalVotes;
final Set<int> selections;
final List<PollOption> pollOptions;
final PollStatus status;
final Status status;
PollState copyWith({
int? totalVotes,
Set<int>? selections,
List<PollOption>? pollOptions,
PollStatus? status,
Status? status,
}) {
return PollState(
totalVotes: totalVotes ?? this.totalVotes,

View File

@ -1,6 +1,7 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:hacki/config/locator.dart';
import 'package:hacki/models/models.dart';
import 'package:hacki/repositories/repositories.dart';
part 'post_state.dart';
@ -14,31 +15,31 @@ class PostCubit extends Cubit<PostState> {
final PostRepository _postRepository;
Future<void> post({required String text, required int to}) async {
emit(state.copyWith(status: PostStatus.loading));
emit(state.copyWith(status: Status.inProgress));
final bool successful = await _postRepository.comment(
parentId: to,
text: text,
);
if (successful) {
emit(state.copyWith(status: PostStatus.successful));
emit(state.copyWith(status: Status.success));
} else {
emit(state.copyWith(status: PostStatus.failure));
emit(state.copyWith(status: Status.failure));
}
}
Future<void> edit({required String text, required int id}) async {
emit(state.copyWith(status: PostStatus.loading));
emit(state.copyWith(status: Status.inProgress));
final bool successful = await _postRepository.edit(id: id, text: text);
if (successful) {
emit(state.copyWith(status: PostStatus.successful));
emit(state.copyWith(status: Status.success));
} else {
emit(state.copyWith(status: PostStatus.failure));
emit(state.copyWith(status: Status.failure));
}
}
void reset() {
emit(state.copyWith(status: PostStatus.init));
emit(state.copyWith(status: Status.idle));
}
}

View File

@ -1,20 +1,13 @@
part of 'post_cubit.dart';
enum PostStatus {
init,
loading,
successful,
failure,
}
class PostState extends Equatable {
const PostState({required this.status});
const PostState.init() : status = PostStatus.init;
const PostState.init() : status = Status.idle;
final PostStatus status;
final Status status;
PostState copyWith({PostStatus? status}) {
PostState copyWith({Status? status}) {
return PostState(
status: status ?? this.status,
);

View File

@ -1,7 +1,7 @@
part of 'search_cubit.dart';
enum SearchStatus {
initial,
idle,
loading,
loadingMore,
loaded,
@ -15,7 +15,7 @@ class SearchState extends Equatable {
});
SearchState.init()
: status = SearchStatus.initial,
: status = SearchStatus.idle,
results = <Item>[],
params = SearchParams.init();

View File

@ -1,6 +1,7 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:hacki/config/locator.dart';
import 'package:hacki/models/models.dart';
import 'package:hacki/repositories/post_repository.dart';
part 'submit_state.dart';
@ -25,7 +26,7 @@ class SubmitCubit extends Cubit<SubmitState> {
}
void onSubmitTapped() {
emit(state.copyWith(status: SubmitStatus.submitting));
emit(state.copyWith(status: Status.inProgress));
if (state.title?.isNotEmpty ?? false) {
_postRepository
@ -35,9 +36,9 @@ class SubmitCubit extends Cubit<SubmitState> {
text: state.text,
)
.then((bool successful) {
emit(state.copyWith(status: SubmitStatus.submitted));
emit(state.copyWith(status: Status.success));
}).onError((Object? error, StackTrace stackTrace) {
emit(state.copyWith(status: SubmitStatus.failure));
emit(state.copyWith(status: Status.failure));
});
}
}

View File

@ -1,12 +1,5 @@
part of 'submit_cubit.dart';
enum SubmitStatus {
initial,
submitting,
submitted,
failure,
}
class SubmitState extends Equatable {
const SubmitState({
required this.title,
@ -19,18 +12,18 @@ class SubmitState extends Equatable {
: title = null,
url = null,
text = null,
status = SubmitStatus.initial;
status = Status.idle;
final String? title;
final String? url;
final String? text;
final SubmitStatus status;
final Status status;
SubmitState copyWith({
String? title,
String? url,
String? text,
SubmitStatus? status,
Status? status,
}) {
return SubmitState(
title: title ?? this.title,

View File

@ -15,16 +15,16 @@ class UserCubit extends Cubit<UserState> {
final StoriesRepository _storiesRepository;
void init({required String userId}) {
emit(state.copyWith(status: UserStatus.loading));
emit(state.copyWith(status: Status.inProgress));
_storiesRepository.fetchUser(id: userId).then((User? user) {
emit(
state.copyWith(
user: user ?? User.emptyWithId(userId),
status: UserStatus.loaded,
status: Status.success,
),
);
}).onError((_, __) {
emit(state.copyWith(status: UserStatus.failure));
emit(state.copyWith(status: Status.failure));
return;
});
}

View File

@ -1,12 +1,5 @@
part of 'user_cubit.dart';
enum UserStatus {
initial,
loading,
loaded,
failure,
}
class UserState extends Equatable {
const UserState({
required this.user,
@ -15,14 +8,14 @@ class UserState extends Equatable {
const UserState.init()
: user = const User.empty(),
status = UserStatus.initial;
status = Status.idle;
final User user;
final UserStatus status;
final Status status;
UserState copyWith({
User? user,
UserStatus? status,
Status? status,
}) {
return UserState(
user: user ?? this.user,

View File

@ -6,7 +6,7 @@ enum Vote {
}
enum VoteStatus {
initial,
idle,
canceled,
submitted,
failureBeHumble,
@ -24,7 +24,7 @@ class VoteState extends Equatable {
const VoteState.init({required this.item})
: vote = null,
status = VoteStatus.initial;
status = VoteStatus.idle;
/// Null means user has not voted,
/// True means user voted up,

View File

@ -14,6 +14,10 @@ extension ObjectExtension on Object {
String identifier = '',
StackTrace? stackTrace,
}) {
locator.get<Logger>().e(identifier, this, stackTrace ?? StackTrace.current);
locator.get<Logger>().e(
identifier,
error: this,
stackTrace: stackTrace ?? StackTrace.current,
);
}
}

View File

@ -58,8 +58,8 @@ Future<void> main({bool testing = false}) async {
FlutterError.onError = (FlutterErrorDetails details) {
locator.get<Logger>().e(
details.summary,
details.exceptionAsString(),
details.stack,
error: details.exceptionAsString(),
stackTrace: details.stack,
);
};
@ -241,7 +241,7 @@ class HackiApp extends StatelessWidget {
create: (BuildContext context) => TabCubit(
preferenceCubit: context.read<PreferenceCubit>(),
)..init(),
)
),
],
child: AdaptiveTheme(
light: ThemeData(
@ -289,7 +289,9 @@ class HackiApp extends StatelessWidget {
child: MaterialApp(
title: 'Hacki',
debugShowCheckedModeBanner: false,
theme: useTrueDark ? trueDarkTheme : theme,
theme: (useTrueDark ? trueDarkTheme : theme).copyWith(
useMaterial3: false,
),
navigatorKey: navigatorKey,
navigatorObservers: <NavigatorObserver>[
locator.get<RouteObserver<ModalRoute<dynamic>>>(),

View File

@ -1,6 +1,14 @@
enum Status {
idle,
loading,
loaded,
error,
inProgress,
success,
failure,
}
extension StatusExtension on Status {
bool get isLoading => this == Status.inProgress;
bool get isSuccessful => this == Status.success;
bool get hasError => this == Status.failure;
}

View File

@ -64,7 +64,7 @@ class PostableRepository {
validateStatus: validateStatus,
),
);
} on DioError catch (e) {
} on DioException catch (e) {
throw ServiceException(e.message);
}
}

View File

@ -213,7 +213,7 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
listeners: <BlocListener<dynamic, dynamic>>[
BlocListener<PostCubit, PostState>(
listener: (BuildContext context, PostState postState) {
if (postState.status == PostStatus.successful) {
if (postState.status == Status.success) {
Navigator.popUntil(
context,
(Route<dynamic> route) =>
@ -228,7 +228,7 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
showSnackBar(content: msg);
context.read<EditCubit>().onReplySubmittedSuccessfully();
context.read<PostCubit>().reset();
} else if (postState.status == PostStatus.failure) {
} else if (postState.status == Status.failure) {
Navigator.popUntil(
context,
(Route<dynamic> route) =>
@ -355,6 +355,7 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
enableDrag: false,
builder: (BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
@ -370,7 +371,7 @@ class _ItemScreenState extends State<ItemScreen> with RouteAware {
),
SizedBox(
height: MediaQuery.of(context).viewInsets.bottom,
)
),
],
);
},

View File

@ -32,30 +32,32 @@ class CustomFloatingActionButton extends StatelessWidget {
Icons.keyboard_arrow_up,
color: Palette.white,
),
title: const Text('Jump to previous root level comment.'),
title: const Text('Shortcut'),
description: const Text(
'''Tapping on this button will take you to the previous off-screen root level comment.''',
'''Tapping on this button will take you to the previous off-screen root level comment.\n\nLong press on it to jump to the very beginning of this thread.''',
),
child: FloatingActionButton.small(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
child: InkWell(
onLongPress: () => itemScrollController.scrollTo(
index: 0,
duration: Durations.ms400,
),
child: FloatingActionButton.small(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
/// Randomly generated string as heroTag to prevent
/// default [FloatingActionButton] animation.
heroTag: UniqueKey().hashCode,
onPressed: () {
if (state.status == CommentsStatus.loading) return;
HapticFeedbackUtil.selection();
context.read<CommentsCubit>().jumpUp(
itemScrollController,
itemPositionsListener,
);
},
child: Icon(
Icons.keyboard_arrow_up,
color: state.status == CommentsStatus.loading
? Palette.grey
: Theme.of(context).colorScheme.primary,
/// Randomly generated string as heroTag to prevent
/// default [FloatingActionButton] animation.
heroTag: UniqueKey().hashCode,
onPressed: () {
HapticFeedbackUtil.selection();
context.read<CommentsCubit>().scrollToPreviousRoot(
itemScrollController,
itemPositionsListener,
);
},
child: Icon(
Icons.keyboard_arrow_up,
color: Theme.of(context).colorScheme.primary,
),
),
),
),
@ -65,29 +67,31 @@ class CustomFloatingActionButton extends StatelessWidget {
Icons.keyboard_arrow_down,
color: Palette.white,
),
title: const Text('Jump to next root level comment.'),
title: const Text('Shortcut'),
description: const Text(
'''Tapping on this button will take you to the next off-screen root level comment.''',
'''Tapping on this button will take you to the next off-screen root level comment.\n\nLong press on it to jump to the end of this thread.''',
),
child: FloatingActionButton.small(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
child: InkWell(
onLongPress: () => itemScrollController.scrollTo(
index: state.comments.length,
duration: Durations.ms400,
),
child: FloatingActionButton.small(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
/// Same as above.
heroTag: UniqueKey().hashCode,
onPressed: () {
if (state.status == CommentsStatus.loading) return;
HapticFeedbackUtil.selection();
context.read<CommentsCubit>().jump(
itemScrollController,
itemPositionsListener,
);
},
child: Icon(
Icons.keyboard_arrow_down,
color: state.status == CommentsStatus.loading
? Palette.grey
: Theme.of(context).colorScheme.primary,
/// Same as above.
heroTag: UniqueKey().hashCode,
onPressed: () {
HapticFeedbackUtil.selection();
context.read<CommentsCubit>().scrollToNextRoot(
itemScrollController,
itemPositionsListener,
);
},
child: Icon(
Icons.keyboard_arrow_down,
color: Theme.of(context).colorScheme.primary,
),
),
),
),

View File

@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hacki/blocs/blocs.dart';
import 'package:hacki/config/constants.dart';
import 'package:hacki/extensions/extensions.dart';
import 'package:hacki/models/status.dart';
import 'package:hacki/screens/widgets/widgets.dart';
import 'package:hacki/styles/styles.dart';
import 'package:hacki/utils/utils.dart';
@ -32,7 +33,7 @@ class _LoginDialogState extends State<LoginDialog> {
builder: (BuildContext context, AuthState state) {
return SimpleDialog(
children: <Widget>[
if (state.status == AuthStatus.loading)
if (state.status.isLoading)
const SizedBox(
height: Dimens.pt36,
width: Dimens.pt36,
@ -82,7 +83,7 @@ class _LoginDialogState extends State<LoginDialog> {
const SizedBox(
height: Dimens.pt16,
),
if (state.status == AuthStatus.failure)
if (state.status == Status.failure)
Padding(
padding: const EdgeInsets.only(
left: Dimens.pt18,
@ -141,7 +142,7 @@ class _LoginDialogState extends State<LoginDialog> {
),
],
),
)
),
],
),
Padding(

View File

@ -159,7 +159,7 @@ class MainView extends StatelessWidget {
prev.status != current.status,
builder: (BuildContext context, CommentsState state) {
return AnimatedOpacity(
opacity: state.status == CommentsStatus.loading
opacity: state.status == CommentsStatus.inProgress
? NumSwitch.on
: NumSwitch.off,
duration: const Duration(
@ -417,27 +417,28 @@ class _ParentItemSection extends StatelessWidget {
width: _viewParentButtonWidth,
child: TextButton(
onPressed: context.read<CommentsCubit>().loadParentThread,
child: state.fetchParentStatus == CommentsStatus.loading
? const SizedBox(
height: Dimens.pt12,
width: Dimens.pt12,
child: CustomCircularProgressIndicator(
strokeWidth: Dimens.pt2,
),
)
: const Text(
'View parent',
style: TextStyle(
fontSize: TextDimens.pt13,
),
),
child:
state.fetchParentStatus == CommentsStatus.inProgress
? const SizedBox(
height: Dimens.pt12,
width: Dimens.pt12,
child: CustomCircularProgressIndicator(
strokeWidth: Dimens.pt2,
),
)
: const Text(
'View parent',
style: TextStyle(
fontSize: TextDimens.pt13,
),
),
),
),
SizedBox(
width: _viewRootButtonWidth,
child: TextButton(
onPressed: context.read<CommentsCubit>().loadRootThread,
child: state.fetchRootStatus == CommentsStatus.loading
child: state.fetchRootStatus == CommentsStatus.inProgress
? const SizedBox(
height: Dimens.pt12,
width: Dimens.pt12,

View File

@ -81,7 +81,7 @@ class MorePopupMenu extends StatelessWidget {
child: BlocBuilder<UserCubit, UserState>(
builder: (BuildContext context, UserState state) {
return Semantics(
excludeSemantics: state.status == UserStatus.loading,
excludeSemantics: state.status == Status.inProgress,
child: ListTile(
leading: const Icon(
Icons.account_circle,
@ -285,7 +285,7 @@ class MorePopupMenu extends StatelessWidget {
child: SearchScreen(
fromUserDialog: true,
),
)
),
],
),
),

View File

@ -25,7 +25,7 @@ class _PollViewState extends State<PollView> {
const SizedBox(
height: Dimens.pt24,
),
if (state.status == PollStatus.loading) ...<Widget>[
if (state.status == Status.inProgress) ...<Widget>[
const LinearProgressIndicator(),
const SizedBox(
height: Dimens.pt24,
@ -134,7 +134,7 @@ class _PollViewState extends State<PollView> {
),
],
),
)
),
],
),
);

View File

@ -5,7 +5,7 @@ import 'package:flutter_feather_icons/flutter_feather_icons.dart';
import 'package:hacki/config/constants.dart';
import 'package:hacki/cubits/cubits.dart';
import 'package:hacki/extensions/extensions.dart';
import 'package:hacki/models/item/item.dart';
import 'package:hacki/models/models.dart';
import 'package:hacki/screens/screens.dart';
import 'package:hacki/screens/widgets/widgets.dart';
import 'package:hacki/styles/styles.dart';
@ -35,11 +35,12 @@ class _ReplyBoxState extends State<ReplyBox> {
bool expanded = false;
double? expandedHeight;
static const double _collapsedHeight = 100;
static const double collapsedHeight = 140;
@override
Widget build(BuildContext context) {
expandedHeight ??= MediaQuery.of(context).size.height;
expandedHeight ??= MediaQuery.of(context).size.height -
MediaQuery.of(context).viewInsets.bottom;
return BlocBuilder<EditCubit, EditState>(
buildWhen: (EditState previous, EditState current) =>
previous.showReplyBox != current.showReplyBox ||
@ -49,7 +50,7 @@ class _ReplyBoxState extends State<ReplyBox> {
return BlocBuilder<PostCubit, PostState>(
builder: (BuildContext context, PostState postState) {
final Item? replyingTo = editState.replyingTo;
final bool isLoading = postState.status == PostStatus.loading;
final bool isLoading = postState.status.isLoading;
return Padding(
padding: EdgeInsets.only(
@ -60,7 +61,7 @@ class _ReplyBoxState extends State<ReplyBox> {
: Dimens.zero,
),
child: AnimatedContainer(
height: expanded ? expandedHeight : _collapsedHeight,
height: expanded ? expandedHeight : collapsedHeight,
duration: Durations.ms200,
decoration: BoxDecoration(
boxShadow: <BoxShadow>[
@ -73,6 +74,7 @@ class _ReplyBoxState extends State<ReplyBox> {
),
child: Material(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (context.read<SplitViewCubit>().state.enabled)
const Divider(
@ -213,13 +215,21 @@ class _ReplyBoxState extends State<ReplyBox> {
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: Dimens.pt16,
padding: EdgeInsets.only(
left: Dimens.pt16,
right: Dimens.pt16,
bottom: expanded
// This padding here prevents keyboard
// overlapping with TextField.
? MediaQuery.of(context).viewInsets.bottom +
Dimens.pt16
: Dimens.zero,
),
child: TextField(
autofocus: true,
controller: widget.textEditingController,
maxLines: 100,
autofocus: true,
expands: true,
maxLines: null,
decoration: const InputDecoration(
alignLabelWithHint: true,
contentPadding: EdgeInsets.zero,

View File

@ -53,7 +53,7 @@ class _ProfileScreenState extends State<ProfileScreen>
previous.status != current.status,
listener:
(BuildContext context, NotificationState notificationState) {
if (notificationState.status == NotificationStatus.loaded) {
if (notificationState.status == Status.success) {
refreshControllerNotification
..refreshCompleted()
..loadComplete();
@ -71,7 +71,7 @@ class _ProfileScreenState extends State<ProfileScreen>
BuildContext context,
HistoryState historyState,
) {
if (historyState.status == HistoryStatus.loaded) {
if (historyState.status == Status.success) {
refreshControllerHistory
..refreshCompleted()
..loadComplete();
@ -83,7 +83,7 @@ class _ProfileScreenState extends State<ProfileScreen>
) {
if ((!authState.isLoggedIn ||
historyState.submittedItems.isEmpty) &&
historyState.status != HistoryStatus.loading) {
historyState.status != Status.inProgress) {
return const CenteredMessageView(
content: 'Your past comments and stories will '
'show up here.',
@ -126,7 +126,7 @@ class _ProfileScreenState extends State<ProfileScreen>
visible: pageType == PageType.fav,
child: BlocConsumer<FavCubit, FavState>(
listener: (BuildContext context, FavState favState) {
if (favState.status == FavStatus.loaded) {
if (favState.status == Status.success) {
refreshControllerFav
..refreshCompleted()
..loadComplete();
@ -134,7 +134,7 @@ class _ProfileScreenState extends State<ProfileScreen>
},
builder: (BuildContext context, FavState favState) {
if (favState.favItems.isEmpty &&
favState.status != FavStatus.loading) {
favState.status != Status.inProgress) {
return const CenteredMessageView(
content: 'Your favorite stories will show up here.'
'\nThey will be synced to your Hacker '
@ -363,7 +363,7 @@ class _ProfileScreenState extends State<ProfileScreen>
? <Comment>[comment]
: <Comment>[
...children,
comment.copyWith(level: children.length)
comment.copyWith(level: children.length),
],
onlyShowTargetComment: true,
),

View File

@ -361,7 +361,7 @@ class _SettingsState extends State<Settings> {
),
Spacer(),
],
)
),
],
),
);

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hacki/cubits/cubits.dart';
import 'package:hacki/extensions/extensions.dart';
import 'package:hacki/models/models.dart';
import 'package:hacki/styles/styles.dart';
import 'package:hacki/utils/utils.dart';
@ -43,13 +44,13 @@ class _SubmitScreenState extends State<SubmitScreen> {
listenWhen: (SubmitState previous, SubmitState current) =>
previous.status != current.status,
listener: (BuildContext context, SubmitState state) {
if (state.status == SubmitStatus.submitted) {
if (state.status == Status.success) {
Navigator.pop(context);
HapticFeedbackUtil.light();
showSnackBar(
content: 'Post submitted successfully.',
);
} else if (state.status == SubmitStatus.failure) {
} else if (state.status == Status.failure) {
showErrorSnackBar();
}
},
@ -95,7 +96,7 @@ class _SubmitScreenState extends State<SubmitScreen> {
'Submit',
),
actions: <Widget>[
if (state.status == SubmitStatus.submitting)
if (state.status == Status.inProgress)
const Padding(
padding: EdgeInsets.symmetric(
vertical: Dimens.pt18,

View File

@ -49,7 +49,7 @@ class _StoriesListViewState extends State<StoriesListView> {
previous.statusByType[storyType] !=
current.statusByType[storyType],
listener: (BuildContext context, StoriesState state) {
if (state.statusByType[storyType] == StoriesStatus.loaded) {
if (state.statusByType[storyType] == Status.success) {
refreshController
..refreshCompleted(resetFooterState: true)
..loadComplete();

View File

@ -229,7 +229,7 @@ class _LinkPreviewPlaceholder extends StatelessWidget {
],
),
),
)
),
],
),
),

View File

@ -273,7 +273,7 @@ class WebAnalyzer {
info.title,
info.description,
info.icon,
info.image
info.image,
];
} else if (info is WebVideoInfo) {
return <dynamic>['1', info.image];

View File

@ -26,8 +26,8 @@ abstract class HtmlUtil {
.querySelectorAll("input[type='hidden']");
return <String, String>{
if (hiddenInputs != null)
for (dom.Element hiddenInput in hiddenInputs)
hiddenInput.attributes['name']!: hiddenInput.attributes['value']!
for (final dom.Element hiddenInput in hiddenInputs)
hiddenInput.attributes['name']!: hiddenInput.attributes['value']!,
};
}

View File

@ -5,34 +5,34 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "405666cd3cf0ee0a48d21ec67e65406aad2c726d9fa58840d3375e7bdcd32a07"
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
url: "https://pub.dev"
source: hosted
version: "60.0.0"
version: "61.0.0"
adaptive_theme:
dependency: "direct main"
description:
name: adaptive_theme
sha256: "3568bb526d4823c7bb35f9ce3604af15e04cc0e9cc4f257da3604fe6b48d74ae"
sha256: "2d9bfee4240cdfad1b169cb43ac38fb49487e7fe1cc845e2973d4cef1780c0f6"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
version: "3.3.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1952250bd005bacb895a01bf1b4dc00e3ba1c526cf47dca54dfe24979c65f5b3"
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
url: "https://pub.dev"
source: hosted
version: "5.12.0"
version: "5.13.0"
args:
dependency: transitive
description:
name: args
sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
async:
dependency: transitive
description:
@ -53,18 +53,18 @@ packages:
dependency: "direct main"
description:
name: bloc
sha256: "658a5ae59edcf1e58aac98b000a71c762ad8f46f1394c34a52050cafb3e11a80"
sha256: "3820f15f502372d979121de1f6b97bfcf1630ebff8fe1d52fb2b0bfa49be5b49"
url: "https://pub.dev"
source: hosted
version: "8.1.1"
version: "8.1.2"
bloc_test:
dependency: "direct dev"
description:
name: bloc_test
sha256: ffbb60c17ee3d8e3784cb78071088e353199057233665541e8ac6cd438dca8ad
sha256: af0de1a1e16a7536e95dcd7491e0a6d6078e11d2d691988e862280b74f5c7968
url: "https://pub.dev"
source: hosted
version: "9.1.1"
version: "9.1.4"
boolean_selector:
dependency: transitive
description:
@ -125,18 +125,18 @@ packages:
dependency: "direct main"
description:
name: collection
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
url: "https://pub.dev"
source: hosted
version: "1.17.1"
version: "1.17.2"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "45262924896ff72a8cd92b722bb7e3d5020f9e0724531a3e10e22ddae2005991"
sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "4.0.2"
connectivity_plus_platform_interface:
dependency: transitive
description:
@ -181,10 +181,10 @@ packages:
dependency: transitive
description:
name: csslib
sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "0.17.2"
version: "1.0.0"
dbus:
dependency: transitive
description:
@ -197,10 +197,10 @@ packages:
dependency: "direct main"
description:
name: device_info_plus
sha256: "9b1a0c32b2a503f8fe9f8764fac7b5fcd4f6bd35d8f49de5350bccf9e2a33b8a"
sha256: "86add5ef97215562d2e090535b0a16f197902b10c369c558a100e74ea06e8659"
url: "https://pub.dev"
source: hosted
version: "9.0.0"
version: "9.0.3"
device_info_plus_platform_interface:
dependency: transitive
description:
@ -221,10 +221,10 @@ packages:
dependency: "direct main"
description:
name: dio
sha256: "347d56c26d63519552ef9a569f2a593dda99a81fdbdff13c584b7197cfe05059"
sha256: ce75a1b40947fea0a0e16ce73337122a86762e38b982e1ccb909daa3b9bc4197
url: "https://pub.dev"
source: hosted
version: "5.1.2"
version: "5.3.2"
equatable:
dependency: "direct main"
description:
@ -262,10 +262,10 @@ packages:
dependency: transitive
description:
name: ffi
sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
version: "2.1.0"
file:
dependency: transitive
description:
@ -283,10 +283,10 @@ packages:
dependency: "direct main"
description:
name: flutter_bloc
sha256: "434951eea948dbe87f737b674281465f610b8259c16c097b8163ce138749a775"
sha256: e74efb89ee6945bcbce74a5b3a5a3376b088e5f21f55c263fc38cbdc6237faae
url: "https://pub.dev"
source: hosted
version: "8.1.2"
version: "8.1.3"
flutter_blurhash:
dependency: transitive
description:
@ -299,10 +299,10 @@ packages:
dependency: "direct main"
description:
name: flutter_cache_manager
sha256: "32cd900555219333326a2d0653aaaf8671264c29befa65bbd9856d204a4c9fb3"
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
url: "https://pub.dev"
source: hosted
version: "3.3.0"
version: "3.3.1"
flutter_driver:
dependency: "direct dev"
description: flutter
@ -312,10 +312,10 @@ packages:
dependency: "direct main"
description:
name: flutter_email_sender
sha256: "9e253c69617f43d4cb5de672e93a7a19c12a21fb6a75e66c6ce7626336c4c1bc"
sha256: "52b713a67a966be4d9e6f68a323fc0a5bc2da71c567eb451af1aa90d30adbc3a"
url: "https://pub.dev"
source: hosted
version: "5.2.0"
version: "6.0.1"
flutter_fadein:
dependency: "direct main"
description:
@ -344,10 +344,10 @@ packages:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "20c92629902b125cb624efdbacbbe98806b3c0b01adb3d84d1c72198b3eafb1a"
sha256: "3cc40fe8c50ab8383f3e053a499f00f975636622ecdc8e20a77418ece3b1e975"
url: "https://pub.dev"
source: hosted
version: "14.0.1"
version: "15.1.0+1"
flutter_local_notifications_linux:
dependency: transitive
description:
@ -442,10 +442,10 @@ packages:
dependency: "direct main"
description:
name: font_awesome_flutter
sha256: "959ef4add147753f990b4a7c6cccb746d5792dbdc81b1cde99e62e7edb31b206"
sha256: "5fb789145cae1f4c3245c58b3f8fb287d055c26323879eab57a7bf0cfd1e45f3"
url: "https://pub.dev"
source: hosted
version: "10.4.0"
version: "10.5.0"
frontend_server_client:
dependency: transitive
description:
@ -479,10 +479,10 @@ packages:
dependency: transitive
description:
name: glob
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
hive:
dependency: "direct main"
description:
@ -495,10 +495,10 @@ packages:
dependency: "direct main"
description:
name: html
sha256: "58e3491f7bf0b6a4ea5110c0c688877460d1a6366731155c4a4580e7ded773e8"
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.3"
version: "0.15.4"
html_unescape:
dependency: "direct main"
description:
@ -511,10 +511,10 @@ packages:
dependency: "direct main"
description:
name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525"
url: "https://pub.dev"
source: hosted
version: "0.13.6"
version: "1.1.0"
http_multi_server:
dependency: transitive
description:
@ -535,10 +535,10 @@ packages:
dependency: "direct main"
description:
name: hydrated_bloc
sha256: eb92d88061b6b911c48779b08a91c8a9f3a3aa8475f80d9380045375d9876536
sha256: "24994e61f64904d911683cce1a31dc4ef611619da5253f1de2b7b8fc6f79a118"
url: "https://pub.dev"
source: hosted
version: "9.1.0"
version: "9.1.2"
in_app_review:
dependency: "direct main"
description:
@ -595,34 +595,34 @@ packages:
dependency: "direct main"
description:
name: logger
sha256: db2ff852ed77090ba9f62d3611e4208a3d11dfa35991a81ae724c113fcb3e3f7
sha256: "66cb048220ca51cf9011da69fa581e4ee2bed4be6e82870d9e9baae75739da49"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
version: "2.0.1"
logging:
dependency: transitive
description:
name: logging
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
url: "https://pub.dev"
source: hosted
version: "0.12.15"
version: "0.12.16"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
url: "https://pub.dev"
source: hosted
version: "0.2.0"
version: "0.5.0"
memoize:
dependency: "direct main"
description:
@ -651,10 +651,10 @@ packages:
dependency: "direct dev"
description:
name: mocktail
sha256: "80a996cd9a69284b3dc521ce185ffe9150cde69767c2d3a0720147d93c0cef53"
sha256: "9503969a7c2c78c7292022c70c0289ed6241df7a9ba720010c0b215af29a5a58"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
version: "1.0.0"
nested:
dependency: transitive
description:
@ -699,10 +699,10 @@ packages:
dependency: "direct main"
description:
name: package_info_plus
sha256: d39e8fbff4c5aef4592737e25ad6ac500df006ce7a7a8e1f838ce1256e167542
sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "4.1.0"
package_info_plus_platform_interface:
dependency: transitive
description:
@ -723,58 +723,50 @@ packages:
dependency: "direct main"
description:
name: path_provider
sha256: "3087813781ab814e4157b172f1a11c46be20179fcc9bea043e0fba36bc0acaa2"
sha256: "909b84830485dbcd0308edf6f7368bc8fd76afa26a270420f34cabea2a6467a0"
url: "https://pub.dev"
source: hosted
version: "2.0.15"
version: "2.1.0"
path_provider_android:
dependency: "direct main"
description:
name: path_provider_android
sha256: "2cec049d282c7f13c594b4a73976b0b4f2d7a1838a6dd5aaf7bd9719196bee86"
sha256: "5d44fc3314d969b84816b569070d7ace0f1dea04bd94a83f74c4829615d22ad8"
url: "https://pub.dev"
source: hosted
version: "2.0.27"
version: "2.1.0"
path_provider_foundation:
dependency: "direct main"
description:
name: path_provider_foundation
sha256: "1995d88ec2948dac43edf8fe58eb434d35d22a2940ecee1a9fefcd62beee6eb3"
sha256: "1b744d3d774e5a879bb76d6cd1ecee2ba2c6960c03b1020cd35212f6aa267ac5"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
version: "2.3.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
sha256: ba2b77f0c52a33db09fc8caf85b12df691bf28d983e84cf87ff6d693cfa007b3
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.2.0"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
sha256: bced5679c7df11190e1ddc35f3222c858f328fff85c3942e46e7f5589bf9eb84
url: "https://pub.dev"
source: hosted
version: "2.0.6"
version: "2.1.0"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6
sha256: ee0e0d164516b90ae1f970bdf29f726f1aa730d7cfc449ecc74c495378b705da
url: "https://pub.dev"
source: hosted
version: "2.1.6"
pedantic:
dependency: transitive
description:
name: pedantic
sha256: "67fc27ed9639506c856c840ccce7594d0bdcd91bc8d53d6e52359449a1d50602"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
version: "2.2.0"
petitparser:
dependency: transitive
description:
@ -795,10 +787,10 @@ packages:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
sha256: "43798d895c929056255600343db8f049921cbec94d31ec87f1dc5c16c01935dd"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.1.5"
pool:
dependency: transitive
description:
@ -900,82 +892,82 @@ packages:
dependency: "direct main"
description:
name: sembast
sha256: a784dbcf313ff38a7f57249694c64a6bcf79f704dbec127958459a7737716830
sha256: "85ff944434f7b566fdc388be4f85b23e954736b7d6e51f965f4f419d966c15b1"
url: "https://pub.dev"
source: hosted
version: "3.4.4"
version: "3.5.0+1"
share_plus:
dependency: "direct main"
description:
name: share_plus
sha256: "322a1ec9d9fe07e2e2252c098ce93d12dbd06133cc4c00ffe6a4ef505c295c17"
sha256: "6cec740fa0943a826951223e76218df002804adb588235a8910dc3d6b0654e11"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
version: "7.1.0"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: "0c6e61471bd71b04a138b8b588fa388e66d8b005e6f2deda63371c5c505a0981"
sha256: "357412af4178d8e11d14f41723f80f12caea54cf0d5cd29af9dcdab85d58aea7"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
version: "3.3.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "16d3fb6b3692ad244a695c0183fca18cf81fd4b821664394a781de42386bf022"
sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.2.0"
shared_preferences_android:
dependency: "direct main"
description:
name: shared_preferences_android
sha256: "6478c6bbbecfe9aced34c483171e90d7c078f5883558b30ec3163cf18402c749"
sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076
url: "https://pub.dev"
source: hosted
version: "2.1.4"
version: "2.2.0"
shared_preferences_foundation:
dependency: "direct main"
description:
name: shared_preferences_foundation
sha256: e014107bb79d6d3297196f4f2d0db54b5d1f85b8ea8ff63b8e8b391a02700feb
sha256: d29753996d8eb8f7619a1f13df6ce65e34bc107bef6330739ed76f18b22310ef
url: "https://pub.dev"
source: hosted
version: "2.2.2"
version: "2.3.3"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "9d387433ca65717bbf1be88f4d5bb18f10508917a8fa2fb02e0fd0d7479a9afa"
sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.3.0"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: fb5cf25c0235df2d0640ac1b1174f6466bd311f621574997ac59018a6664548d
sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.3.0"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "74083203a8eae241e0de4a0d597dbedab3b8fef5563f33cf3c12d7e93c655ca5"
sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
version: "2.2.0"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "5e588e2efef56916a3b229c3bfe81e6a525665a454519ca51dbcc4236a274173"
sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d
url: "https://pub.dev"
source: hosted
version: "2.2.0"
version: "2.3.0"
shelf:
dependency: transitive
description:
@ -1012,10 +1004,10 @@ packages:
dependency: "direct main"
description:
name: shimmer
sha256: "1f1009b5845a1f88f1c5630212279540486f97409e9fc3f63883e71070d107bf"
sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
@ -1041,26 +1033,26 @@ packages:
dependency: transitive
description:
name: source_span
sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.10.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: b4d6710e1200e96845747e37338ea8a819a12b51689a3bcf31eff0003b37a0b9
sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a"
url: "https://pub.dev"
source: hosted
version: "2.2.8+4"
version: "2.3.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: e77abf6ff961d69dfef41daccbb66b51e9983cdd5cb35bf30733598057401555
sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a"
url: "https://pub.dev"
source: hosted
version: "2.4.5"
version: "2.5.0"
stack_trace:
dependency: transitive
description:
@ -1120,26 +1112,26 @@ packages:
dependency: transitive
description:
name: test
sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4"
sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46"
url: "https://pub.dev"
source: hosted
version: "1.24.1"
version: "1.24.3"
test_api:
dependency: transitive
description:
name: test_api
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
version: "0.6.0"
test_core:
dependency: transitive
description:
name: test_core
sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93"
sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
version: "0.5.3"
timezone:
dependency: transitive
description:
@ -1168,18 +1160,18 @@ packages:
dependency: "direct main"
description:
name: url_launcher
sha256: eb1e00ab44303d50dd487aab67ebc575456c146c6af44422f9c13889984c00f3
sha256: "781bd58a1eb16069412365c98597726cd8810ae27435f04b3b4d3a470bacd61e"
url: "https://pub.dev"
source: hosted
version: "6.1.11"
version: "6.1.12"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "1a5848f598acc5b7d8f7c18b8cb834ab667e59a13edc3c93e9d09cf38cc6bc87"
sha256: "3dd2388cc0c42912eee04434531a26a82512b9cb1827e0214430c9bcbddfe025"
url: "https://pub.dev"
source: hosted
version: "6.0.34"
version: "6.0.38"
url_launcher_ios:
dependency: transitive
description:
@ -1200,34 +1192,34 @@ packages:
dependency: transitive
description:
name: url_launcher_macos
sha256: "91ee3e75ea9dadf38036200c5d3743518f4a5eb77a8d13fda1ee5764373f185e"
sha256: "1c4fdc0bfea61a70792ce97157e5cc17260f61abbe4f39354513f39ec6fd73b1"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.6"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370"
sha256: bfdfa402f1f3298637d71ca8ecfe840b4696698213d5346e9d12d4ab647ee2ea
url: "https://pub.dev"
source: hosted
version: "2.1.2"
version: "2.1.3"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa"
sha256: cc26720eefe98c1b71d85f9dc7ef0cada5132617046369d9dc296b3ecaa5cbb4
url: "https://pub.dev"
source: hosted
version: "2.0.16"
version: "2.0.18"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "254708f17f7c20a9c8c471f67d86d76d4a3f9c1591aad1e15292008aceb82771"
sha256: "7967065dd2b5fccc18c653b97958fdf839c5478c28e767c61ee879f4e7882422"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
version: "3.0.7"
uuid:
dependency: transitive
description:
@ -1248,18 +1240,18 @@ packages:
dependency: "direct dev"
description:
name: very_good_analysis
sha256: "5f77d7c00d6010d8ad93ac5d91ecc851c216bcc1e7a51e56c3c01b27152453bb"
sha256: "5e4ea72d2a9188630f0dd8f120a541de730090ef8863243fedca8267a84508b8"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
version: "5.0.0+1"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe
sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f
url: "https://pub.dev"
source: hosted
version: "11.3.0"
version: "11.7.1"
wakelock:
dependency: "direct main"
description:
@ -1308,6 +1300,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
web_socket_channel:
dependency: transitive
description:
@ -1336,42 +1336,42 @@ packages:
dependency: "direct main"
description:
name: webview_flutter
sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567"
sha256: "789d52bd789373cc1e100fb634af2127e86c99cf9abde09499743270c5de8d00"
url: "https://pub.dev"
source: hosted
version: "4.2.0"
version: "4.2.2"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759"
sha256: bca797abba472868655b5f1a6029c1132385685ee9db4713cb0e7f33076210c6
url: "https://pub.dev"
source: hosted
version: "3.7.0"
version: "3.9.3"
webview_flutter_platform_interface:
dependency: transitive
description:
name: webview_flutter_platform_interface
sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507"
sha256: "0ca3cfcc6781a7de701d580917af4a9efc4e3e129f8ead95a80587f0a749480a"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.5.0"
webview_flutter_wkwebview:
dependency: transitive
description:
name: webview_flutter_wkwebview
sha256: "4646bb68297803bdbb96d46853e8fcb560d6cb5e04153fa64581535767875dfe"
sha256: ed749f94ac9e814d04a258a9255cf69cfa4cc6006ff59542aea7fb4590144972
url: "https://pub.dev"
source: hosted
version: "3.4.3"
version: "3.7.3"
win32:
dependency: "direct overridden"
description:
name: win32
sha256: "6ca3aaab1790eeb1f5cad232e33d9c53ba66e884dd3e7686c4e730bffc45f1a3"
sha256: f2add6fa510d3ae152903412227bda57d0d5a8da61d2c39c1fb022c9429a41c0
url: "https://pub.dev"
source: hosted
version: "5.0.2"
version: "5.0.6"
win32_registry:
dependency: transitive
description:
@ -1392,10 +1392,10 @@ packages:
dependency: transitive
description:
name: xdg_directories
sha256: bd512f03919aac5f1313eb8249f223bacf4927031bf60b02601f81f687689e86
sha256: f0c26453a2d47aa4c2570c6a033246a3fc62da2fe23c7ffdd0a7495086dc0247
url: "https://pub.dev"
source: hosted
version: "0.2.0+3"
version: "1.0.2"
xml:
dependency: transitive
description:
@ -1413,5 +1413,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.0.0 <4.0.0"
flutter: ">=3.10.5"
dart: ">=3.1.0-185.0.dev <4.0.0"
flutter: ">=3.13.2"

View File

@ -1,11 +1,11 @@
name: hacki
description: A Hacker News reader.
version: 1.8.1+117
version: 1.8.4+120
publish_to: none
environment:
sdk: ">=3.0.0 <4.0.0"
flutter: "3.10.5"
flutter: "3.13.2"
dependencies:
adaptive_theme: ^3.2.0
@ -27,11 +27,11 @@ dependencies:
sdk: flutter
flutter_bloc: ^8.1.2
flutter_cache_manager: ^3.3.0
flutter_email_sender: ^5.2.0
flutter_email_sender: ^6.0.1
flutter_fadein: ^2.0.0
flutter_feather_icons: 2.0.0+1
flutter_inappwebview: ^5.7.2+3
flutter_local_notifications: ^14.0.1
flutter_local_notifications: ^15.1.0+1
flutter_secure_storage: ^8.0.0
flutter_siri_suggestions: ^2.1.0
flutter_slidable: ^3.0.0
@ -41,13 +41,13 @@ dependencies:
hive: ^2.2.3
html: ^0.15.1
html_unescape: ^2.0.0
http: ^0.13.5
http: ^1.1.0
hydrated_bloc: ^9.1.0
in_app_review:
path: components/in_app_review
intl: ^0.18.0
linkify: ^5.0.0
logger: ^1.3.0
logger: ^2.0.1
memoize: ^3.0.0
package_info_plus: ^4.0.0
path: ^1.8.2
@ -69,7 +69,7 @@ dependencies:
shared_preferences: ^2.0.17
shared_preferences_android: ^2.0.15
shared_preferences_foundation: ^2.1.3
shimmer: ^2.0.0
shimmer: ^3.0.0
synced_shared_preferences:
path: components/synced_shared_preferences
universal_platform: ^1.0.0+1
@ -89,7 +89,7 @@ dev_dependencies:
sdk: flutter
integration_test:
sdk: flutter
mocktail: ^0.3.0
mocktail: ^1.0.0
very_good_analysis: ^5.0.0
flutter:

View File

@ -85,7 +85,7 @@ void main() {
},
expect: () => <AuthState>[
const AuthState.init().copyWith(
status: AuthStatus.loaded,
status: Status.success,
),
],
verify: (_) {
@ -125,25 +125,25 @@ void main() {
const AuthState(
user: User.empty(),
isLoggedIn: false,
status: AuthStatus.loaded,
status: Status.success,
agreedToEULA: false,
),
const AuthState(
user: User.empty(),
isLoggedIn: false,
status: AuthStatus.loaded,
status: Status.success,
agreedToEULA: true,
),
const AuthState(
user: User.empty(),
isLoggedIn: false,
status: AuthStatus.loading,
status: Status.inProgress,
agreedToEULA: true,
),
const AuthState(
user: tUser,
isLoggedIn: true,
status: AuthStatus.loaded,
status: Status.success,
agreedToEULA: true,
),
],