diff --git a/lib/main_common.dart b/lib/main_common.dart
index 0dec2f1..1854e84 100644
--- a/lib/main_common.dart
+++ b/lib/main_common.dart
@@ -12,6 +12,7 @@ import 'l10n/timeago/pl.dart';
import 'pages/log_console/log_console_page_store.dart';
import 'stores/accounts_store.dart';
import 'stores/config_store.dart';
+import 'stores/hidden_store.dart';
import 'util/mobx_provider.dart';
Future<void> mainCommon(AppConfig appConfig) async {
@@ -29,6 +30,7 @@ Future<void> mainCommon(AppConfig appConfig) async {
MultiProvider(
providers: [
MobxProvider(create: (context) => ConfigStore.load(sharedPrefs)),
+ MobxProvider(create: (context) => HiddenStore.load(sharedPrefs)),
ChangeNotifierProvider.value(value: accountsStore),
MobxProvider.value(value: logConsoleStore),
],
diff --git a/lib/stores/hidden_store.dart b/lib/stores/hidden_store.dart
new file mode 100644
index 0000000..d00fe15
--- /dev/null
+++ b/lib/stores/hidden_store.dart
@@ -0,0 +1,50 @@
+import 'dart:convert';
+
+import 'package:flutter/material.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:mobx/mobx.dart';
+import 'package:shared_preferences/shared_preferences.dart';
+
+import '../l10n/l10n.dart';
+import '../util/mobx_provider.dart';
+
+part 'hidden_store.g.dart';
+
+/// Store managing user-level configuration such as theme or language
+@JsonSerializable()
+@LocaleConverter()
+class HiddenStore extends _HiddenStore with _$HiddenStore, DisposableStore {
+ static const _prefsKey = 'v1:HiddenStore';
+ late final SharedPreferences _sharedPrefs;
+
+ @visibleForTesting
+ HiddenStore();
+
+ factory HiddenStore.load(SharedPreferences sharedPrefs) {
+ final store = _$HiddenStoreFromJson(
+ jsonDecode(sharedPrefs.getString(_prefsKey) ?? '{}')
+ as Map<String, dynamic>,
+ ).._sharedPrefs = sharedPrefs;
+
+ store.addReaction(autorun((_) => store.save()));
+
+ return store;
+ }
+
+ Future<void> save() async {
+ final serialized = jsonEncode(_$HiddenStoreToJson(this));
+
+ await _sharedPrefs.setString(_prefsKey, serialized);
+ }
+}
+
+abstract class _HiddenStore with Store {
+ @observable
+ @JsonKey(defaultValue: [])
+ List<int> postIds = [];
+
+ @action
+ void addPost(int postId) {
+ postIds.add(postId);
+ }
+}
diff --git a/lib/stores/hidden_store.g.dart b/lib/stores/hidden_store.g.dart
new file mode 100644
index 0000000..171e198
--- /dev/null
+++ b/lib/stores/hidden_store.g.dart
@@ -0,0 +1,61 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'hidden_store.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+HiddenStore _$HiddenStoreFromJson(Map<String, dynamic> json) => HiddenStore()
+ ..postIds =
+ (json['postIds'] as List<dynamic>?)?.map((e) => e as int).toList() ?? [];
+
+Map<String, dynamic> _$HiddenStoreToJson(HiddenStore instance) =>
+ <String, dynamic>{
+ 'postIds': instance.postIds,
+ };
+
+// **************************************************************************
+// StoreGenerator
+// **************************************************************************
+
+// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic, no_leading_underscores_for_local_identifiers
+
+mixin _$HiddenStore on _HiddenStore, Store {
+ late final _$postIdsAtom =
+ Atom(name: '_HiddenStore.postIds', context: context);
+
+ @override
+ List<int> get postIds {
+ _$postIdsAtom.reportRead();
+ return super.postIds;
+ }
+
+ @override
+ set postIds(List<int> value) {
+ _$postIdsAtom.reportWrite(value, super.postIds, () {
+ super.postIds = value;
+ });
+ }
+
+ late final _$_HiddenStoreActionController =
+ ActionController(name: '_HiddenStore', context: context);
+
+ @override
+ void addPost(int postId) {
+ final _$actionInfo = _$_HiddenStoreActionController.startAction(
+ name: '_HiddenStore.addPost');
+ try {
+ return super.addPost(postId);
+ } finally {
+ _$_HiddenStoreActionController.endAction(_$actionInfo);
+ }
+ }
+
+ @override
+ String toString() {
+ return '''
+postIds: ${postIds}
+ ''';
+ }
+}
diff --git a/lib/widgets/post/post_actions.dart b/lib/widgets/post/post_actions.dart
index 1f89e1a..3764f6e 100644
--- a/lib/widgets/post/post_actions.dart
+++ b/lib/widgets/post/post_actions.dart
@@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
+import '../../hooks/stores.dart';
import '../../l10n/l10n.dart';
+import '../../stores/hidden_store.dart';
import '../../util/icons.dart';
import '../../util/observer_consumers.dart';
import '../../util/share.dart';
@@ -17,6 +19,7 @@ class PostActions extends HookWidget {
Widget build(BuildContext context) {
final fullPost = context.read<IsFullPost>();
final shareButtonKey = GlobalKey();
+ final hiddenStore = useStore((HiddenStore store) => store);
// assemble actions section
return ObserverBuilder<PostStore>(builder: (context, store) {
return Padding(
@@ -34,6 +37,16 @@ class PostActions extends HookWidget {
softWrap: false,
),
),
+ if (!fullPost)
+ IconButton(
+ onPressed: () =>
+ // hiddenStore.postIds.add(store.postView.post.id),
+ hiddenStore.postIds = [
+ ...hiddenStore.postIds,
+ store.postView.post.id
+ ],
+ icon: const Icon(Icons.visibility_off),
+ ),
if (!fullPost)
IconButton(
key: shareButtonKey,
diff --git a/lib/widgets/sortable_infinite_list.dart b/lib/widgets/sortable_infinite_list.dart
index 47a43ae..be0e73a 100644
--- a/lib/widgets/sortable_infinite_list.dart
+++ b/lib/widgets/sortable_infinite_list.dart
@@ -7,6 +7,7 @@ import '../hooks/infinite_scroll.dart';
import '../hooks/stores.dart';
import '../resources/app_theme.dart';
import '../stores/config_store.dart';
+import '../stores/hidden_store.dart';
import '../util/observer_consumers.dart';
import 'comment/comment.dart';
import 'comment_list_options.dart';
@@ -128,32 +129,42 @@ class InfinitePostList extends SortableInfiniteList<PostView> {
required super.fetcher,
super.controller,
}) : super(
- itemBuilder: (post) => Consumer<AppTheme>(
- builder: (context, state, child) => Column(
- children: [
- PostTile.fromPostView(post),
- if (state.amoled)
- SizedBox(
- width: 250,
- height: 1,
- child: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topRight,
- end: Alignment.bottomLeft,
- colors: [
- Theme.of(context).primaryColorDark,
- Theme.of(context).colorScheme.secondary,
- Theme.of(context).primaryColorDark,
- ],
- )),
+ itemBuilder: (post) => ObserverBuilder<HiddenStore>(
+ builder: (context, hiddenStore) => AnimatedCrossFade(
+ duration: const Duration(milliseconds: 300),
+ firstChild: const SizedBox(height: 0),
+ secondChild: Consumer<AppTheme>(
+ builder: (context, state, child) => Column(
+ children: [
+ PostTile.fromPostView(post),
+ if (state.amoled)
+ SizedBox(
+ width: 250,
+ height: 1,
+ child: Container(
+ decoration: BoxDecoration(
+ gradient: LinearGradient(
+ begin: Alignment.topRight,
+ end: Alignment.bottomLeft,
+ colors: [
+ Theme.of(context).primaryColorDark,
+ Theme.of(context).colorScheme.secondary,
+ Theme.of(context).primaryColorDark,
+ ],
+ )),
+ ),
+ ),
+ SizedBox(
+ height: context.read<ConfigStore>().compactPostView
+ ? 2
+ : 10,
),
- ),
- SizedBox(
- height: context.read<ConfigStore>().compactPostView
- ? 2
- : 10),
- ],
+ ],
+ ),
+ ),
+ crossFadeState: hiddenStore.postIds.contains(post.post.id)
+ ? CrossFadeState.showFirst
+ : CrossFadeState.showSecond,
)),
noItems: const Text('there are no posts'),
uniqueProp: (item) => item.post.apId,