diff --git a/lib/app/components/counter_items_footer/replies_counter_button.dart b/lib/app/components/counter_items_footer/replies_counter_button.dart index 54db9f3b4..960cecb95 100644 --- a/lib/app/components/counter_items_footer/replies_counter_button.dart +++ b/lib/app/components/counter_items_footer/replies_counter_button.dart @@ -76,7 +76,7 @@ class RepliesCounterButton extends HookConsumerWidget { if (!context.mounted) return; if (canReply) { - await CreatePostRoute(parentEvent: eventReference.toString()).push(context); + await CreatePostRoute(parentEvent: eventReference.encode()).push(context); await HapticFeedback.lightImpact(); } else { await showSimpleBottomSheet( diff --git a/lib/app/components/counter_items_footer/reposts_counter_button.dart b/lib/app/components/counter_items_footer/reposts_counter_button.dart index 03f39b255..5afacb048 100644 --- a/lib/app/components/counter_items_footer/reposts_counter_button.dart +++ b/lib/app/components/counter_items_footer/reposts_counter_button.dart @@ -30,9 +30,7 @@ class RepostsCounterButton extends ConsumerWidget { return GestureDetector( onTap: () { HapticFeedback.lightImpact(); - RepostOptionsModalRoute( - eventReference: eventReference.toString(), - ).push(context); + RepostOptionsModalRoute(eventReference: eventReference.encode()).push(context); }, child: TextActionButton( icon: Assets.svg.iconBlockRepost.icon( diff --git a/lib/app/components/counter_items_footer/share_button.dart b/lib/app/components/counter_items_footer/share_button.dart index cf0ab33ab..0032b884a 100644 --- a/lib/app/components/counter_items_footer/share_button.dart +++ b/lib/app/components/counter_items_footer/share_button.dart @@ -23,7 +23,7 @@ class ShareButton extends StatelessWidget { return GestureDetector( onTap: () { HapticFeedback.lightImpact(); - SharePostModalRoute(postId: eventReference.eventId).push(context); + SharePostModalRoute(eventReference: eventReference.encode()).push(context); }, child: TextActionButton( icon: Assets.svg.iconBlockShare.icon( diff --git a/lib/app/exceptions/exceptions.dart b/lib/app/exceptions/exceptions.dart index 44e19f6e0..79463282a 100644 --- a/lib/app/exceptions/exceptions.dart +++ b/lib/app/exceptions/exceptions.dart @@ -232,3 +232,8 @@ class DeleteEntityUnsupportedTypeException extends IONException { DeleteEntityUnsupportedTypeException() : super(10048, 'Failed to delete entity, unsupported type'); } + +class UnknownEventReferenceType extends IONException { + UnknownEventReferenceType({required String type}) + : super(10049, 'Unknown event reference type $type'); +} diff --git a/lib/app/features/chat/providers/user_chat_relays_provider.c.dart b/lib/app/features/chat/providers/user_chat_relays_provider.c.dart index e537a84af..d234d5fa3 100644 --- a/lib/app/features/chat/providers/user_chat_relays_provider.c.dart +++ b/lib/app/features/chat/providers/user_chat_relays_provider.c.dart @@ -3,9 +3,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/features/auth/providers/auth_provider.c.dart'; -import 'package:ion/app/features/ion_connect/ion_connect.dart'; -import 'package:ion/app/features/ion_connect/model/action_source.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; +import 'package:ion/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart'; import 'package:ion/app/features/user/model/user_chat_relays.c.dart'; import 'package:ion/app/features/user/model/user_relays.c.dart'; @@ -16,22 +15,11 @@ part 'user_chat_relays_provider.c.g.dart'; @riverpod Future userChatRelays(Ref ref, String pubkey) async { - final cached = ref.watch( - ionConnectCacheProvider.select( - cacheSelector(UserChatRelaysEntity.cacheKeyBuilder(pubkey: pubkey)), - ), - ); - if (cached != null) return cached; - - final requestMessage = RequestMessage() - ..addFilter( - RequestFilter(kinds: const [UserChatRelaysEntity.kind], authors: [pubkey]), - ); - - return ref.watch(ionConnectNotifierProvider.notifier).requestEntity( - requestMessage, - actionSource: ActionSourceUser(pubkey), - ); + return await ref.watch( + ionConnectEntityProvider( + eventReference: ReplaceableEventReference(pubkey: pubkey, kind: UserChatRelaysEntity.kind), + ).future, + ) as UserChatRelaysEntity?; } @riverpod diff --git a/lib/app/features/components/entities_list/components/article_list_item.dart b/lib/app/features/components/entities_list/components/article_list_item.dart index d1fc07abc..40fdb968b 100644 --- a/lib/app/features/components/entities_list/components/article_list_item.dart +++ b/lib/app/features/components/entities_list/components/article_list_item.dart @@ -5,7 +5,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ion/app/extensions/num.dart'; import 'package:ion/app/features/feed/data/models/entities/article_data.c.dart'; import 'package:ion/app/features/feed/views/components/article/article.dart'; -import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/router/app_routes.c.dart'; class ArticleListItem extends ConsumerWidget { @@ -15,14 +14,14 @@ class ArticleListItem extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final eventReference = EventReference.fromIonConnectEntity(article); + final eventReference = article.toEventReference(); return Padding( padding: EdgeInsets.only(top: 12.0.s, bottom: 24.0.s, right: 16.0.s), child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => - ArticleDetailsRoute(eventReference: eventReference.toString()).push(context), + ArticleDetailsRoute(eventReference: eventReference.encode()).push(context), child: Article( eventReference: eventReference, ), diff --git a/lib/app/features/components/entities_list/components/generic_repost_list_item.dart b/lib/app/features/components/entities_list/components/generic_repost_list_item.dart index a75c1aa24..e455f2824 100644 --- a/lib/app/features/components/entities_list/components/generic_repost_list_item.dart +++ b/lib/app/features/components/entities_list/components/generic_repost_list_item.dart @@ -21,11 +21,12 @@ class GenericRepostListItem extends StatelessWidget { return Text('Repost of kind ${repost.data.kind} is not supported'); } - final eventReference = EventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); + //TODO:replaceable - should not be immutable + final eventReference = + ImmutableEventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); return GestureDetector( - onTap: () => - ArticleDetailsRoute(eventReference: eventReference.toString()).push(context), + onTap: () => ArticleDetailsRoute(eventReference: eventReference.encode()).push(context), behavior: HitTestBehavior.opaque, child: ScreenSideOffset.small( child: Column( diff --git a/lib/app/features/components/entities_list/components/post_list_item.dart b/lib/app/features/components/entities_list/components/post_list_item.dart index f9e3d09fa..ac207b852 100644 --- a/lib/app/features/components/entities_list/components/post_list_item.dart +++ b/lib/app/features/components/entities_list/components/post_list_item.dart @@ -4,7 +4,6 @@ import 'package:flutter/widgets.dart'; import 'package:ion/app/components/screen_offset/screen_side_offset.dart'; import 'package:ion/app/features/feed/data/models/entities/post_data.c.dart'; import 'package:ion/app/features/feed/views/components/post/post.dart'; -import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/router/app_routes.c.dart'; class PostListItem extends StatelessWidget { @@ -16,11 +15,11 @@ class PostListItem extends StatelessWidget { @override Widget build(BuildContext context) { - final eventReference = EventReference.fromIonConnectEntity(post); + final eventReference = post.toEventReference(); // TODO: process 20002 in the feed provider to fetch 10002 return GestureDetector( - onTap: () => PostDetailsRoute(eventReference: eventReference.toString()).push(context), + onTap: () => PostDetailsRoute(eventReference: eventReference.encode()).push(context), behavior: HitTestBehavior.opaque, child: ScreenSideOffset.small( child: Post(eventReference: eventReference, showParent: showParent), diff --git a/lib/app/features/components/entities_list/components/repost_list_item.dart b/lib/app/features/components/entities_list/components/repost_list_item.dart index a96f4a635..2070049b3 100644 --- a/lib/app/features/components/entities_list/components/repost_list_item.dart +++ b/lib/app/features/components/entities_list/components/repost_list_item.dart @@ -15,10 +15,11 @@ class RepostListItem extends StatelessWidget { @override Widget build(BuildContext context) { - final eventReference = EventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); + final eventReference = + ImmutableEventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); return GestureDetector( - onTap: () => PostDetailsRoute(eventReference: eventReference.toString()).push(context), + onTap: () => PostDetailsRoute(eventReference: eventReference.encode()).push(context), behavior: HitTestBehavior.opaque, child: ScreenSideOffset.small( child: Column( diff --git a/lib/app/features/components/entities_list/entities_list.dart b/lib/app/features/components/entities_list/entities_list.dart index 3a9f26eae..6aa290154 100644 --- a/lib/app/features/components/entities_list/entities_list.dart +++ b/lib/app/features/components/entities_list/entities_list.dart @@ -61,7 +61,7 @@ class _EntityListItem extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final userMetadata = - ref.watch(userMetadataProvider(entity.masterPubkey, cacheOnly: true)).valueOrNull; + ref.watch(userMetadataProvider(entity.masterPubkey, network: false)).valueOrNull; final isBlockedOrBlocking = ref.watch(isEntityBlockedOrBlockingProvider(entity, cacheOnly: true)).valueOrNull ?? true; diff --git a/lib/app/features/feed/create_post/providers/create_post_notifier.c.dart b/lib/app/features/feed/create_post/providers/create_post_notifier.c.dart index ab05e8e78..809cb0ae6 100644 --- a/lib/app/features/feed/create_post/providers/create_post_notifier.c.dart +++ b/lib/app/features/feed/create_post/providers/create_post_notifier.c.dart @@ -82,13 +82,13 @@ class CreatePostNotifier extends _$CreatePostNotifier { ); } - if (quotedEvent != null) { + if (quotedEvent != null && quotedEvent is ImmutableEventReference) { data = data.copyWith( quotedEvent: QuotedEvent(eventId: quotedEvent.eventId, pubkey: quotedEvent.pubkey), ); } - if (parentEvent != null) { + if (parentEvent != null && parentEvent is ImmutableEventReference) { final parentEntity = await ref.read(ionConnectEntityProvider(eventReference: parentEvent).future); if (parentEntity == null) { diff --git a/lib/app/features/feed/create_post/views/components/reply_input_field/reply_input_field.dart b/lib/app/features/feed/create_post/views/components/reply_input_field/reply_input_field.dart index c28efc12a..aaa54609f 100644 --- a/lib/app/features/feed/create_post/views/components/reply_input_field/reply_input_field.dart +++ b/lib/app/features/feed/create_post/views/components/reply_input_field/reply_input_field.dart @@ -101,7 +101,7 @@ class ReplyInputField extends HookConsumerWidget { GestureDetector( onTap: () async { final content = await CreatePostRoute( - parentEvent: eventReference.toString(), + parentEvent: eventReference.encode(), showCollapseButton: true, content: textEditorController.document.toPlainText().trim(), ).push(context); diff --git a/lib/app/features/feed/data/models/bookmarks/bookmarks.c.dart b/lib/app/features/feed/data/models/bookmarks/bookmarks.c.dart index 92f9cba7d..37a132e4a 100644 --- a/lib/app/features/feed/data/models/bookmarks/bookmarks.c.dart +++ b/lib/app/features/feed/data/models/bookmarks/bookmarks.c.dart @@ -7,15 +7,17 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; part 'bookmarks.c.freezed.dart'; @Freezed(equal: false) -class BookmarksEntity with _$BookmarksEntity, IonConnectEntity implements CacheableEntity { +class BookmarksEntity + with _$BookmarksEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory BookmarksEntity({ required String id, required String pubkey, @@ -44,15 +46,15 @@ class BookmarksEntity with _$BookmarksEntity, IonConnectEntity implements Cachea } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); - - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 10003; } @freezed -class BookmarksData with _$BookmarksData implements EventSerializable { +class BookmarksData with _$BookmarksData implements EventSerializable, ReplaceableEntityData { const factory BookmarksData({ required List ids, required List bookmarksSetRefs, @@ -89,4 +91,12 @@ class BookmarksData with _$BookmarksData implements EventSerializable { content: '', ); } + + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: BookmarksEntity.kind, + pubkey: pubkey, + ); + } } diff --git a/lib/app/features/feed/data/models/bookmarks/bookmarks_set.c.dart b/lib/app/features/feed/data/models/bookmarks/bookmarks_set.c.dart index 2947c18c7..a38d844a9 100644 --- a/lib/app/features/feed/data/models/bookmarks/bookmarks_set.c.dart +++ b/lib/app/features/feed/data/models/bookmarks/bookmarks_set.c.dart @@ -7,10 +7,10 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/model/replaceable_event_identifier.c.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; part 'bookmarks_set.c.freezed.dart'; @@ -28,7 +28,9 @@ enum BookmarksSetType { } @Freezed(equal: false) -class BookmarksSetEntity with _$BookmarksSetEntity, IonConnectEntity implements CacheableEntity { +class BookmarksSetEntity + with _$BookmarksSetEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory BookmarksSetEntity({ required String id, required String pubkey, @@ -57,16 +59,15 @@ class BookmarksSetEntity with _$BookmarksSetEntity, IonConnectEntity implements } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey, type: data.type); - - static String cacheKeyBuilder({required String pubkey, required BookmarksSetType type}) => - '$kind:$type:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 30003; } @freezed -class BookmarksSetData with _$BookmarksSetData implements EventSerializable { +class BookmarksSetData with _$BookmarksSetData implements EventSerializable, ReplaceableEntityData { const factory BookmarksSetData({ required BookmarksSetType type, required List postsIds, @@ -121,6 +122,7 @@ class BookmarksSetData with _$BookmarksSetData implements EventSerializable { ); } + @override ReplaceableEventReference toReplaceableEventReference(String pubkey) { return ReplaceableEventReference( pubkey: pubkey, diff --git a/lib/app/features/feed/data/models/entities/article_data.c.dart b/lib/app/features/feed/data/models/entities/article_data.c.dart index 84636e8e7..d6beaa115 100644 --- a/lib/app/features/feed/data/models/entities/article_data.c.dart +++ b/lib/app/features/feed/data/models/entities/article_data.c.dart @@ -10,13 +10,13 @@ import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/feed/data/models/who_can_reply_settings_option.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; import 'package:ion/app/features/ion_connect/model/entity_published_at.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/event_setting.c.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/model/media_attachment.dart'; import 'package:ion/app/features/ion_connect/model/related_hashtag.c.dart'; import 'package:ion/app/features/ion_connect/model/replaceable_event_identifier.c.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; part 'article_data.c.freezed.dart'; @@ -24,7 +24,9 @@ part 'article_data.c.freezed.dart'; const textEditorSingleImageKey = 'text-editor-single-image'; @Freezed(equal: false) -class ArticleEntity with _$ArticleEntity, IonConnectEntity implements CacheableEntity { +class ArticleEntity + with _$ArticleEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory ArticleEntity({ required String id, required String pubkey, @@ -52,23 +54,15 @@ class ArticleEntity with _$ArticleEntity, IonConnectEntity implements CacheableE } @override - String get cacheKey => cacheKeyBuilder(id: id); - - static String cacheKeyBuilder({required String id}) => id; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const kind = 30023; - - ReplaceableEventReference toReplaceableEventReference() { - return ReplaceableEventReference( - kind: kind, - pubkey: masterPubkey, - dTag: id, - ); - } } @freezed -class ArticleData with _$ArticleData implements EventSerializable { +class ArticleData with _$ArticleData implements EventSerializable, ReplaceableEntityData { const factory ArticleData({ required String content, required Map media, @@ -160,6 +154,15 @@ class ArticleData with _$ArticleData implements EventSerializable { ); } + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: ArticleEntity.kind, + pubkey: pubkey, + dTag: replaceableEventId.value, + ); + } + static List extractHashtagsFromMarkdown(String content) { final operations = jsonDecode(content) as List; const insertKey = 'insert'; diff --git a/lib/app/features/feed/data/models/entities/event_count_request_data.c.dart b/lib/app/features/feed/data/models/entities/event_count_request_data.c.dart index 2ba05a5c9..764c7c160 100644 --- a/lib/app/features/feed/data/models/entities/event_count_request_data.c.dart +++ b/lib/app/features/feed/data/models/entities/event_count_request_data.c.dart @@ -16,8 +16,7 @@ part 'event_count_request_data.c.freezed.dart'; @Freezed(equal: false) class EventCountRequestEntity - with _$EventCountRequestEntity, IonConnectEntity - implements CacheableEntity { + with _$EventCountRequestEntity, IonConnectEntity, ImmutableEntity, CacheableEntity { const factory EventCountRequestEntity({ required String id, required String pubkey, @@ -45,11 +44,6 @@ class EventCountRequestEntity ); } - @override - String get cacheKey => cacheKeyBuilder(id: id); - - static String cacheKeyBuilder({required String id}) => id; - static const int kind = 5400; } diff --git a/lib/app/features/feed/data/models/entities/event_count_result_data.c.dart b/lib/app/features/feed/data/models/entities/event_count_result_data.c.dart index 7a5b7a539..79ecff7b9 100644 --- a/lib/app/features/feed/data/models/entities/event_count_result_data.c.dart +++ b/lib/app/features/feed/data/models/entities/event_count_result_data.c.dart @@ -27,8 +27,7 @@ enum EventCountResultType { @Freezed(equal: false) class EventCountResultEntity - with _$EventCountResultEntity, IonConnectEntity - implements CacheableEntity { + with _$EventCountResultEntity, IonConnectEntity, ImmutableEntity, CacheableEntity { const factory EventCountResultEntity({ required String id, required String pubkey, diff --git a/lib/app/features/feed/data/models/entities/modifiable_post_data.c.dart b/lib/app/features/feed/data/models/entities/modifiable_post_data.c.dart index fc830b07b..1e59a8960 100644 --- a/lib/app/features/feed/data/models/entities/modifiable_post_data.c.dart +++ b/lib/app/features/feed/data/models/entities/modifiable_post_data.c.dart @@ -12,11 +12,12 @@ import 'package:ion/app/features/ion_connect/model/entity_editing_ended_at.c.dar import 'package:ion/app/features/ion_connect/model/entity_expiration.c.dart'; import 'package:ion/app/features/ion_connect/model/entity_media_data.dart'; import 'package:ion/app/features/ion_connect/model/entity_published_at.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/event_setting.c.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/model/media_attachment.dart'; -import 'package:ion/app/features/ion_connect/model/quoted_modifiable_event.c.dart'; +import 'package:ion/app/features/ion_connect/model/quoted_replaceable_event.c.dart'; import 'package:ion/app/features/ion_connect/model/related_hashtag.c.dart'; import 'package:ion/app/features/ion_connect/model/related_pubkey.c.dart'; import 'package:ion/app/features/ion_connect/model/related_replaceable_event.c.dart'; @@ -30,8 +31,8 @@ part 'modifiable_post_data.c.freezed.dart'; @Freezed(equal: false) class ModifiablePostEntity - with _$ModifiablePostEntity, IonConnectEntity - implements CacheableEntity { + with _$ModifiablePostEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory ModifiablePostEntity({ required String id, required String pubkey, @@ -60,9 +61,9 @@ class ModifiablePostEntity } @override - String get cacheKey => cacheKeyBuilder(replaceableEventId: data.replaceableEventId.value); - - static String cacheKeyBuilder({required String replaceableEventId}) => replaceableEventId; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const kind = 30175; } @@ -70,7 +71,7 @@ class ModifiablePostEntity @freezed class ModifiablePostData with _$ModifiablePostData, EntityMediaDataMixin - implements EventSerializable { + implements EventSerializable, ReplaceableEntityData { const factory ModifiablePostData({ required List content, required Map media, @@ -78,7 +79,7 @@ class ModifiablePostData required EntityPublishedAt publishedAt, EntityEditingEndedAt? editingEndedAt, EntityExpiration? expiration, - QuotedModifiableEvent? quotedEvent, + QuotedReplaceableEvent? quotedEvent, List? relatedEvents, List? relatedPubkeys, List? relatedHashtags, @@ -102,8 +103,8 @@ class ModifiablePostData expiration: tags[EntityExpiration.tagName] != null ? EntityExpiration.fromTag(tags[EntityExpiration.tagName]!.first) : null, - quotedEvent: tags[QuotedModifiableEvent.tagName] != null - ? QuotedModifiableEvent.fromTag(tags[QuotedModifiableEvent.tagName]!.first) + quotedEvent: tags[QuotedReplaceableEvent.tagName] != null + ? QuotedReplaceableEvent.fromTag(tags[QuotedReplaceableEvent.tagName]!.first) : null, relatedEvents: tags[RelatedReplaceableEvent.tagName]?.map(RelatedReplaceableEvent.fromTag).toList(), @@ -159,6 +160,15 @@ class ModifiablePostData ); } + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: ModifiablePostEntity.kind, + pubkey: pubkey, + dTag: replaceableEventId.value, + ); + } + RelatedReplaceableEvent? get parentEvent { if (relatedEvents == null) return null; diff --git a/lib/app/features/feed/data/models/entities/post_data.c.dart b/lib/app/features/feed/data/models/entities/post_data.c.dart index 2a90775d9..027fcbd54 100644 --- a/lib/app/features/feed/data/models/entities/post_data.c.dart +++ b/lib/app/features/feed/data/models/entities/post_data.c.dart @@ -26,7 +26,7 @@ import 'package:ion/app/services/text_parser/text_parser.dart'; part 'post_data.c.freezed.dart'; @Freezed(equal: false) -class PostEntity with _$PostEntity, IonConnectEntity implements CacheableEntity { +class PostEntity with _$PostEntity, IonConnectEntity, ImmutableEntity, CacheableEntity { const factory PostEntity({ required String id, required String pubkey, @@ -54,11 +54,6 @@ class PostEntity with _$PostEntity, IonConnectEntity implements CacheableEntity ); } - @override - String get cacheKey => cacheKeyBuilder(id: id); - - static String cacheKeyBuilder({required String id}) => id; - static const kind = 1; } diff --git a/lib/app/features/feed/data/models/entities/reaction_data.c.dart b/lib/app/features/feed/data/models/entities/reaction_data.c.dart index 686db6136..46100665e 100644 --- a/lib/app/features/feed/data/models/entities/reaction_data.c.dart +++ b/lib/app/features/feed/data/models/entities/reaction_data.c.dart @@ -14,7 +14,7 @@ import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart' part 'reaction_data.c.freezed.dart'; @Freezed(equal: false) -class ReactionEntity with _$ReactionEntity, IonConnectEntity implements CacheableEntity { +class ReactionEntity with _$ReactionEntity, IonConnectEntity, ImmutableEntity, CacheableEntity { const factory ReactionEntity({ required String id, required String pubkey, diff --git a/lib/app/features/feed/data/models/entities/repost_data.c.dart b/lib/app/features/feed/data/models/entities/repost_data.c.dart index b9b73b797..87a7b347e 100644 --- a/lib/app/features/feed/data/models/entities/repost_data.c.dart +++ b/lib/app/features/feed/data/models/entities/repost_data.c.dart @@ -14,7 +14,7 @@ import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart' part 'repost_data.c.freezed.dart'; @Freezed(equal: false) -class RepostEntity with _$RepostEntity, IonConnectEntity implements CacheableEntity { +class RepostEntity with _$RepostEntity, IonConnectEntity, ImmutableEntity, CacheableEntity { const factory RepostEntity({ required String id, required String pubkey, @@ -42,11 +42,6 @@ class RepostEntity with _$RepostEntity, IonConnectEntity implements CacheableEnt ); } - @override - String get cacheKey => cacheKeyBuilder(id: id); - - static String cacheKeyBuilder({required String id}) => id; - static const int kind = 6; } diff --git a/lib/app/features/feed/data/models/generic_repost.c.dart b/lib/app/features/feed/data/models/generic_repost.c.dart index a29c9b432..926c3b7df 100644 --- a/lib/app/features/feed/data/models/generic_repost.c.dart +++ b/lib/app/features/feed/data/models/generic_repost.c.dart @@ -14,7 +14,8 @@ import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart' part 'generic_repost.c.freezed.dart'; @Freezed(equal: false) -class GenericRepostEntity with _$GenericRepostEntity, IonConnectEntity implements CacheableEntity { +class GenericRepostEntity + with _$GenericRepostEntity, IonConnectEntity, ImmutableEntity, CacheableEntity { const factory GenericRepostEntity({ required String id, required String pubkey, @@ -42,11 +43,6 @@ class GenericRepostEntity with _$GenericRepostEntity, IonConnectEntity implement ); } - @override - String get cacheKey => cacheKeyBuilder(id: id); - - static String cacheKeyBuilder({required String id}) => id; - static const int kind = 16; } diff --git a/lib/app/features/feed/providers/bookmarks_notifier.c.dart b/lib/app/features/feed/providers/bookmarks_notifier.c.dart index d39789bbe..804a1b8da 100644 --- a/lib/app/features/feed/providers/bookmarks_notifier.c.dart +++ b/lib/app/features/feed/providers/bookmarks_notifier.c.dart @@ -12,7 +12,6 @@ import 'package:ion/app/features/ion_connect/ion_connect.dart'; import 'package:ion/app/features/ion_connect/model/action_source.dart'; import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart'; @@ -27,7 +26,13 @@ Future> bookmarks( ) async { final bookmarksMap = Map.fromEntries( BookmarksSetType.values.map((type) { - final cacheKey = BookmarksSetEntity.cacheKeyBuilder(pubkey: pubkey, type: type); + final cacheKey = CacheableEntity.cacheKeyBuilder( + eventReference: ReplaceableEventReference( + pubkey: pubkey, + kind: BookmarksSetEntity.kind, + dTag: type.dTagName, + ), + ); final bookmarkSet = ref.watch( ionConnectCacheProvider.select(cacheSelector(cacheKey)), ); @@ -88,7 +93,7 @@ Future isBookmarked(Ref ref, EventReference eventReference) async { ArticleEntity() => currentBookmarks[BookmarksSetType.articles] ?.data .articlesRefs - .contains(ionConnectEntity.toReplaceableEventReference()) ?? + .contains(ionConnectEntity.toEventReference()) ?? false, _ => false, }; @@ -171,7 +176,7 @@ class BookmarksNotifier extends _$BookmarksNotifier { } void _toggleArticleBookmark(Set articlesRefs, ArticleEntity article) { - final articleRef = article.toReplaceableEventReference(); + final articleRef = article.toEventReference(); if (articlesRefs.contains(articleRef)) { articlesRefs.remove(articleRef); } else { diff --git a/lib/app/features/feed/providers/can_reply_notifier.c.dart b/lib/app/features/feed/providers/can_reply_notifier.c.dart index bcdcf037c..65dfbb928 100644 --- a/lib/app/features/feed/providers/can_reply_notifier.c.dart +++ b/lib/app/features/feed/providers/can_reply_notifier.c.dart @@ -23,9 +23,9 @@ class CanReply extends _$CanReply { Future build(EventReference eventReference) async { ref.onDispose(() { ref - ..invalidate(followListProvider(eventReference.pubkey, skipCache: true)) + ..invalidate(followListProvider(eventReference.pubkey, cache: false)) ..invalidate( - ionConnectEntityProvider(eventReference: eventReference, skipCache: true), + ionConnectEntityProvider(eventReference: eventReference, cache: false), ); }); @@ -35,7 +35,7 @@ class CanReply extends _$CanReply { } final ionConnectEntity = await ref.watch( - ionConnectEntityProvider(eventReference: eventReference, skipCache: _skipCache).future, + ionConnectEntityProvider(eventReference: eventReference, cache: !_skipCache).future, ); if (ionConnectEntity == null) { return true; @@ -60,7 +60,7 @@ class CanReply extends _$CanReply { return true; case WhoCanReplySettingsOption.followedAccounts: final followers = - await ref.watch(followListProvider(authorPubkey, skipCache: _skipCache).future); + await ref.watch(followListProvider(authorPubkey, cache: !_skipCache).future); if (followers == null) { return false; } diff --git a/lib/app/features/feed/providers/counters/like_reaction_provider.c.dart b/lib/app/features/feed/providers/counters/like_reaction_provider.c.dart index 851430c12..a7b0d5660 100644 --- a/lib/app/features/feed/providers/counters/like_reaction_provider.c.dart +++ b/lib/app/features/feed/providers/counters/like_reaction_provider.c.dart @@ -17,6 +17,11 @@ ReactionEntity? likeReaction(Ref ref, EventReference eventReference) { return null; } + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + final reactionEntity = ref.watch( ionConnectCacheProvider.select( cacheSelector( diff --git a/lib/app/features/feed/providers/counters/likes_count_provider.c.dart b/lib/app/features/feed/providers/counters/likes_count_provider.c.dart index 375f397f4..8c508bb1f 100644 --- a/lib/app/features/feed/providers/counters/likes_count_provider.c.dart +++ b/lib/app/features/feed/providers/counters/likes_count_provider.c.dart @@ -12,6 +12,11 @@ part 'likes_count_provider.c.g.dart'; class LikesCount extends _$LikesCount { @override int build(EventReference eventReference) { + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + final reactionsCountEntity = ref.watch( ionConnectCacheProvider.select( cacheSelector( diff --git a/lib/app/features/feed/providers/counters/likes_notifier.c.dart b/lib/app/features/feed/providers/counters/likes_notifier.c.dart index e7242fd56..01a538062 100644 --- a/lib/app/features/feed/providers/counters/likes_notifier.c.dart +++ b/lib/app/features/feed/providers/counters/likes_notifier.c.dart @@ -24,6 +24,12 @@ class LikesNotifier extends _$LikesNotifier { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { + final reference = eventReference; + if (reference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + final likeEntity = ref.read(likeReactionProvider(eventReference)); if (likeEntity != null) { @@ -36,7 +42,7 @@ class LikesNotifier extends _$LikesNotifier { } else { final data = ReactionData( content: ReactionEntity.likeSymbol, - eventId: eventReference.eventId, + eventId: reference.eventId, pubkey: eventReference.pubkey, ); await ref.read(ionConnectNotifierProvider.notifier).sendEntityData(data); diff --git a/lib/app/features/feed/providers/counters/replied_events_provider.c.dart b/lib/app/features/feed/providers/counters/replied_events_provider.c.dart index 42d2c3da9..5da2fd0fa 100644 --- a/lib/app/features/feed/providers/counters/replied_events_provider.c.dart +++ b/lib/app/features/feed/providers/counters/replied_events_provider.c.dart @@ -103,6 +103,11 @@ List? _getCurrentUserRepliedIds(IonConnectEntity entity, {required Strin @riverpod bool isReplied(Ref ref, EventReference eventReference) { + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + final repliedMap = ref.watch(repliedEventsProvider).valueOrNull; final replyIds = repliedMap?[eventReference.eventId]; diff --git a/lib/app/features/feed/providers/counters/replies_count_provider.c.dart b/lib/app/features/feed/providers/counters/replies_count_provider.c.dart index 45006bbd1..f6b1498e9 100644 --- a/lib/app/features/feed/providers/counters/replies_count_provider.c.dart +++ b/lib/app/features/feed/providers/counters/replies_count_provider.c.dart @@ -11,6 +11,11 @@ part 'replies_count_provider.c.g.dart'; class RepliesCount extends _$RepliesCount { @override int build(EventReference eventReference) { + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + final cacheCount = ref .watch( ionConnectCacheProvider.select( diff --git a/lib/app/features/feed/providers/counters/reposted_events_provider.c.dart b/lib/app/features/feed/providers/counters/reposted_events_provider.c.dart index 1fbb0402d..b20a25709 100644 --- a/lib/app/features/feed/providers/counters/reposted_events_provider.c.dart +++ b/lib/app/features/feed/providers/counters/reposted_events_provider.c.dart @@ -57,5 +57,9 @@ String? _getCurrentUserRepostedId(IonConnectEntity entity, {required String curr @riverpod bool isReposted(Ref ref, EventReference eventReference) { + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } return ref.watch(repostedEventsProvider).valueOrNull?.contains(eventReference.eventId) ?? false; } diff --git a/lib/app/features/feed/providers/counters/reposts_count_provider.c.dart b/lib/app/features/feed/providers/counters/reposts_count_provider.c.dart index e2d0ae428..8ce797af6 100644 --- a/lib/app/features/feed/providers/counters/reposts_count_provider.c.dart +++ b/lib/app/features/feed/providers/counters/reposts_count_provider.c.dart @@ -11,6 +11,11 @@ part 'reposts_count_provider.c.g.dart'; class RepostsCount extends _$RepostsCount { @override int? build(EventReference eventReference) { + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + final repostsCountEntity = ref.watch( ionConnectCacheProvider.select( cacheSelector( diff --git a/lib/app/features/feed/providers/delete_entity_provider.c.dart b/lib/app/features/feed/providers/delete_entity_provider.c.dart index 5864a7037..5361ef613 100644 --- a/lib/app/features/feed/providers/delete_entity_provider.c.dart +++ b/lib/app/features/feed/providers/delete_entity_provider.c.dart @@ -65,7 +65,7 @@ Future _deleteReply(Ref ref, PostEntity post) async { ref .read( repliesCountProvider( - EventReference( + ImmutableEventReference( eventId: parentId, pubkey: post.data.parentEvent!.pubkey, ), @@ -76,7 +76,7 @@ Future _deleteReply(Ref ref, PostEntity post) async { await ref .read( repliesProvider( - EventReference( + ImmutableEventReference( eventId: parentId, pubkey: post.data.parentEvent!.pubkey, ), diff --git a/lib/app/features/feed/providers/replies_data_source_provider.c.dart b/lib/app/features/feed/providers/replies_data_source_provider.c.dart index 09d08eca7..25d5a8f3b 100644 --- a/lib/app/features/feed/providers/replies_data_source_provider.c.dart +++ b/lib/app/features/feed/providers/replies_data_source_provider.c.dart @@ -30,6 +30,11 @@ List? repliesDataSource( return null; } + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + if (entity is! PostEntity) { throw IncorrectEventKindException(eventId: eventReference.eventId, kind: PostEntity.kind); } diff --git a/lib/app/features/feed/providers/replies_provider.c.dart b/lib/app/features/feed/providers/replies_provider.c.dart index dfd76615e..daf42e56b 100644 --- a/lib/app/features/feed/providers/replies_provider.c.dart +++ b/lib/app/features/feed/providers/replies_provider.c.dart @@ -34,6 +34,10 @@ class Replies extends _$Replies { } bool _isReply(IonConnectEntity entity, EventReference parentEventReference) { + if (parentEventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } return entity is PostEntity && entity.data.parentEvent?.eventId == parentEventReference.eventId; } diff --git a/lib/app/features/feed/providers/repost_entity_provider.c.dart b/lib/app/features/feed/providers/repost_entity_provider.c.dart index ced29500a..330ff2625 100644 --- a/lib/app/features/feed/providers/repost_entity_provider.c.dart +++ b/lib/app/features/feed/providers/repost_entity_provider.c.dart @@ -11,6 +11,11 @@ part 'repost_entity_provider.c.g.dart'; @riverpod CacheableEntity? repostEntity(Ref ref, EventReference eventReference) { + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + final cache = ref.read(ionConnectCacheProvider); return cache.values.firstWhereOrNull( diff --git a/lib/app/features/feed/providers/repost_notifier.c.dart b/lib/app/features/feed/providers/repost_notifier.c.dart index 456f4dc8c..e059ebc14 100644 --- a/lib/app/features/feed/providers/repost_notifier.c.dart +++ b/lib/app/features/feed/providers/repost_notifier.c.dart @@ -26,6 +26,11 @@ class RepostNotifier extends _$RepostNotifier { state = await AsyncValue.guard(() async { final entity = ref.read(ionConnectEntityProvider(eventReference: eventReference)).valueOrNull; + if (eventReference is! ImmutableEventReference) { + //TODO:replaceable handle replaceable references + throw UnimplementedError(); + } + if (entity == null) { throw EntityNotFoundException(eventReference.eventId); } diff --git a/lib/app/features/feed/stories/views/components/story_viewer/components/core/story_content.dart b/lib/app/features/feed/stories/views/components/story_viewer/components/core/story_content.dart index b58fbd6d2..9a7b798ce 100644 --- a/lib/app/features/feed/stories/views/components/story_viewer/components/core/story_content.dart +++ b/lib/app/features/feed/stories/views/components/story_viewer/components/core/story_content.dart @@ -11,7 +11,6 @@ import 'package:ion/app/features/feed/stories/providers/emoji_reaction_provider. import 'package:ion/app/features/feed/stories/providers/story_pause_provider.c.dart'; import 'package:ion/app/features/feed/stories/views/components/story_viewer/components/components.dart'; import 'package:ion/app/features/feed/stories/views/components/story_viewer/components/header/header.dart'; -import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/hooks/use_on_init.dart'; class StoryContent extends HookConsumerWidget { @@ -27,8 +26,7 @@ class StoryContent extends HookConsumerWidget { final emojiState = ref.watch(emojiReactionsControllerProvider); final textController = useTextEditingController(); final isKeyboardVisible = KeyboardVisibilityProvider.isKeyboardVisible(context); - final postReference = EventReference.fromIonConnectEntity(post); - final canReply = ref.watch(canReplyProvider(postReference)).valueOrNull ?? false; + final canReply = ref.watch(canReplyProvider(post.toEventReference())).valueOrNull ?? false; useOnInit( () => ref.read(storyPauseControllerProvider.notifier).paused = isKeyboardVisible, diff --git a/lib/app/features/feed/views/components/article/widgetbook.dart b/lib/app/features/feed/views/components/article/widgetbook.dart index 3589c9115..29aad2118 100644 --- a/lib/app/features/feed/views/components/article/widgetbook.dart +++ b/lib/app/features/feed/views/components/article/widgetbook.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:ion/app/features/feed/data/models/entities/article_data.c.dart'; import 'package:ion/app/features/feed/views/components/article/article.dart'; import 'package:ion/app/features/feed/views/components/article/mocked_data.dart'; -import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; @widgetbook.UseCase( @@ -13,12 +12,11 @@ import 'package:widgetbook_annotation/widgetbook_annotation.dart' as widgetbook; ) Widget feedPostUseCase(BuildContext context) { final article = ArticleEntity.fromEventMessage(mockedArticleEvents[0]); - final eventReference = EventReference.fromIonConnectEntity(article); return Scaffold( body: Center( child: Article( - eventReference: eventReference, + eventReference: article.toEventReference(), ), ), ); diff --git a/lib/app/features/feed/views/components/post/post.dart b/lib/app/features/feed/views/components/post/post.dart index e584933e8..7d15e404d 100644 --- a/lib/app/features/feed/views/components/post/post.dart +++ b/lib/app/features/feed/views/components/post/post.dart @@ -79,12 +79,12 @@ class Post extends ConsumerWidget { if (showParent) { final parentEvent = postEntity.data.parentEvent; if (parentEvent != null) { - return EventReference(eventId: parentEvent.eventId, pubkey: parentEvent.pubkey); + return ImmutableEventReference(eventId: parentEvent.eventId, pubkey: parentEvent.pubkey); } } else { final quotedEvent = postEntity.data.quotedEvent; if (quotedEvent != null) { - return EventReference(eventId: quotedEvent.eventId, pubkey: quotedEvent.pubkey); + return ImmutableEventReference(eventId: quotedEvent.eventId, pubkey: quotedEvent.pubkey); } } return null; @@ -132,7 +132,7 @@ final class _QuotedPost extends ConsumerWidget { return QuotedEntityFrame.post( child: GestureDetector( onTap: () { - PostDetailsRoute(eventReference: eventReference.toString()).push(context); + PostDetailsRoute(eventReference: eventReference.encode()).push(context); }, child: AbsorbPointer( child: Post( @@ -156,7 +156,7 @@ final class _QuotedArticle extends ConsumerWidget { return QuotedEntityFrame.article( child: GestureDetector( onTap: () { - ArticleDetailsRoute(eventReference: eventReference.toString()).push(context); + ArticleDetailsRoute(eventReference: eventReference.encode()).push(context); }, child: AbsorbPointer(child: Article.quoted(eventReference: eventReference)), ), diff --git a/lib/app/features/feed/views/components/post/widgetbook.dart b/lib/app/features/feed/views/components/post/widgetbook.dart index e804bedb6..4d6b0db4f 100644 --- a/lib/app/features/feed/views/components/post/widgetbook.dart +++ b/lib/app/features/feed/views/components/post/widgetbook.dart @@ -16,19 +16,19 @@ Widget feedPostUseCase(BuildContext context) { mainAxisAlignment: MainAxisAlignment.center, children: [ Post( - eventReference: EventReference( + eventReference: ImmutableEventReference( eventId: 'ccfac3cb94d5d1190d5129c38e11ba6c02e76352fd884a3dd9357360b74d3cef', pubkey: '32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245', ), ), Post( - eventReference: EventReference( + eventReference: ImmutableEventReference( eventId: 'c429025124ee136c1bcb11fcd2ff905c84a0f946759cc39f336c4b296db7a4ba', pubkey: 'd3f94b353542a632962062f3c914638d0deeba64af1f980d93907ee1b3e0d4f9', ), ), Post( - eventReference: EventReference( + eventReference: ImmutableEventReference( eventId: 'ef5b8e58a3edfe759250927e1c16e22a06cc32c8794f66882a4c0fea01038d2b', pubkey: '19e3eb646d228812b1cff08c505ea2ee5a85d34c567e42f47b3b32658e377fe1', ), diff --git a/lib/app/features/feed/views/components/user_info_menu/user_info_menu.dart b/lib/app/features/feed/views/components/user_info_menu/user_info_menu.dart index ec7ffb015..12f3f2e9b 100644 --- a/lib/app/features/feed/views/components/user_info_menu/user_info_menu.dart +++ b/lib/app/features/feed/views/components/user_info_menu/user_info_menu.dart @@ -10,11 +10,10 @@ import 'package:ion/app/extensions/num.dart'; import 'package:ion/app/extensions/theme_data.dart'; import 'package:ion/app/features/core/views/pages/unfollow_user_page.dart'; import 'package:ion/app/features/feed/views/components/user_info_menu/user_info_menu_item.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; -import 'package:ion/app/features/user/model/user_metadata.c.dart'; import 'package:ion/app/features/user/pages/profile_page/pages/block_user_modal/block_user_modal.dart'; import 'package:ion/app/features/user/providers/block_list_notifier.c.dart'; import 'package:ion/app/features/user/providers/follow_list_provider.c.dart'; +import 'package:ion/app/features/user/providers/user_metadata_provider.c.dart'; import 'package:ion/app/router/utils/show_simple_bottom_sheet.dart'; import 'package:ion/generated/assets.gen.dart'; @@ -32,11 +31,7 @@ class UserInfoMenu extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final userMetadata = ref.watch( - ionConnectCacheProvider.select( - cacheSelector(UserMetadataEntity.cacheKeyBuilder(pubkey: pubkey)), - ), - ); + final userMetadata = ref.watch(userMetadataProvider(pubkey, network: false)).valueOrNull; if (userMetadata == null) { return const SizedBox.shrink(); diff --git a/lib/app/features/feed/views/pages/feed_page/components/stories/components/story_list_item.dart b/lib/app/features/feed/views/pages/feed_page/components/stories/components/story_list_item.dart index c5031693f..dbb942a96 100644 --- a/lib/app/features/feed/views/pages/feed_page/components/stories/components/story_list_item.dart +++ b/lib/app/features/feed/views/pages/feed_page/components/stories/components/story_list_item.dart @@ -28,7 +28,7 @@ class StoryListItem extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final viewed = useState(false); - final userMetadataAsync = ref.watch(userMetadataProvider(pubkey, cacheOnly: true)); + final userMetadataAsync = ref.watch(userMetadataProvider(pubkey, network: false)); return userMetadataAsync.maybeWhen( data: (userMetadata) { diff --git a/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_video_author.dart b/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_video_author.dart index 52f6e6c38..0a5d2192f 100644 --- a/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_video_author.dart +++ b/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_video_author.dart @@ -18,7 +18,7 @@ class TrendingVideoAuthor extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final userMetadata = ref.watch(userMetadataProvider(pubkey, cacheOnly: true)).valueOrNull; + final userMetadata = ref.watch(userMetadataProvider(pubkey, network: false)).valueOrNull; if (userMetadata == null) { return const SizedBox.shrink(); diff --git a/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_videos_list_item.dart b/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_videos_list_item.dart index 6ac0585e0..a8ac91d23 100644 --- a/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_videos_list_item.dart +++ b/lib/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_videos_list_item.dart @@ -23,7 +23,7 @@ class TrendingVideoListItem extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final eventReference = EventReference.fromIonConnectEntity(video); + final eventReference = video.toEventReference(); final thumbnailUrl = video.data.primaryVideo?.thumb; if (thumbnailUrl == null || thumbnailUrl.isEmpty) { @@ -32,7 +32,7 @@ class TrendingVideoListItem extends ConsumerWidget { return GestureDetector( onTap: () { - VideosRoute(eventReference: eventReference.toString()).push(context); + VideosRoute(eventReference: eventReference.encode()).push(context); }, child: _VideoContainer( thumbnailUrl: thumbnailUrl, diff --git a/lib/app/features/feed/views/pages/feed_page/components/trending_videos/trending_videos.dart b/lib/app/features/feed/views/pages/feed_page/components/trending_videos/trending_videos.dart index 700124596..7a6d2fe2f 100644 --- a/lib/app/features/feed/views/pages/feed_page/components/trending_videos/trending_videos.dart +++ b/lib/app/features/feed/views/pages/feed_page/components/trending_videos/trending_videos.dart @@ -15,7 +15,6 @@ import 'package:ion/app/features/feed/views/components/list_separator/list_separ import 'package:ion/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_videos_list.dart'; import 'package:ion/app/features/feed/views/pages/feed_page/components/trending_videos/components/trending_videos_list_skeleton.dart'; import 'package:ion/app/features/feed/views/pages/feed_page/components/trending_videos/components/video_icon.dart'; -import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/entities_paged_data_provider.c.dart'; import 'package:ion/app/router/app_routes.c.dart'; @@ -47,8 +46,8 @@ class TrendingVideos extends ConsumerWidget { children: [ SectionHeader( onPress: () { - final eventReference = EventReference.fromIonConnectEntity(videos.first); - VideosRoute(eventReference: eventReference.toString()).push(context); + final eventReference = videos.first.toEventReference(); + VideosRoute(eventReference: eventReference.encode()).push(context); }, title: context.i18n.feed_trending_videos, leadingIcon: const VideosIcon(), diff --git a/lib/app/features/feed/views/pages/notifications_history_page/components/notification_item/notification_item.dart b/lib/app/features/feed/views/pages/notifications_history_page/components/notification_item/notification_item.dart index 298c747a0..3b8547f18 100644 --- a/lib/app/features/feed/views/pages/notifications_history_page/components/notification_item/notification_item.dart +++ b/lib/app/features/feed/views/pages/notifications_history_page/components/notification_item/notification_item.dart @@ -8,7 +8,6 @@ import 'package:ion/app/features/feed/views/components/post/post.dart'; import 'package:ion/app/features/feed/views/pages/notifications_history_page/components/notification_item/notification_info.dart'; import 'package:ion/app/features/feed/views/pages/notifications_history_page/components/notification_item/notification_type_icon.dart'; import 'package:ion/app/features/feed/views/pages/notifications_history_page/components/notification_item/user_avatar.dart'; -import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; class NotificationItem extends StatelessWidget { const NotificationItem({ @@ -60,9 +59,7 @@ class NotificationItem extends StatelessWidget { height: 8.0.s, ), Post( - eventReference: EventReference.fromIonConnectEntity( - notificationData.postEntity!, - ), + eventReference: notificationData.postEntity!.toEventReference(), header: const SizedBox.shrink(), footer: const SizedBox.shrink(), ), diff --git a/lib/app/features/feed/views/pages/repost_options_modal/repost_options_modal.dart b/lib/app/features/feed/views/pages/repost_options_modal/repost_options_modal.dart index d0ebdf03b..7f802dec2 100644 --- a/lib/app/features/feed/views/pages/repost_options_modal/repost_options_modal.dart +++ b/lib/app/features/feed/views/pages/repost_options_modal/repost_options_modal.dart @@ -80,7 +80,7 @@ class RepostOptionsModal extends HookConsumerWidget { } case RepostOptionAction.quotePost: - CreatePostRoute(quotedEvent: eventReference.toString()).go(context); + CreatePostRoute(quotedEvent: eventReference.encode()).go(context); case RepostOptionAction.undoRepost: selectedAction.value = option; final repostEntity = ref.read(repostEntityProvider(eventReference)); diff --git a/lib/app/features/feed/views/pages/share_post_modal/share_post_modal.dart b/lib/app/features/feed/views/pages/share_post_modal/share_post_modal.dart index f02ed39f3..29c0a2b49 100644 --- a/lib/app/features/feed/views/pages/share_post_modal/share_post_modal.dart +++ b/lib/app/features/feed/views/pages/share_post_modal/share_post_modal.dart @@ -5,11 +5,12 @@ import 'package:go_router/go_router.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/feed/views/components/share_modal_base/share_modal_base.dart'; import 'package:ion/app/features/feed/views/pages/share_post_modal/components/share_options.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; class SharePostModal extends StatelessWidget { - const SharePostModal({required this.postId, super.key}); + const SharePostModal({required this.eventReference, super.key}); - final String postId; + final EventReference eventReference; @override Widget build(BuildContext context) { diff --git a/lib/app/features/feed/views/pages/who_can_reply_info_modal/who_can_reply_info_modal.dart b/lib/app/features/feed/views/pages/who_can_reply_info_modal/who_can_reply_info_modal.dart index f41531afb..89533b2aa 100644 --- a/lib/app/features/feed/views/pages/who_can_reply_info_modal/who_can_reply_info_modal.dart +++ b/lib/app/features/feed/views/pages/who_can_reply_info_modal/who_can_reply_info_modal.dart @@ -25,7 +25,7 @@ class WhoCanReplyInfoModal extends HookConsumerWidget { [context, eventReference], ); final userMetadata = - ref.watch(userMetadataProvider(eventReference.pubkey, cacheOnly: true)).valueOrNull; + ref.watch(userMetadataProvider(eventReference.pubkey, network: false)).valueOrNull; return Column( children: [ diff --git a/lib/app/features/ion_connect/model/event_reference.c.dart b/lib/app/features/ion_connect/model/event_reference.c.dart index b6623acd8..671ed2433 100644 --- a/lib/app/features/ion_connect/model/event_reference.c.dart +++ b/lib/app/features/ion_connect/model/event_reference.c.dart @@ -1,33 +1,118 @@ // SPDX-License-Identifier: ice License 1.0 import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; +import 'package:ion/app/exceptions/exceptions.dart'; part 'event_reference.c.freezed.dart'; +abstract class EventReference { + factory EventReference.fromEncoded(String input) { + final parts = input.split(separator); + return switch (parts[0]) { + ImmutableEventReference.tagName => ImmutableEventReference.fromEncoded(input), + ReplaceableEventReference.tagName => ReplaceableEventReference.fromEncoded(input), + _ => throw UnknownEventReferenceType(type: parts[0]), + }; + } + + String get pubkey; + + String encode(); + + static String separator = ':'; +} + @Freezed(toStringOverride: false) -class EventReference with _$EventReference { - const factory EventReference({ - required String eventId, +class ImmutableEventReference with _$ImmutableEventReference implements EventReference { + const factory ImmutableEventReference({ required String pubkey, - }) = _EventReference; + required String eventId, + }) = _ImmutableEventReference; - const EventReference._(); + const ImmutableEventReference._(); // TODO: use https://github.com/nostr-protocol/nips/blob/master/19.md#shareable-identifiers-with-extra-metadata ? - factory EventReference.fromString(String input) { - final parts = input.split(separator); - return EventReference(eventId: parts[0], pubkey: parts[1]); + factory ImmutableEventReference.fromEncoded(String input) { + final parts = input.split(EventReference.separator); + + if (parts[0] != tagName) { + throw UnknownEventReferenceType(type: parts[0]); + } + + return ImmutableEventReference(eventId: parts[1], pubkey: parts[2]); } - factory EventReference.fromIonConnectEntity(IonConnectEntity ionEntity) { - return EventReference(eventId: ionEntity.id, pubkey: ionEntity.masterPubkey); + @override + String encode() { + return [tagName, eventId, pubkey].join(EventReference.separator); } @override String toString() { - return '$eventId$separator$pubkey'; + return [eventId, pubkey].nonNulls.join(EventReference.separator); } - static String separator = ':'; + static const String tagName = 'e'; +} + +@Freezed(toStringOverride: false) +class ReplaceableEventReference with _$ReplaceableEventReference implements EventReference { + const factory ReplaceableEventReference({ + required String pubkey, + required int kind, + String? dTag, + }) = _ReplaceableEventReference; + + const ReplaceableEventReference._(); + + factory ReplaceableEventReference.fromTag(List tag) { + if (tag[0] != tagName) { + throw IncorrectEventTagNameException(actual: tag[0], expected: tagName); + } + + return ReplaceableEventReference.fromString(tag[1]); + } + + factory ReplaceableEventReference.fromEncoded(String input) { + final parts = input.split(EventReference.separator); + + if (parts[0] != tagName) { + throw UnknownEventReferenceType(type: parts[0]); + } + + return ReplaceableEventReference( + kind: int.parse(parts[1]), + pubkey: parts[2], + dTag: parts.elementAtOrNull(3), + ); + } + + factory ReplaceableEventReference.fromString(String input) { + final parts = input.split(EventReference.separator); + + return ReplaceableEventReference( + kind: int.parse(parts[0]), + pubkey: parts[1], + dTag: parts.elementAtOrNull(2), + ); + } + + List toTag() { + return [ + tagName, + [kind, pubkey, dTag].nonNulls.join(EventReference.separator), + ]; + } + + @override + String encode() { + return [tagName, kind, pubkey, dTag].nonNulls.join(EventReference.separator); + } + + @override + String toString() { + return [kind, pubkey, dTag].nonNulls.join(EventReference.separator); + } + + static const String tagName = 'a'; } diff --git a/lib/app/features/ion_connect/model/file_metadata.c.dart b/lib/app/features/ion_connect/model/file_metadata.c.dart index 72f1a9ec2..5dceac140 100644 --- a/lib/app/features/ion_connect/model/file_metadata.c.dart +++ b/lib/app/features/ion_connect/model/file_metadata.c.dart @@ -13,7 +13,8 @@ import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart' part 'file_metadata.c.freezed.dart'; @Freezed(equal: false) -class FileMetadataEntity with _$FileMetadataEntity, IonConnectEntity implements CacheableEntity { +class FileMetadataEntity + with _$FileMetadataEntity, IonConnectEntity, ImmutableEntity, CacheableEntity { const factory FileMetadataEntity({ required String id, required String pubkey, @@ -41,11 +42,6 @@ class FileMetadataEntity with _$FileMetadataEntity, IonConnectEntity implements ); } - @override - String get cacheKey => cacheKeyBuilder(id: id); - - static String cacheKeyBuilder({required String id}) => id; - static const int kind = 1063; } diff --git a/lib/app/features/ion_connect/model/ion_connect_entity.dart b/lib/app/features/ion_connect/model/ion_connect_entity.dart index 0732a4afa..93172dc97 100644 --- a/lib/app/features/ion_connect/model/ion_connect_entity.dart +++ b/lib/app/features/ion_connect/model/ion_connect_entity.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; @immutable @@ -24,6 +25,8 @@ abstract mixin class IonConnectEntity { ); } + EventReference toEventReference(); + @override bool operator ==(Object other) { return other is IonConnectEntity && id == other.id; @@ -33,6 +36,21 @@ abstract mixin class IonConnectEntity { int get hashCode => id.hashCode; } +mixin ImmutableEntity on IonConnectEntity { + @override + ImmutableEventReference toEventReference() { + return ImmutableEventReference(eventId: id, pubkey: masterPubkey); + } +} + +mixin ReplaceableEntity on IonConnectEntity { + ReplaceableEntityData get data; +} + +abstract class ReplaceableEntityData { + ReplaceableEventReference toReplaceableEventReference(String pubkey); +} + class SimpleSigner implements EventSigner { SimpleSigner(this.publicKey, this.signature); diff --git a/lib/app/features/ion_connect/model/not_authoritative_event.c.dart b/lib/app/features/ion_connect/model/not_authoritative_event.c.dart index cb1058d1f..b9a3b25db 100644 --- a/lib/app/features/ion_connect/model/not_authoritative_event.c.dart +++ b/lib/app/features/ion_connect/model/not_authoritative_event.c.dart @@ -12,7 +12,7 @@ import 'package:nostr_dart/nostr_dart.dart'; part 'not_authoritative_event.c.freezed.dart'; @Freezed(equal: false) -class NotAuthoritativeEvent with _$NotAuthoritativeEvent, IonConnectEntity { +class NotAuthoritativeEvent with _$NotAuthoritativeEvent, IonConnectEntity, ImmutableEntity { const factory NotAuthoritativeEvent({ required String id, required String pubkey, diff --git a/lib/app/features/ion_connect/model/quoted_modifiable_event.c.dart b/lib/app/features/ion_connect/model/quoted_replaceable_event.c.dart similarity index 66% rename from lib/app/features/ion_connect/model/quoted_modifiable_event.c.dart rename to lib/app/features/ion_connect/model/quoted_replaceable_event.c.dart index 24a5be1f6..7610cafbf 100644 --- a/lib/app/features/ion_connect/model/quoted_modifiable_event.c.dart +++ b/lib/app/features/ion_connect/model/quoted_replaceable_event.c.dart @@ -2,28 +2,28 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; -part 'quoted_modifiable_event.c.freezed.dart'; +part 'quoted_replaceable_event.c.freezed.dart'; @freezed -class QuotedModifiableEvent with _$QuotedModifiableEvent { - const factory QuotedModifiableEvent({ +class QuotedReplaceableEvent with _$QuotedReplaceableEvent { + const factory QuotedReplaceableEvent({ required ReplaceableEventReference eventReference, required String pubkey, - }) = _QuotedModifiableEvent; + }) = _QuotedReplaceableEvent; - const QuotedModifiableEvent._(); + const QuotedReplaceableEvent._(); /// https://github.com/ice-blockchain/subzero/blob/master/.ion-connect-protocol/ICIP-01.md#quotes - factory QuotedModifiableEvent.fromTag(List tag) { + factory QuotedReplaceableEvent.fromTag(List tag) { if (tag[0] != tagName) { throw IncorrectEventTagNameException(actual: tag[0], expected: tagName); } if (tag.length < 4) { throw IncorrectEventTagException(tag: tag.toString()); } - return QuotedModifiableEvent( + return QuotedReplaceableEvent( eventReference: ReplaceableEventReference.fromString(tag[1]), pubkey: tag[3], ); diff --git a/lib/app/features/ion_connect/model/related_event_marker.dart b/lib/app/features/ion_connect/model/related_event_marker.dart new file mode 100644 index 000000000..5ef30425c --- /dev/null +++ b/lib/app/features/ion_connect/model/related_event_marker.dart @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: ice License 1.0 + +enum RelatedEventMarker { reply, root, mention } diff --git a/lib/app/features/ion_connect/model/related_replaceable_event.c.dart b/lib/app/features/ion_connect/model/related_replaceable_event.c.dart index 0e5e9065f..8c93c907f 100644 --- a/lib/app/features/ion_connect/model/related_replaceable_event.c.dart +++ b/lib/app/features/ion_connect/model/related_replaceable_event.c.dart @@ -2,7 +2,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; part 'related_replaceable_event.c.freezed.dart'; diff --git a/lib/app/features/ion_connect/model/replaceable_event_reference.c.dart b/lib/app/features/ion_connect/model/replaceable_event_reference.c.dart deleted file mode 100644 index 0eaf48987..000000000 --- a/lib/app/features/ion_connect/model/replaceable_event_reference.c.dart +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: ice License 1.0 - -import 'package:collection/collection.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:ion/app/exceptions/exceptions.dart'; - -part 'replaceable_event_reference.c.freezed.dart'; - -@freezed -class ReplaceableEventReference with _$ReplaceableEventReference { - const factory ReplaceableEventReference({ - required int kind, - required String pubkey, - String? dTag, - }) = _ReplaceableEventReference; - - const ReplaceableEventReference._(); - - factory ReplaceableEventReference.fromTag(List tag) { - if (tag[0] != tagName) { - throw IncorrectEventTagNameException(actual: tag[0], expected: tagName); - } - - return ReplaceableEventReference.fromString(tag[1]); - } - - factory ReplaceableEventReference.fromString(String input) { - final parts = input.split(separator); - - return ReplaceableEventReference( - kind: int.parse(parts[0]), - pubkey: parts[1], - dTag: parts.elementAtOrNull(2), - ); - } - - List toTag() { - return [tagName, toString()]; - } - - @override - String toString() { - return [kind, pubkey, dTag].nonNulls.join(separator); - } - - static const String separator = ':'; - - static const String tagName = 'a'; -} diff --git a/lib/app/features/ion_connect/providers/ion_connect_cache.c.dart b/lib/app/features/ion_connect/providers/ion_connect_cache.c.dart index 30b32c3bc..847459698 100644 --- a/lib/app/features/ion_connect/providers/ion_connect_cache.c.dart +++ b/lib/app/features/ion_connect/providers/ion_connect_cache.c.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -11,7 +12,10 @@ part 'ion_connect_cache.c.g.dart'; final _ionConnectCacheStreamController = StreamController.broadcast(); mixin CacheableEntity on IonConnectEntity { - String get cacheKey; + String get cacheKey => cacheKeyBuilder(eventReference: toEventReference()); + + static String cacheKeyBuilder({required EventReference eventReference}) => + eventReference.toString(); } @Riverpod(keepAlive: true) diff --git a/lib/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart b/lib/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart index 75e33c395..dd834509b 100644 --- a/lib/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart +++ b/lib/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart @@ -15,12 +15,13 @@ part 'ion_connect_entity_provider.c.g.dart'; Future ionConnectEntity( Ref ref, { required EventReference eventReference, - bool skipCache = false, + bool network = true, + bool cache = true, }) async { - if (!skipCache) { + if (cache) { final entity = ref.watch( ionConnectCacheProvider.select( - cacheSelector(eventReference.eventId), + cacheSelector(CacheableEntity.cacheKeyBuilder(eventReference: eventReference)), ), ); if (entity != null) { @@ -28,10 +29,34 @@ Future ionConnectEntity( } } - final requestMessage = RequestMessage() - ..addFilter(RequestFilter(ids: [eventReference.eventId], limit: 1)); - return ref.read(ionConnectNotifierProvider.notifier).requestEntity( - requestMessage, - actionSource: ActionSourceUser(eventReference.pubkey), - ); + if (network) { + if (eventReference is ImmutableEventReference) { + final requestMessage = RequestMessage() + ..addFilter(RequestFilter(ids: [eventReference.eventId], limit: 1)); + return ref.read(ionConnectNotifierProvider.notifier).requestEntity( + requestMessage, + actionSource: ActionSourceUser(eventReference.pubkey), + ); + } else if (eventReference is ReplaceableEventReference) { + final requestMessage = RequestMessage() + ..addFilter( + RequestFilter( + kinds: [eventReference.kind], + authors: [eventReference.pubkey], + tags: { + if (eventReference.dTag != null) '#d': [eventReference.dTag.toString()], + }, + limit: 1, + ), + ); + return ref.read(ionConnectNotifierProvider.notifier).requestEntity( + requestMessage, + actionSource: ActionSourceUser(eventReference.pubkey), + ); + } else { + throw UnsupportedError(eventReference.toString()); + } + } + + return null; } diff --git a/lib/app/features/user/model/block_list.c.dart b/lib/app/features/user/model/block_list.c.dart index 93837ca95..6510e6180 100644 --- a/lib/app/features/user/model/block_list.c.dart +++ b/lib/app/features/user/model/block_list.c.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; @@ -13,7 +14,9 @@ import 'package:nostr_dart/nostr_dart.dart'; part 'block_list.c.freezed.dart'; @Freezed(equal: false) -class BlockListEntity with _$BlockListEntity, IonConnectEntity implements CacheableEntity { +class BlockListEntity + with _$BlockListEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory BlockListEntity({ required String id, required String pubkey, @@ -42,15 +45,15 @@ class BlockListEntity with _$BlockListEntity, IonConnectEntity implements Cachea } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); - - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 10000; } @freezed -class BlockListData with _$BlockListData implements EventSerializable { +class BlockListData with _$BlockListData implements EventSerializable, ReplaceableEntityData { const factory BlockListData({ required List pubkeys, }) = _BlockListData; @@ -80,4 +83,12 @@ class BlockListData with _$BlockListData implements EventSerializable { content: '', ); } + + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: BlockListEntity.kind, + pubkey: pubkey, + ); + } } diff --git a/lib/app/features/user/model/follow_list.c.dart b/lib/app/features/user/model/follow_list.c.dart index f4d9c985c..80508e88d 100644 --- a/lib/app/features/user/model/follow_list.c.dart +++ b/lib/app/features/user/model/follow_list.c.dart @@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; @@ -13,7 +14,9 @@ import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart' part 'follow_list.c.freezed.dart'; @Freezed(equal: false) -class FollowListEntity with _$FollowListEntity, IonConnectEntity implements CacheableEntity { +class FollowListEntity + with _$FollowListEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory FollowListEntity({ required String id, required String pubkey, @@ -44,15 +47,15 @@ class FollowListEntity with _$FollowListEntity, IonConnectEntity implements Cach List get pubkeys => data.list.map((followee) => followee.pubkey).toList(); @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); - - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 3; } @freezed -class FollowListData with _$FollowListData implements EventSerializable { +class FollowListData with _$FollowListData implements EventSerializable, ReplaceableEntityData { const factory FollowListData({ required List list, }) = _FollowListData; @@ -85,6 +88,14 @@ class FollowListData with _$FollowListData implements EventSerializable { content: '', ); } + + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: FollowListEntity.kind, + pubkey: pubkey, + ); + } } @freezed diff --git a/lib/app/features/user/model/interest_set.c.dart b/lib/app/features/user/model/interest_set.c.dart index e94f3c031..5f30036c5 100644 --- a/lib/app/features/user/model/interest_set.c.dart +++ b/lib/app/features/user/model/interest_set.c.dart @@ -7,10 +7,10 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/model/replaceable_event_identifier.c.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; part 'interest_set.c.freezed.dart'; @@ -18,7 +18,9 @@ part 'interest_set.c.freezed.dart'; enum InterestSetType { languages, unknown } @Freezed(equal: false) -class InterestSetEntity with _$InterestSetEntity, IonConnectEntity implements CacheableEntity { +class InterestSetEntity + with _$InterestSetEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory InterestSetEntity({ required String id, required String pubkey, @@ -47,16 +49,15 @@ class InterestSetEntity with _$InterestSetEntity, IonConnectEntity implements Ca } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey, type: data.type); - - static String cacheKeyBuilder({required String pubkey, required InterestSetType type}) => - '$kind:$type:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 30015; } @freezed -class InterestSetData with _$InterestSetData implements EventSerializable { +class InterestSetData with _$InterestSetData implements EventSerializable, ReplaceableEntityData { const factory InterestSetData({ required InterestSetType type, required List hashtags, @@ -97,6 +98,7 @@ class InterestSetData with _$InterestSetData implements EventSerializable { ); } + @override ReplaceableEventReference toReplaceableEventReference(String pubkey) { return ReplaceableEventReference( kind: InterestSetEntity.kind, diff --git a/lib/app/features/user/model/interests.c.dart b/lib/app/features/user/model/interests.c.dart index 42884daeb..eeaab774b 100644 --- a/lib/app/features/user/model/interests.c.dart +++ b/lib/app/features/user/model/interests.c.dart @@ -7,15 +7,17 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; part 'interests.c.freezed.dart'; @Freezed(equal: false) -class InterestsEntity with _$InterestsEntity, IonConnectEntity implements CacheableEntity { +class InterestsEntity + with _$InterestsEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory InterestsEntity({ required String id, required String pubkey, @@ -44,15 +46,15 @@ class InterestsEntity with _$InterestsEntity, IonConnectEntity implements Cachea } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); - - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 10015; } @freezed -class InterestsData with _$InterestsData implements EventSerializable { +class InterestsData with _$InterestsData implements EventSerializable, ReplaceableEntityData { const factory InterestsData({ required List hashtags, required List interestSetRefs, @@ -89,4 +91,12 @@ class InterestsData with _$InterestsData implements EventSerializable { content: '', ); } + + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: InterestsEntity.kind, + pubkey: pubkey, + ); + } } diff --git a/lib/app/features/user/model/user_chat_relays.c.dart b/lib/app/features/user/model/user_chat_relays.c.dart index cf63a73dc..f3409be29 100644 --- a/lib/app/features/user/model/user_chat_relays.c.dart +++ b/lib/app/features/user/model/user_chat_relays.c.dart @@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; @@ -15,8 +16,8 @@ part 'user_chat_relays.c.freezed.dart'; @Freezed(equal: false) class UserChatRelaysEntity - with _$UserChatRelaysEntity, IonConnectEntity - implements CacheableEntity { + with _$UserChatRelaysEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory UserChatRelaysEntity({ required String id, required String pubkey, @@ -45,17 +46,19 @@ class UserChatRelaysEntity } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } List get urls => data.list.map((relay) => relay.url).toList(); - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; - static const int kind = 10050; } @freezed -class UserChatRelaysData with _$UserChatRelaysData implements EventSerializable { +class UserChatRelaysData + with _$UserChatRelaysData + implements EventSerializable, ReplaceableEntityData { const factory UserChatRelaysData({ required List list, }) = _UserChatRelaysData; @@ -89,6 +92,14 @@ class UserChatRelaysData with _$UserChatRelaysData implements EventSerializable ); } + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: UserChatRelaysEntity.kind, + pubkey: pubkey, + ); + } + static List toRelayTag(UserRelay relay) => ['relay', relay.url]; static UserRelay fromRelayTag(List tag) => UserRelay(url: tag[1]); } diff --git a/lib/app/features/user/model/user_delegation.c.dart b/lib/app/features/user/model/user_delegation.c.dart index 69602b046..4eb69bd29 100644 --- a/lib/app/features/user/model/user_delegation.c.dart +++ b/lib/app/features/user/model/user_delegation.c.dart @@ -4,6 +4,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; @@ -13,8 +14,8 @@ enum DelegationStatus { active, inactive, revoked } @Freezed(equal: false) class UserDelegationEntity - with _$UserDelegationEntity, IonConnectEntity - implements CacheableEntity { + with _$UserDelegationEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory UserDelegationEntity({ required String id, required String pubkey, @@ -43,15 +44,15 @@ class UserDelegationEntity } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); - - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 10100; } @freezed -class UserDelegationData with _$UserDelegationData { +class UserDelegationData with _$UserDelegationData implements ReplaceableEntityData { const factory UserDelegationData({ required List delegates, }) = _UserDelegationData; @@ -95,6 +96,14 @@ class UserDelegationData with _$UserDelegationData { List> get tags { return delegates.map((delegate) => delegate.toTag()).toList(); } + + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: UserDelegationEntity.kind, + pubkey: pubkey, + ); + } } @freezed diff --git a/lib/app/features/user/model/user_metadata.c.dart b/lib/app/features/user/model/user_metadata.c.dart index fa66d349e..cba37b96f 100644 --- a/lib/app/features/user/model/user_metadata.c.dart +++ b/lib/app/features/user/model/user_metadata.c.dart @@ -7,6 +7,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/model/media_attachment.dart'; @@ -16,7 +17,9 @@ part 'user_metadata.c.freezed.dart'; part 'user_metadata.c.g.dart'; @Freezed(equal: false) -class UserMetadataEntity with _$UserMetadataEntity, IonConnectEntity implements CacheableEntity { +class UserMetadataEntity + with _$UserMetadataEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory UserMetadataEntity({ required String id, required String pubkey, @@ -45,15 +48,15 @@ class UserMetadataEntity with _$UserMetadataEntity, IonConnectEntity implements } @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); - - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 0; } @freezed -class UserMetadata with _$UserMetadata implements EventSerializable { +class UserMetadata with _$UserMetadata implements EventSerializable, ReplaceableEntityData { const factory UserMetadata({ @Default('') String name, @Default('') String displayName, @@ -116,6 +119,14 @@ class UserMetadata with _$UserMetadata implements EventSerializable { ], ); } + + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: UserMetadataEntity.kind, + pubkey: pubkey, + ); + } } @JsonSerializable(createToJson: true) diff --git a/lib/app/features/user/model/user_relays.c.dart b/lib/app/features/user/model/user_relays.c.dart index 3e56883c5..1e4b5c4db 100644 --- a/lib/app/features/user/model/user_relays.c.dart +++ b/lib/app/features/user/model/user_relays.c.dart @@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/event_serializable.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; @@ -13,7 +14,9 @@ import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart' part 'user_relays.c.freezed.dart'; @Freezed(equal: false) -class UserRelaysEntity with _$UserRelaysEntity, IonConnectEntity implements CacheableEntity { +class UserRelaysEntity + with _$UserRelaysEntity, IonConnectEntity, CacheableEntity + implements ReplaceableEntity { const factory UserRelaysEntity({ required String id, required String pubkey, @@ -44,15 +47,15 @@ class UserRelaysEntity with _$UserRelaysEntity, IonConnectEntity implements Cach List get urls => data.list.map((relay) => relay.url).toList(); @override - String get cacheKey => cacheKeyBuilder(pubkey: masterPubkey); - - static String cacheKeyBuilder({required String pubkey}) => '$kind:$pubkey'; + ReplaceableEventReference toEventReference() { + return data.toReplaceableEventReference(masterPubkey); + } static const int kind = 10002; } @freezed -class UserRelaysData with _$UserRelaysData implements EventSerializable { +class UserRelaysData with _$UserRelaysData implements EventSerializable, ReplaceableEntityData { const factory UserRelaysData({ required List list, }) = _UserRelaysData; @@ -85,6 +88,14 @@ class UserRelaysData with _$UserRelaysData implements EventSerializable { content: '', ); } + + @override + ReplaceableEventReference toReplaceableEventReference(String pubkey) { + return ReplaceableEventReference( + kind: UserRelaysEntity.kind, + pubkey: pubkey, + ); + } } @freezed diff --git a/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_avatars.dart b/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_avatars.dart index ab4d73cbf..9c5826b89 100644 --- a/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_avatars.dart +++ b/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_avatars.dart @@ -4,8 +4,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ion/app/components/avatar/avatar.dart'; import 'package:ion/app/extensions/extensions.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; -import 'package:ion/app/features/user/model/user_metadata.c.dart'; +import 'package:ion/app/features/user/providers/user_metadata_provider.c.dart'; class FollowedByAvatars extends StatelessWidget { const FollowedByAvatars({required this.pubkeys, super.key}); @@ -45,11 +44,7 @@ class _FollowedAvatar extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { // User metadata is fetched alongside the `followersYouKnowDataSourceProvider`, so don't fetch it manually - final userMetadata = ref.watch( - ionConnectCacheProvider.select( - cacheSelector(UserMetadataEntity.cacheKeyBuilder(pubkey: pubkey)), - ), - ); + final userMetadata = ref.watch(userMetadataProvider(pubkey, network: false)).valueOrNull; return Container( padding: EdgeInsets.all(borderWidth), diff --git a/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_text.dart b/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_text.dart index b368af5e6..55a0368ab 100644 --- a/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_text.dart +++ b/lib/app/features/user/pages/profile_page/components/profile_details/followers_you_know/followed_by_text.dart @@ -3,9 +3,8 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ion/app/extensions/extensions.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; import 'package:ion/app/features/user/model/follow_type.dart'; -import 'package:ion/app/features/user/model/user_metadata.c.dart'; +import 'package:ion/app/features/user/providers/user_metadata_provider.c.dart'; import 'package:ion/app/hooks/use_tap_gesture_recognizer.dart'; import 'package:ion/app/router/app_routes.c.dart'; @@ -22,13 +21,8 @@ class FollowedByText extends HookConsumerWidget { final firstUserPubkey = pubkeys.first; // User metadata is fetched alongside the `followersYouKnowDataSourceProvider`, so don't fetch it manually - final firstUserMetadata = ref.watch( - ionConnectCacheProvider.select( - cacheSelector( - UserMetadataEntity.cacheKeyBuilder(pubkey: firstUserPubkey), - ), - ), - ); + final firstUserMetadata = + ref.watch(userMetadataProvider(firstUserPubkey, network: false)).valueOrNull; final userTapRecognizer = useTapGestureRecognizer( onTap: () => ProfileRoute(pubkey: firstUserPubkey).push(context), diff --git a/lib/app/features/user/providers/block_list_notifier.c.dart b/lib/app/features/user/providers/block_list_notifier.c.dart index c133facfd..81d8daf37 100644 --- a/lib/app/features/user/providers/block_list_notifier.c.dart +++ b/lib/app/features/user/providers/block_list_notifier.c.dart @@ -6,15 +6,12 @@ import 'package:ion/app/features/auth/providers/auth_provider.c.dart'; import 'package:ion/app/features/feed/data/models/entities/post_data.c.dart'; import 'package:ion/app/features/feed/data/models/entities/repost_data.c.dart'; import 'package:ion/app/features/feed/data/models/generic_repost.c.dart'; -import 'package:ion/app/features/ion_connect/model/action_source.dart'; import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/model/ion_connect_entity.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart'; import 'package:ion/app/features/user/model/block_list.c.dart'; import 'package:ion/app/features/user/providers/follow_list_provider.c.dart'; -import 'package:nostr_dart/nostr_dart.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'block_list_notifier.c.g.dart'; @@ -23,29 +20,16 @@ part 'block_list_notifier.c.g.dart'; Future blockList( Ref ref, String pubkey, { - bool cacheOnly = false, + bool cache = true, + bool network = true, }) async { - final cacheKey = BlockListEntity.cacheKeyBuilder(pubkey: pubkey); - final blockList = ref.watch( - ionConnectCacheProvider.select(cacheSelector(cacheKey)), - ); - - if (blockList != null || cacheOnly) { - return blockList; - } - - final requestMessage = RequestMessage() - ..addFilter( - RequestFilter( - kinds: const [BlockListEntity.kind], - authors: [pubkey], - ), - ); - - return ref.watch(ionConnectNotifierProvider.notifier).requestEntity( - requestMessage, - actionSource: ActionSourceUser(pubkey), - ); + return await ref.watch( + ionConnectEntityProvider( + eventReference: ReplaceableEventReference(pubkey: pubkey, kind: BlockListEntity.kind), + network: network, + cache: cache, + ).future, + ) as BlockListEntity?; } @riverpod @@ -69,8 +53,7 @@ Future isBlocking(Ref ref, String pubkey, {bool cacheOnly = false}) async if (currentPubkey == null) { return false; } - final otherUserBlockList = - await ref.watch(blockListProvider(pubkey, cacheOnly: cacheOnly).future); + final otherUserBlockList = await ref.watch(blockListProvider(pubkey, network: !cacheOnly).future); return otherUserBlockList?.data.pubkeys.contains(currentPubkey) ?? false; } @@ -111,7 +94,7 @@ Future isPostChildBlockedOrBlocking( }) async { final quotedEvent = entity.data.quotedEvent; if (quotedEvent == null) return false; - final quotedPostReference = EventReference( + final quotedPostReference = ImmutableEventReference( eventId: quotedEvent.eventId, pubkey: quotedEvent.pubkey, ); @@ -131,7 +114,8 @@ Future isRepostChildBlockedOrBlocking( RepostEntity repost, { bool cacheOnly = false, }) async { - final eventReference = EventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); + final eventReference = + ImmutableEventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); final entity = await ref.watch(ionConnectEntityProvider(eventReference: eventReference).future); if (entity == null) return true; return ref.watch(isEntityBlockedOrBlockingProvider(entity, cacheOnly: cacheOnly)).valueOrNull ?? @@ -144,7 +128,8 @@ Future isGenericRepostChildBlockedOrBlocking( GenericRepostEntity repost, { bool cacheOnly = false, }) async { - final eventReference = EventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); + final eventReference = + ImmutableEventReference(eventId: repost.data.eventId, pubkey: repost.data.pubkey); final entity = await ref.watch(ionConnectEntityProvider(eventReference: eventReference).future); if (entity == null) return true; return ref.watch(isEntityBlockedOrBlockingProvider(entity, cacheOnly: cacheOnly)).valueOrNull ?? diff --git a/lib/app/features/user/providers/follow_list_provider.c.dart b/lib/app/features/user/providers/follow_list_provider.c.dart index f99bcb67e..90199ea54 100644 --- a/lib/app/features/user/providers/follow_list_provider.c.dart +++ b/lib/app/features/user/providers/follow_list_provider.c.dart @@ -4,9 +4,8 @@ import 'package:collection/collection.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/features/auth/providers/auth_provider.c.dart'; -import 'package:ion/app/features/ion_connect/ion_connect.dart'; -import 'package:ion/app/features/ion_connect/model/action_source.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; +import 'package:ion/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart'; import 'package:ion/app/features/user/model/follow_list.c.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -17,34 +16,16 @@ part 'follow_list_provider.c.g.dart'; Future followList( Ref ref, String pubkey, { - bool skipCache = false, + bool network = true, + bool cache = true, }) async { - if (!skipCache) { - final followList = ref.watch( - ionConnectCacheProvider.select( - cacheSelector( - FollowListEntity.cacheKeyBuilder(pubkey: pubkey), - ), - ), - ); - if (followList != null) { - return followList; - } - } - - final requestMessage = RequestMessage() - ..addFilter( - RequestFilter( - kinds: const [FollowListEntity.kind], - authors: [pubkey], - limit: 1, - ), - ); - - return ref.watch(ionConnectNotifierProvider.notifier).requestEntity( - requestMessage, - actionSource: ActionSourceUser(pubkey), - ); + return await ref.watch( + ionConnectEntityProvider( + eventReference: ReplaceableEventReference(pubkey: pubkey, kind: FollowListEntity.kind), + network: network, + cache: cache, + ).future, + ) as FollowListEntity?; } @Riverpod(keepAlive: true) diff --git a/lib/app/features/user/providers/user_delegation_provider.c.dart b/lib/app/features/user/providers/user_delegation_provider.c.dart index 51284fc19..7f39ea547 100644 --- a/lib/app/features/user/providers/user_delegation_provider.c.dart +++ b/lib/app/features/user/providers/user_delegation_provider.c.dart @@ -5,6 +5,7 @@ import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/features/auth/providers/auth_provider.c.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; import 'package:ion/app/features/ion_connect/model/action_source.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart'; import 'package:ion/app/features/user/model/user_delegation.c.dart'; @@ -19,7 +20,12 @@ part 'user_delegation_provider.c.g.dart'; Future userDelegation(Ref ref, String pubkey) async { final userDelegation = ref.watch( ionConnectCacheProvider.select( - cacheSelector(UserDelegationEntity.cacheKeyBuilder(pubkey: pubkey)), + cacheSelector( + CacheableEntity.cacheKeyBuilder( + eventReference: + ReplaceableEventReference(pubkey: pubkey, kind: UserDelegationEntity.kind), + ), + ), ), ); if (userDelegation != null) { diff --git a/lib/app/features/user/providers/user_metadata_provider.c.dart b/lib/app/features/user/providers/user_metadata_provider.c.dart index cc4a22438..d5d3f7be3 100644 --- a/lib/app/features/user/providers/user_metadata_provider.c.dart +++ b/lib/app/features/user/providers/user_metadata_provider.c.dart @@ -3,10 +3,8 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ion/app/exceptions/exceptions.dart'; import 'package:ion/app/features/auth/providers/auth_provider.c.dart'; -import 'package:ion/app/features/ion_connect/ion_connect.dart'; -import 'package:ion/app/features/ion_connect/model/action_source.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; -import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; +import 'package:ion/app/features/ion_connect/providers/ion_connect_entity_provider.c.dart'; import 'package:ion/app/features/user/model/user_metadata.c.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -16,24 +14,16 @@ part 'user_metadata_provider.c.g.dart'; Future userMetadata( Ref ref, String pubkey, { - bool cacheOnly = false, + bool network = true, + bool cache = true, }) async { - final userMetadata = ref.watch( - ionConnectCacheProvider.select( - cacheSelector(UserMetadataEntity.cacheKeyBuilder(pubkey: pubkey)), - ), - ); - - if (userMetadata != null || cacheOnly) { - return userMetadata; - } - - final requestMessage = RequestMessage() - ..addFilter(RequestFilter(kinds: const [UserMetadataEntity.kind], authors: [pubkey], limit: 1)); - return ref.read(ionConnectNotifierProvider.notifier).requestEntity( - requestMessage, - actionSource: ActionSourceUser(pubkey), - ); + return await ref.watch( + ionConnectEntityProvider( + eventReference: ReplaceableEventReference(pubkey: pubkey, kind: UserMetadataEntity.kind), + network: network, + cache: cache, + ).future, + ) as UserMetadataEntity?; } @Riverpod(keepAlive: true) diff --git a/lib/app/features/user/providers/user_relays_manager.c.dart b/lib/app/features/user/providers/user_relays_manager.c.dart index 81d3a4d16..c0e96e116 100644 --- a/lib/app/features/user/providers/user_relays_manager.c.dart +++ b/lib/app/features/user/providers/user_relays_manager.c.dart @@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:ion/app/features/auth/providers/auth_provider.c.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; import 'package:ion/app/features/ion_connect/model/action_source.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_cache.c.dart'; import 'package:ion/app/features/ion_connect/providers/ion_connect_notifier.c.dart'; import 'package:ion/app/features/user/model/user_relays.c.dart'; @@ -43,7 +44,12 @@ class UserRelaysManager extends _$UserRelaysManager { for (final pubkey in pubkeys) { final cached = ref.read( ionConnectCacheProvider.select( - cacheSelector(UserRelaysEntity.cacheKeyBuilder(pubkey: pubkey)), + cacheSelector( + CacheableEntity.cacheKeyBuilder( + eventReference: + ReplaceableEventReference(pubkey: pubkey, kind: UserRelaysEntity.kind), + ), + ), ), ); if (cached != null) { diff --git a/lib/app/features/video/views/pages/video_page.dart b/lib/app/features/video/views/pages/video_page.dart index 5be5d9fa3..cbf3badba 100644 --- a/lib/app/features/video/views/pages/video_page.dart +++ b/lib/app/features/video/views/pages/video_page.dart @@ -7,7 +7,6 @@ import 'package:ion/app/extensions/extensions.dart'; import 'package:ion/app/features/core/providers/app_lifecycle_provider.c.dart'; import 'package:ion/app/features/core/providers/video_player_provider.c.dart'; import 'package:ion/app/features/feed/data/models/entities/post_data.c.dart'; -import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/video/views/components/video_actions.dart'; import 'package:ion/app/features/video/views/components/video_controls.dart'; import 'package:ion/app/features/video/views/components/video_header.dart'; @@ -100,7 +99,7 @@ class VideoPage extends HookConsumerWidget { ), ), VideoActions( - eventReference: EventReference.fromIonConnectEntity(video), + eventReference: video.toEventReference(), ), ], ), diff --git a/lib/app/router/feed_routes.dart b/lib/app/router/feed_routes.dart index 3547dc107..fa0fb74a4 100644 --- a/lib/app/router/feed_routes.dart +++ b/lib/app/router/feed_routes.dart @@ -19,7 +19,7 @@ class FeedRoutes { TypedShellRoute( routes: [ TypedGoRoute(path: 'post-repost-options/:eventReference'), - TypedGoRoute(path: 'share-post/:postId'), + TypedGoRoute(path: 'share-post/:eventReference'), TypedGoRoute(path: 'create-post'), TypedGoRoute(path: 'create-article'), TypedGoRoute( @@ -42,7 +42,7 @@ class ArticleDetailsRoute extends BaseRouteData { ArticleDetailsRoute({required this.eventReference}) : super( child: ArticleDetailsPage( - eventReference: EventReference.fromString(eventReference), + eventReference: EventReference.fromEncoded(eventReference), ), ); @@ -52,7 +52,7 @@ class ArticleDetailsRoute extends BaseRouteData { class PostDetailsRoute extends BaseRouteData { PostDetailsRoute({required this.eventReference}) : super( - child: PostDetailsPage(eventReference: EventReference.fromString(eventReference)), + child: PostDetailsPage(eventReference: EventReference.fromEncoded(eventReference)), ); final String eventReference; @@ -70,7 +70,7 @@ class RepostOptionsModalRoute extends BaseRouteData { required this.eventReference, }) : super( child: RepostOptionsModal( - eventReference: EventReference.fromString(eventReference), + eventReference: EventReference.fromEncoded(eventReference), ), type: IceRouteType.bottomSheet, ); @@ -80,13 +80,13 @@ class RepostOptionsModalRoute extends BaseRouteData { class SharePostModalRoute extends BaseRouteData { SharePostModalRoute({ - required this.postId, + required this.eventReference, }) : super( - child: SharePostModal(postId: postId), + child: SharePostModal(eventReference: EventReference.fromEncoded(eventReference)), type: IceRouteType.bottomSheet, ); - final String postId; + final String eventReference; } class FeedSimpleSearchRoute extends BaseRouteData { @@ -134,8 +134,8 @@ class CreatePostRoute extends BaseRouteData { this.videoPath, }) : super( child: CreatePostModal( - parentEvent: parentEvent != null ? EventReference.fromString(parentEvent) : null, - quotedEvent: quotedEvent != null ? EventReference.fromString(quotedEvent) : null, + parentEvent: parentEvent != null ? EventReference.fromEncoded(parentEvent) : null, + quotedEvent: quotedEvent != null ? EventReference.fromEncoded(quotedEvent) : null, content: content, showCollapseButton: showCollapseButton, videoPath: videoPath, @@ -276,7 +276,7 @@ class VideosRoute extends BaseRouteData { VideosRoute({required this.eventReference}) : super( child: VideosPage( - EventReference.fromString(eventReference), + EventReference.fromEncoded(eventReference), ), ); diff --git a/test/user/interests_test.dart b/test/user/interests_test.dart index 290c4b8df..fbff3a1ee 100644 --- a/test/user/interests_test.dart +++ b/test/user/interests_test.dart @@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:ion/app/features/ion_connect/ion_connect.dart'; -import 'package:ion/app/features/ion_connect/model/replaceable_event_reference.c.dart'; +import 'package:ion/app/features/ion_connect/model/event_reference.c.dart'; import 'package:ion/app/features/user/model/interests.c.dart'; import 'package:ion/app/services/ion_connect/ed25519_key_store.dart';