mirror of
https://github.com/Livinglist/Hacki.git
synced 2025-08-06 18:24:42 +08:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
f0d6cac3fd |
@ -1 +0,0 @@
|
|||||||
- Bugfixes.
|
|
@ -1 +0,0 @@
|
|||||||
- Updates to UI.
|
|
@ -1 +0,0 @@
|
|||||||
- Updates to UI.
|
|
@ -1 +0,0 @@
|
|||||||
- Updates to UI.
|
|
@ -1 +0,0 @@
|
|||||||
- Tapping on comments in notification and history screen will lead you directly to the comment.
|
|
@ -1,3 +0,0 @@
|
|||||||
- Tapping on comment in notification or history screen will now lead you directly to the comment.
|
|
||||||
- Fixed the bug where reply box cannot be expanded in editing mode.
|
|
||||||
- Fixed inconsistent font size in history screen.
|
|
@ -1 +0,0 @@
|
|||||||
- Added offline mode.
|
|
@ -1,2 +0,0 @@
|
|||||||
- Added offline mode.
|
|
||||||
- Bugfixes.
|
|
@ -1,2 +0,0 @@
|
|||||||
- Added offline mode.
|
|
||||||
- Bugfixes.
|
|
@ -1,2 +0,0 @@
|
|||||||
- Added offline mode.
|
|
||||||
- Bugfixes.
|
|
@ -1,3 +0,0 @@
|
|||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,3 +0,0 @@
|
|||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,3 +0,0 @@
|
|||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,4 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
@ -1,5 +0,0 @@
|
|||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
||||||
- Bugfixes.
|
|
@ -1,6 +0,0 @@
|
|||||||
- You can now add filters for searching.
|
|
||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
||||||
- Bugfixes.
|
|
@ -1,6 +0,0 @@
|
|||||||
- You can now add filters for searching.
|
|
||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
||||||
- Bugfixes.
|
|
@ -1,6 +0,0 @@
|
|||||||
- You can now add filters for searching.
|
|
||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
||||||
- Bugfixes.
|
|
@ -1,6 +0,0 @@
|
|||||||
- You can now add filters for searching.
|
|
||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
||||||
- Bugfixes.
|
|
@ -1,7 +0,0 @@
|
|||||||
- You can share links.
|
|
||||||
- You can now add filters for searching.
|
|
||||||
- You can now participate in polls.
|
|
||||||
- Pick up where you left off.
|
|
||||||
- Swipe left on comment tile to view its parents without scrolling all the way up.
|
|
||||||
- Huge performance boost.
|
|
||||||
- Bugfixes.
|
|
2
fastlane/metadata/android/en-US/changelogs/66.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/66.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
- Offline mode now includes web pages.
|
||||||
|
- You can now sort comments in story screen.
|
@ -568,7 +568,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -577,7 +577,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.2.23;
|
MARKETING_VERSION = 0.2.24;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -705,7 +705,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -714,7 +714,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.2.23;
|
MARKETING_VERSION = 0.2.24;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -736,7 +736,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
DEVELOPMENT_TEAM = QMWX3X2NF7;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@ -745,7 +745,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 0.2.23;
|
MARKETING_VERSION = 0.2.24;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
PRODUCT_BUNDLE_IDENTIFIER = com.jiaqi.hacki;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
@ -7,7 +7,9 @@ import 'package:hacki/config/locator.dart';
|
|||||||
import 'package:hacki/cubits/cubits.dart';
|
import 'package:hacki/cubits/cubits.dart';
|
||||||
import 'package:hacki/models/models.dart';
|
import 'package:hacki/models/models.dart';
|
||||||
import 'package:hacki/repositories/repositories.dart';
|
import 'package:hacki/repositories/repositories.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
part 'stories_event.dart';
|
part 'stories_event.dart';
|
||||||
part 'stories_state.dart';
|
part 'stories_state.dart';
|
||||||
@ -281,12 +283,6 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
|||||||
includingWebPage: event.includingWebPage,
|
includingWebPage: event.includingWebPage,
|
||||||
isPrioritized: false,
|
isPrioritized: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
downloadStatus: StoriesDownloadStatus.finished,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
@ -321,41 +317,50 @@ class StoriesBloc extends Bloc<StoriesEvent, StoriesState> {
|
|||||||
await _cacheRepository.cacheStory(story: story);
|
await _cacheRepository.cacheStory(story: story);
|
||||||
|
|
||||||
if (story.url.isNotEmpty && includingWebPage) {
|
if (story.url.isNotEmpty && includingWebPage) {
|
||||||
|
locator.get<Logger>().i('downloading ${story.url}');
|
||||||
await _cacheRepository.cacheUrl(url: story.url);
|
await _cacheRepository.cacheUrl(url: story.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Completer<void> completer = Completer<void>();
|
|
||||||
_storiesRepository
|
_storiesRepository
|
||||||
.fetchAllChildrenComments(ids: story.kids)
|
.fetchAllChildrenComments(ids: story.kids)
|
||||||
.listen((Comment? comment) async {
|
.whereType<Comment>()
|
||||||
if (comment != null) {
|
.listen(
|
||||||
await _cacheRepository.cacheComment(comment: comment);
|
(Comment comment) => unawaited(
|
||||||
}
|
_cacheRepository.cacheComment(comment: comment),
|
||||||
}).onDone(() {
|
),
|
||||||
completer.complete();
|
)
|
||||||
add(StoryDownloaded(skipped: false));
|
.onDone(() => add(StoryDownloaded(skipped: false)));
|
||||||
});
|
|
||||||
|
|
||||||
await completer.future;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onStoryDownloaded(StoryDownloaded event, Emitter<StoriesState> emit) {
|
void onStoryDownloaded(StoryDownloaded event, Emitter<StoriesState> emit) {
|
||||||
if (event.skipped) {
|
if (event.skipped) {
|
||||||
|
final int updatedStoriesToBeDownloaded = state.storiesToBeDownloaded - 1;
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
storiesToBeDownloaded: state.storiesToBeDownloaded - 1,
|
storiesToBeDownloaded: updatedStoriesToBeDownloaded,
|
||||||
|
downloadStatus:
|
||||||
|
state.storiesDownloaded == updatedStoriesToBeDownloaded
|
||||||
|
? StoriesDownloadStatus.finished
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final int updatedStoriesDownloaded = state.storiesDownloaded + 1;
|
final int updatedStoriesDownloaded = state.storiesDownloaded + 1;
|
||||||
|
final int updatedStoriesToBeDownloaded =
|
||||||
|
updatedStoriesDownloaded > state.storiesToBeDownloaded
|
||||||
|
? state.storiesToBeDownloaded + 1
|
||||||
|
: state.storiesToBeDownloaded;
|
||||||
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
storiesDownloaded: updatedStoriesDownloaded,
|
storiesDownloaded: updatedStoriesDownloaded,
|
||||||
storiesToBeDownloaded:
|
storiesToBeDownloaded: updatedStoriesToBeDownloaded,
|
||||||
updatedStoriesDownloaded > state.storiesToBeDownloaded
|
downloadStatus:
|
||||||
? state.storiesToBeDownloaded + 1
|
updatedStoriesDownloaded == updatedStoriesToBeDownloaded
|
||||||
: state.storiesToBeDownloaded,
|
? StoriesDownloadStatus.finished
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
31
lib/config/custom_log_filter.dart
Normal file
31
lib/config/custom_log_filter.dart
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
|
class CustomLogFilter extends LogFilter {
|
||||||
|
@override
|
||||||
|
// ignore: overridden_fields
|
||||||
|
Level? level = Level.verbose;
|
||||||
|
|
||||||
|
/// The minimal level allowed in production.
|
||||||
|
static const Level _minimalLevel = Level.info;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldLog(LogEvent event) {
|
||||||
|
bool shouldLog = false;
|
||||||
|
|
||||||
|
if (event.level.index >= _minimalLevel.index) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(
|
||||||
|
() {
|
||||||
|
if (event.level.index >= level!.index) {
|
||||||
|
shouldLog = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}(),
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
|
||||||
|
return shouldLog;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
import 'package:hacki/config/custom_log_filter.dart';
|
||||||
import 'package:hacki/repositories/repositories.dart';
|
import 'package:hacki/repositories/repositories.dart';
|
||||||
import 'package:hacki/services/services.dart';
|
import 'package:hacki/services/services.dart';
|
||||||
import 'package:logger/logger.dart';
|
import 'package:logger/logger.dart';
|
||||||
@ -19,8 +19,5 @@ Future<void> setUpLocator() async {
|
|||||||
..registerSingleton<CacheRepository>(CacheRepository())
|
..registerSingleton<CacheRepository>(CacheRepository())
|
||||||
..registerSingleton<CacheService>(CacheService())
|
..registerSingleton<CacheService>(CacheService())
|
||||||
..registerSingleton<LocalNotification>(LocalNotification())
|
..registerSingleton<LocalNotification>(LocalNotification())
|
||||||
..registerSingleton<Logger>(Logger())
|
..registerSingleton<Logger>(Logger(filter: CustomLogFilter()));
|
||||||
..registerSingleton<RouteObserver<ModalRoute<dynamic>>>(
|
|
||||||
RouteObserver<ModalRoute<dynamic>>(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -221,9 +221,6 @@ class HackiApp extends StatelessWidget {
|
|||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: useTrueDark ? trueDarkTheme : theme,
|
theme: useTrueDark ? trueDarkTheme : theme,
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: navigatorKey,
|
||||||
navigatorObservers: <NavigatorObserver>[
|
|
||||||
locator.get<RouteObserver<ModalRoute<dynamic>>>(),
|
|
||||||
],
|
|
||||||
onGenerateRoute: CustomRouter.onGenerateRoute,
|
onGenerateRoute: CustomRouter.onGenerateRoute,
|
||||||
initialRoute: HomeScreen.routeName,
|
initialRoute: HomeScreen.routeName,
|
||||||
),
|
),
|
||||||
|
@ -5,6 +5,7 @@ import 'package:hacki/config/locator.dart';
|
|||||||
import 'package:hacki/models/models.dart';
|
import 'package:hacki/models/models.dart';
|
||||||
import 'package:hacki/repositories/postable_repository.dart';
|
import 'package:hacki/repositories/postable_repository.dart';
|
||||||
import 'package:hacki/repositories/repositories.dart';
|
import 'package:hacki/repositories/repositories.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
class AuthRepository extends PostableRepository {
|
class AuthRepository extends PostableRepository {
|
||||||
AuthRepository({
|
AuthRepository({
|
||||||
@ -44,6 +45,7 @@ class AuthRepository extends PostableRepository {
|
|||||||
password: password,
|
password: password,
|
||||||
);
|
);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:hacki/config/locator.dart';
|
||||||
import 'package:hacki/models/models.dart';
|
import 'package:hacki/models/models.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:http/http.dart';
|
import 'package:http/http.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
|
|
||||||
/// [CacheRepository] is for storing stories and comments for offline reading.
|
/// [CacheRepository] is for storing stories and comments for offline reading.
|
||||||
/// It's using [Hive] as its database which is being stored in temp directory.
|
/// It's using [Hive] as its database which is being stored in temp directory.
|
||||||
@ -9,12 +11,12 @@ class CacheRepository {
|
|||||||
CacheRepository({
|
CacheRepository({
|
||||||
Future<Box<List<int>>>? storyIdBox,
|
Future<Box<List<int>>>? storyIdBox,
|
||||||
Future<Box<Map<dynamic, dynamic>>>? storyBox,
|
Future<Box<Map<dynamic, dynamic>>>? storyBox,
|
||||||
Future<Box<String>>? webPageBox,
|
Future<LazyBox<String>>? webPageBox,
|
||||||
Future<LazyBox<Map<dynamic, dynamic>>>? commentBox,
|
Future<LazyBox<Map<dynamic, dynamic>>>? commentBox,
|
||||||
}) : _storyIdBox = storyIdBox ?? Hive.openBox<List<int>>(_storyIdBoxName),
|
}) : _storyIdBox = storyIdBox ?? Hive.openBox<List<int>>(_storyIdBoxName),
|
||||||
_storyBox =
|
_storyBox =
|
||||||
storyBox ?? Hive.openBox<Map<dynamic, dynamic>>(_storyBoxName),
|
storyBox ?? Hive.openBox<Map<dynamic, dynamic>>(_storyBoxName),
|
||||||
_webPageBox = webPageBox ?? Hive.openBox<String>(_webPageBoxName),
|
_webPageBox = webPageBox ?? Hive.openLazyBox<String>(_webPageBoxName),
|
||||||
_commentBox = commentBox ??
|
_commentBox = commentBox ??
|
||||||
Hive.openLazyBox<Map<dynamic, dynamic>>(_commentBoxName);
|
Hive.openLazyBox<Map<dynamic, dynamic>>(_commentBoxName);
|
||||||
|
|
||||||
@ -25,7 +27,7 @@ class CacheRepository {
|
|||||||
final Future<Box<List<int>>> _storyIdBox;
|
final Future<Box<List<int>>> _storyIdBox;
|
||||||
final Future<Box<Map<dynamic, dynamic>>> _storyBox;
|
final Future<Box<Map<dynamic, dynamic>>> _storyBox;
|
||||||
final Future<LazyBox<Map<dynamic, dynamic>>> _commentBox;
|
final Future<LazyBox<Map<dynamic, dynamic>>> _commentBox;
|
||||||
final Future<Box<String>> _webPageBox;
|
final Future<LazyBox<String>> _webPageBox;
|
||||||
|
|
||||||
Future<bool> get hasCachedStories =>
|
Future<bool> get hasCachedStories =>
|
||||||
_storyBox.then((Box<Map<dynamic, dynamic>> box) => box.isNotEmpty);
|
_storyBox.then((Box<Map<dynamic, dynamic>> box) => box.isNotEmpty);
|
||||||
@ -34,39 +36,92 @@ class CacheRepository {
|
|||||||
required StoryType of,
|
required StoryType of,
|
||||||
required List<int> ids,
|
required List<int> ids,
|
||||||
}) async {
|
}) async {
|
||||||
final Box<List<int>> box = await _storyIdBox;
|
late final Box<List<int>> box;
|
||||||
|
|
||||||
|
try {
|
||||||
|
box = await _storyIdBox;
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_storyIdBoxName);
|
||||||
|
box = await _storyIdBox;
|
||||||
|
}
|
||||||
|
|
||||||
return box.put(of.name, ids);
|
return box.put(of.name, ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> cacheStory({required Story story}) async {
|
Future<void> cacheStory({required Story story}) async {
|
||||||
final Box<Map<dynamic, dynamic>> box = await _storyBox;
|
late final Box<Map<dynamic, dynamic>> box;
|
||||||
|
|
||||||
|
try {
|
||||||
|
box = await _storyBox;
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_storyBoxName);
|
||||||
|
box = await _storyBox;
|
||||||
|
}
|
||||||
|
|
||||||
return box.put(story.id.toString(), story.toJson());
|
return box.put(story.id.toString(), story.toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> cacheUrl({required String url}) async {
|
Future<void> cacheUrl({required String url}) async {
|
||||||
final Box<String> box = await _webPageBox;
|
late final LazyBox<String> box;
|
||||||
final String html = await compute(downloadWebPage, url);
|
|
||||||
|
try {
|
||||||
|
box = await _webPageBox;
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_webPageBoxName);
|
||||||
|
box = await _webPageBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String html = await compute(_downloadWebPage, url);
|
||||||
return box.put(url, html);
|
return box.put(url, html);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> getHtml({required String url}) async {
|
Future<String?> getHtml({required String url}) async {
|
||||||
final Box<String> box = await _webPageBox;
|
try {
|
||||||
|
final LazyBox<String> box = await _webPageBox;
|
||||||
return box.get(url);
|
return box.get(url);
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_webPageBoxName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> hasCachedWebPage({required String url}) async {
|
Future<bool> hasCachedWebPage({required String url}) async {
|
||||||
final Box<String> box = await _webPageBox;
|
try {
|
||||||
|
final LazyBox<String> box = await _webPageBox;
|
||||||
return box.containsKey(url);
|
return box.containsKey(url);
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_webPageBoxName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<int>> getCachedStoryIds({required StoryType of}) async {
|
Future<List<int>> getCachedStoryIds({required StoryType of}) async {
|
||||||
|
try {
|
||||||
final Box<List<int>> box = await _storyIdBox;
|
final Box<List<int>> box = await _storyIdBox;
|
||||||
final List<int>? ids = box.get(of.name);
|
final List<int>? ids = box.get(of.name);
|
||||||
return ids ?? <int>[];
|
return ids ?? <int>[];
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_storyIdBoxName);
|
||||||
|
return <int>[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Story> getCachedStoriesStream({required List<int> ids}) async* {
|
Stream<Story> getCachedStoriesStream({required List<int> ids}) async* {
|
||||||
final Box<Map<dynamic, dynamic>> box = await _storyBox;
|
late final Box<Map<dynamic, dynamic>> box;
|
||||||
|
|
||||||
|
try {
|
||||||
|
box = await _storyBox;
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_storyBoxName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (final int id in ids) {
|
for (final int id in ids) {
|
||||||
final Map<dynamic, dynamic>? json = box.get(id.toString());
|
final Map<dynamic, dynamic>? json = box.get(id.toString());
|
||||||
@ -83,7 +138,16 @@ class CacheRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Story?> getCachedStory({required int id}) async {
|
Future<Story?> getCachedStory({required int id}) async {
|
||||||
final Box<Map<dynamic, dynamic>> box = await _storyBox;
|
late final Box<Map<dynamic, dynamic>> box;
|
||||||
|
|
||||||
|
try {
|
||||||
|
box = await _storyBox;
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_storyBoxName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final Map<dynamic, dynamic>? json = box.get(id.toString());
|
final Map<dynamic, dynamic>? json = box.get(id.toString());
|
||||||
if (json == null) {
|
if (json == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -93,11 +157,21 @@ class CacheRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> cacheComment({required Comment comment}) async {
|
Future<void> cacheComment({required Comment comment}) async {
|
||||||
final LazyBox<Map<dynamic, dynamic>> box = await _commentBox;
|
late final LazyBox<Map<dynamic, dynamic>> box;
|
||||||
|
|
||||||
|
try {
|
||||||
|
box = await _commentBox;
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_commentBoxName);
|
||||||
|
box = await _commentBox;
|
||||||
|
}
|
||||||
|
|
||||||
return box.put(comment.id.toString(), comment.toJson());
|
return box.put(comment.id.toString(), comment.toJson());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Comment?> getCachedComment({required int id}) async {
|
Future<Comment?> getCachedComment({required int id}) async {
|
||||||
|
try {
|
||||||
final LazyBox<Map<dynamic, dynamic>> box = await _commentBox;
|
final LazyBox<Map<dynamic, dynamic>> box = await _commentBox;
|
||||||
final Map<dynamic, dynamic>? json = await box.get(id.toString());
|
final Map<dynamic, dynamic>? json = await box.get(id.toString());
|
||||||
if (json == null) {
|
if (json == null) {
|
||||||
@ -105,6 +179,11 @@ class CacheRepository {
|
|||||||
}
|
}
|
||||||
final Comment comment = Comment.fromJson(json.cast<String, dynamic>());
|
final Comment comment = Comment.fromJson(json.cast<String, dynamic>());
|
||||||
return comment;
|
return comment;
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_commentBoxName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Comment> getCachedCommentsStream({
|
Stream<Comment> getCachedCommentsStream({
|
||||||
@ -127,23 +206,47 @@ class CacheRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<int> deleteAllStoryIds() async {
|
Future<int> deleteAllStoryIds() async {
|
||||||
|
try {
|
||||||
final Box<List<int>> box = await _storyIdBox;
|
final Box<List<int>> box = await _storyIdBox;
|
||||||
return box.clear();
|
return box.clear();
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_storyIdBoxName);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> deleteAllStories() async {
|
Future<int> deleteAllStories() async {
|
||||||
|
try {
|
||||||
final Box<Map<dynamic, dynamic>> box = await _storyBox;
|
final Box<Map<dynamic, dynamic>> box = await _storyBox;
|
||||||
return box.clear();
|
return box.clear();
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_storyBoxName);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> deleteAllComments() async {
|
Future<int> deleteAllComments() async {
|
||||||
|
try {
|
||||||
final LazyBox<Map<dynamic, dynamic>> box = await _commentBox;
|
final LazyBox<Map<dynamic, dynamic>> box = await _commentBox;
|
||||||
return box.clear();
|
return box.clear();
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_commentBoxName);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> deleteAllWebPages() async {
|
Future<int> deleteAllWebPages() async {
|
||||||
final Box<String> box = await _webPageBox;
|
try {
|
||||||
|
final LazyBox<String> box = await _webPageBox;
|
||||||
return box.clear();
|
return box.clear();
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
await Hive.deleteBoxFromDisk(_webPageBoxName);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> deleteAll() async {
|
Future<int> deleteAll() async {
|
||||||
@ -153,12 +256,13 @@ class CacheRepository {
|
|||||||
.whenComplete(deleteAllWebPages);
|
.whenComplete(deleteAllWebPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<String> downloadWebPage(String link) async {
|
static Future<String> _downloadWebPage(String link) async {
|
||||||
try {
|
try {
|
||||||
final Client client = Client();
|
final Client client = Client();
|
||||||
final Uri url = Uri.parse(link);
|
final Uri url = Uri.parse(link);
|
||||||
final Response response = await client.get(url);
|
final Response response = await client.get(url);
|
||||||
final String body = response.body;
|
final String body = response.body;
|
||||||
|
client.close();
|
||||||
return body;
|
return body;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return '''Web page not available.''';
|
return '''Web page not available.''';
|
||||||
|
@ -2,6 +2,8 @@ import 'dart:async';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:hacki/config/locator.dart';
|
||||||
|
import 'package:logger/logger.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:synced_shared_preferences/synced_shared_preferences.dart';
|
import 'package:synced_shared_preferences/synced_shared_preferences.dart';
|
||||||
|
|
||||||
@ -153,9 +155,13 @@ class PreferenceRepository {
|
|||||||
aOptions: androidOptions,
|
aOptions: androidOptions,
|
||||||
);
|
);
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
|
try {
|
||||||
await _secureStorage.deleteAll(
|
await _secureStorage.deleteAll(
|
||||||
aOptions: androidOptions,
|
aOptions: androidOptions,
|
||||||
);
|
);
|
||||||
|
} catch (_) {
|
||||||
|
locator.get<Logger>().e(_);
|
||||||
|
}
|
||||||
|
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ class HomeScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HomeScreenState extends State<HomeScreen>
|
class _HomeScreenState extends State<HomeScreen>
|
||||||
with SingleTickerProviderStateMixin, RouteAware {
|
with SingleTickerProviderStateMixin {
|
||||||
final CacheService cacheService = locator.get<CacheService>();
|
final CacheService cacheService = locator.get<CacheService>();
|
||||||
final Throttle featureDiscoveryDismissThrottle = Throttle(
|
final Throttle featureDiscoveryDismissThrottle = Throttle(
|
||||||
delay: _throttleDelay,
|
delay: _throttleDelay,
|
||||||
@ -61,18 +61,6 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
|
|
||||||
static const Duration _throttleDelay = Duration(seconds: 1);
|
static const Duration _throttleDelay = Duration(seconds: 1);
|
||||||
|
|
||||||
@override
|
|
||||||
void didPopNext() {
|
|
||||||
super.didPopNext();
|
|
||||||
if (context.read<StoriesBloc>().deviceScreenType ==
|
|
||||||
DeviceScreenType.mobile) {
|
|
||||||
Future<void>.delayed(
|
|
||||||
const Duration(milliseconds: 500),
|
|
||||||
cacheService.resetCollapsedComments,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -100,23 +88,13 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
siriSuggestionSubject.stream.listen(onSiriSuggestionTapped);
|
siriSuggestionSubject.stream.listen(onSiriSuggestionTapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
SchedulerBinding.instance
|
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||||
..addPostFrameCallback((_) {
|
|
||||||
FeatureDiscovery.discoverFeatures(
|
FeatureDiscovery.discoverFeatures(
|
||||||
context,
|
context,
|
||||||
const <String>{
|
const <String>{
|
||||||
Constants.featureLogIn,
|
Constants.featureLogIn,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
})
|
|
||||||
..addPostFrameCallback((_) {
|
|
||||||
final ModalRoute<dynamic>? route = ModalRoute.of(context);
|
|
||||||
|
|
||||||
if (route == null) return;
|
|
||||||
|
|
||||||
locator
|
|
||||||
.get<RouteObserver<ModalRoute<dynamic>>>()
|
|
||||||
.subscribe(this, route);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tabController = TabController(vsync: this, length: 6)
|
tabController = TabController(vsync: this, length: 6)
|
||||||
|
@ -66,12 +66,17 @@ class ItemScreen extends StatefulWidget {
|
|||||||
static Route<dynamic> route(ItemScreenArgs args) {
|
static Route<dynamic> route(ItemScreenArgs args) {
|
||||||
return MaterialPageRoute<ItemScreen>(
|
return MaterialPageRoute<ItemScreen>(
|
||||||
settings: const RouteSettings(name: routeName),
|
settings: const RouteSettings(name: routeName),
|
||||||
builder: (BuildContext context) => MultiBlocProvider(
|
builder: (BuildContext context) => RepositoryProvider<CacheService>(
|
||||||
|
create: (BuildContext context) => CacheService(),
|
||||||
|
lazy: false,
|
||||||
|
child: MultiBlocProvider(
|
||||||
providers: <BlocProvider<dynamic>>[
|
providers: <BlocProvider<dynamic>>[
|
||||||
BlocProvider<CommentsCubit>(
|
BlocProvider<CommentsCubit>(
|
||||||
create: (_) => CommentsCubit(
|
create: (BuildContext context) => CommentsCubit(
|
||||||
offlineReading: context.read<StoriesBloc>().state.offlineReading,
|
offlineReading:
|
||||||
|
context.read<StoriesBloc>().state.offlineReading,
|
||||||
item: args.item,
|
item: args.item,
|
||||||
|
cacheService: context.read<CacheService>(),
|
||||||
)..init(
|
)..init(
|
||||||
onlyShowTargetComment: args.onlyShowTargetComment,
|
onlyShowTargetComment: args.onlyShowTargetComment,
|
||||||
targetParents: args.targetComments,
|
targetParents: args.targetComments,
|
||||||
@ -87,6 +92,7 @@ class ItemScreen extends StatefulWidget {
|
|||||||
parentComments: args.targetComments ?? <Comment>[],
|
parentComments: args.targetComments ?? <Comment>[],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,13 +106,17 @@ class ItemScreen extends StatefulWidget {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
child: RepositoryProvider<CacheService>(
|
||||||
|
create: (BuildContext context) => CacheService(),
|
||||||
child: MultiBlocProvider(
|
child: MultiBlocProvider(
|
||||||
key: ValueKey<ItemScreenArgs>(args),
|
key: ValueKey<ItemScreenArgs>(args),
|
||||||
providers: <BlocProvider<dynamic>>[
|
providers: <BlocProvider<dynamic>>[
|
||||||
BlocProvider<CommentsCubit>(
|
BlocProvider<CommentsCubit>(
|
||||||
create: (BuildContext context) => CommentsCubit(
|
create: (BuildContext context) => CommentsCubit(
|
||||||
offlineReading: context.read<StoriesBloc>().state.offlineReading,
|
offlineReading:
|
||||||
|
context.read<StoriesBloc>().state.offlineReading,
|
||||||
item: args.item,
|
item: args.item,
|
||||||
|
cacheService: context.read<CacheService>(),
|
||||||
)..init(
|
)..init(
|
||||||
onlyShowTargetComment: args.onlyShowTargetComment,
|
onlyShowTargetComment: args.onlyShowTargetComment,
|
||||||
targetParents: args.targetComments,
|
targetParents: args.targetComments,
|
||||||
@ -123,6 +133,7 @@ class ItemScreen extends StatefulWidget {
|
|||||||
splitViewEnabled: true,
|
splitViewEnabled: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ class _ProfileScreenState extends State<ProfileScreen>
|
|||||||
showAboutDialog(
|
showAboutDialog(
|
||||||
context: context,
|
context: context,
|
||||||
applicationName: 'Hacki',
|
applicationName: 'Hacki',
|
||||||
applicationVersion: 'v0.2.23',
|
applicationVersion: 'v0.2.24',
|
||||||
applicationIcon: ClipRRect(
|
applicationIcon: ClipRRect(
|
||||||
borderRadius: const BorderRadius.all(
|
borderRadius: const BorderRadius.all(
|
||||||
Radius.circular(
|
Radius.circular(
|
||||||
|
@ -7,6 +7,7 @@ import 'package:hacki/blocs/blocs.dart';
|
|||||||
import 'package:hacki/cubits/cubits.dart';
|
import 'package:hacki/cubits/cubits.dart';
|
||||||
import 'package:hacki/extensions/extensions.dart';
|
import 'package:hacki/extensions/extensions.dart';
|
||||||
import 'package:hacki/models/models.dart';
|
import 'package:hacki/models/models.dart';
|
||||||
|
import 'package:hacki/services/services.dart';
|
||||||
import 'package:hacki/styles/styles.dart';
|
import 'package:hacki/styles/styles.dart';
|
||||||
import 'package:hacki/utils/utils.dart';
|
import 'package:hacki/utils/utils.dart';
|
||||||
|
|
||||||
@ -43,6 +44,7 @@ class CommentTile extends StatelessWidget {
|
|||||||
lazy: false,
|
lazy: false,
|
||||||
create: (_) => CollapseCubit(
|
create: (_) => CollapseCubit(
|
||||||
commentId: comment.id,
|
commentId: comment.id,
|
||||||
|
cacheService: context.read<CacheService>(),
|
||||||
)..init(),
|
)..init(),
|
||||||
child: BlocBuilder<CollapseCubit, CollapseState>(
|
child: BlocBuilder<CollapseCubit, CollapseState>(
|
||||||
builder: (BuildContext context, CollapseState state) {
|
builder: (BuildContext context, CollapseState state) {
|
||||||
|
@ -2,11 +2,11 @@ import 'package:hacki/models/models.dart' show Comment;
|
|||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
class CacheService {
|
class CacheService {
|
||||||
static final Map<int, Comment> _comments = <int, Comment>{};
|
final Map<int, Comment> _comments = <int, Comment>{};
|
||||||
static final Map<int, String> _drafts = <int, String>{};
|
final Map<int, String> _drafts = <int, String>{};
|
||||||
static final Map<int, Set<int>> _kids = <int, Set<int>>{};
|
final Map<int, Set<int>> _kids = <int, Set<int>>{};
|
||||||
static final Set<int> _collapsed = <int>{};
|
final Set<int> _collapsed = <int>{};
|
||||||
static final Map<int, Set<int>> _hidden = <int, Set<int>>{};
|
final Map<int, Set<int>> _hidden = <int, Set<int>>{};
|
||||||
final PublishSubject<Map<int, Set<int>>> _hiddenCommentsSubject =
|
final PublishSubject<Map<int, Set<int>>> _hiddenCommentsSubject =
|
||||||
PublishSubject<Map<int, Set<int>>>();
|
PublishSubject<Map<int, Set<int>>>();
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
name: hacki
|
name: hacki
|
||||||
description: A Hacker News reader.
|
description: A Hacker News reader.
|
||||||
version: 0.2.23+65
|
version: 0.2.24+66
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
Reference in New Issue
Block a user