Skip to content

Commit

Permalink
feat: followers counter flow (#463)
Browse files Browse the repository at this point in the history
## Description
Add fetch followers count

## Type of Change
- [ ] Bug fix
- [x] New feature
- [ ] Breaking change
- [ ] Refactoring
- [ ] Documentation
- [ ] Chore

---------

Co-authored-by: ion-endymion <[email protected]>
  • Loading branch information
ice-orion and ice-endymion authored Jan 21, 2025
1 parent dcca083 commit 0db8977
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class EventCountRequestEntity
class EventCountRequestData with _$EventCountRequestData implements EventSerializable {
const factory EventCountRequestData({
required List<RequestFilter> filters,
required EventCountRequestParams params,
EventCountRequestParams? params,
List<String>? relays,
String? output,
}) = _EventCountRequestData;

Expand All @@ -71,6 +72,7 @@ class EventCountRequestData with _$EventCountRequestData implements EventSeriali
return EventCountRequestData(
filters: filters,
params: EventCountRequestParams.fromTags(tags[EventCountRequestParams.tagName] ?? []),
relays: tags['relays']?.first.skip(1).toList(),
output: tags['output']?.first[1],
);
}
Expand All @@ -85,8 +87,12 @@ class EventCountRequestData with _$EventCountRequestData implements EventSeriali
signer: signer,
createdAt: createdAt,
kind: EventCountRequestEntity.kind,
content: json.encode(filters.map((filter) => filter.toString()).toList()),
tags: [...tags, ...params.toTags()],
content: json.encode(filters),
tags: [
...tags,
if (params != null) ...params!.toTags(),
if (relays != null) ['relays', ...relays!],
],
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,18 @@ class EventCountResultEntity
const EventCountResultEntity._();

/// https://github.com/nostr-protocol/nips/blob/vending-machine/90.md
factory EventCountResultEntity.fromEventMessage(EventMessage eventMessage) {
factory EventCountResultEntity.fromEventMessage(
EventMessage eventMessage, {
String? key,
}) {
if (eventMessage.kind != kind) {
throw IncorrectEventKindException(eventId: eventMessage.id, kind: kind);
}

final data = EventCountResultData.fromEventMessage(eventMessage);
final type = data.getType();
final summary = EventCountResultSummary(
key: data.getKey(type),
key: key ?? data.getKey(type),
type: type,
content: data.content,
requestEventId: data.request.id,
Expand Down Expand Up @@ -125,14 +128,14 @@ class EventCountResultData with _$EventCountResultData {
EventCountResultType getType() {
final EventCountRequestData(:filters, :params) = request.data;
final filter = filters.first;
if (params.group == RelatedEventMarker.reply.toShortString() ||
params.group == RelatedEventMarker.root.toShortString()) {
if (params?.group == RelatedEventMarker.reply.toShortString() ||
params?.group == RelatedEventMarker.root.toShortString()) {
return EventCountResultType.replies;
} else if (filter.kinds != null &&
filter.kinds!.contains(RepostEntity.kind) &&
params.group == RelatedEvent.tagName) {
params?.group == RelatedEvent.tagName) {
return EventCountResultType.reposts;
} else if (params.group == QuotedEvent.tagName) {
} else if (params?.group == QuotedEvent.tagName) {
return EventCountResultType.quotes;
} else if (filter.kinds != null && filter.kinds!.contains(ReactionEntity.kind)) {
return EventCountResultType.reactions;
Expand Down
18 changes: 0 additions & 18 deletions lib/app/features/ion_connect/providers/ion_connect_notifier.c.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import 'package:ion/app/exceptions/exceptions.dart';
import 'package:ion/app/extensions/extensions.dart';
import 'package:ion/app/features/auth/providers/auth_provider.c.dart';
import 'package:ion/app/features/chat/providers/user_chat_relays_provider.c.dart';
import 'package:ion/app/features/feed/data/models/entities/event_count_request_data.c.dart';
import 'package:ion/app/features/feed/data/models/entities/event_count_result_data.c.dart';
import 'package:ion/app/features/ion_connect/ion_connect.dart' as ion;
import 'package:ion/app/features/ion_connect/ion_connect.dart' hide requestEvents;
import 'package:ion/app/features/ion_connect/model/action_source.dart';
Expand Down Expand Up @@ -196,22 +194,6 @@ class IonConnectNotifier extends _$IonConnectNotifier {
return entities.isNotEmpty ? entities.first as T : null;
}

Future<EventCountResultEntity> requestCount(
EventCountRequestData requestData, {
ActionSource actionSource = const ActionSourceCurrentUser(),
}) async {
final requestEventMessage = await sign(requestData);
final relay = await _getRelay(actionSource);
relay.sendMessage(requestEventMessage);
return relay.messages
.where((message) => message is EventMessage && message.kind == EventCountResultEntity.kind)
.cast<EventMessage>()
.map(EventCountResultEntity.fromEventMessage)
.firstWhere(
(countResult) => countResult.data.requestEventId == requestEventMessage.id,
);
}

Future<EventMessage> sign(EventSerializable entityData) async {
final eventSigner = ref.read(currentUserIonConnectEventSignerProvider).valueOrNull;
final mainWallet = ref.read(mainWalletProvider).valueOrNull;
Expand Down
110 changes: 84 additions & 26 deletions lib/app/features/user/providers/followers_count_provider.c.dart
Original file line number Diff line number Diff line change
@@ -1,43 +1,101 @@
// SPDX-License-Identifier: ice License 1.0

import 'dart:math';

import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:ion/app/exceptions/exceptions.dart';
import 'package:ion/app/extensions/extensions.dart';
import 'package:ion/app/features/feed/data/models/entities/event_count_request_data.c.dart';
import 'package:ion/app/features/feed/data/models/entities/event_count_result_data.c.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/providers/relays_provider.c.dart';
import 'package:ion/app/features/user/model/follow_list.c.dart';
import 'package:ion/app/features/user/providers/user_relays_manager.c.dart';
import 'package:nostr_dart/nostr_dart.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'followers_count_provider.c.g.dart';

@Riverpod(keepAlive: true)
Future<int?> followersCount(Ref ref, String pubkey) async {
final followersCountEntity = ref.watch(
ionConnectCacheProvider.select(
cacheSelector<EventCountResultEntity>(
EventCountResultEntity.cacheKeyBuilder(
key: pubkey,
type: EventCountResultType.followers,
class FollowersCount extends _$FollowersCount {
@override
Future<int> build(String pubkey) async {
final followersCountEntity = ref.watch(
ionConnectCacheProvider.select(
cacheSelector<EventCountResultEntity>(
EventCountResultEntity.cacheKeyBuilder(
key: pubkey,
type: EventCountResultType.followers,
),
),
),
),
);
);

if (followersCountEntity != null) {
return followersCountEntity.data.content as int;
}

if (followersCountEntity != null) {
return 0;
return _fetchFollowersCount(pubkey);
}

// TODO:uncomment when impl
// final followersCountRequest = EventCountRequestData(
// params: const EventCountRequestParams(group: 'p'),
// filters: [
// RequestFilter(kinds: const [FollowListEntity.kind], p: [pubkey]),
// ],
// );
Future<int> _fetchFollowersCount(String pubkey) async {
final relay = await _getRandomUserRelay();

final requestEvent = await _buildRequestEvent(relayUrl: relay.url);
final subscriptionMessage = RequestMessage()
..addFilter(
RequestFilter(
kinds: const [EventCountResultEntity.kind, 7400],
tags: {
'#p': [pubkey],
},
),
);

final subscription = relay.subscribe(subscriptionMessage);
EventMessage? responseMessage;

try {
await ref.read(ionConnectNotifierProvider.notifier).sendEvent(
requestEvent,
actionSource: ActionSourceRelayUrl(relay.url),
cache: false,
);

// final response = await ref.read(ionConnectNotifierProvider.notifier).requestCount(
// followersCountRequest,
// actionSource: ActionSourceUser(pubkey),
// );
responseMessage = await subscription.messages
.firstWhere((message) => message is EventMessage)
.timeout(const Duration(seconds: 10)) as EventMessage;

return Random().nextInt(100);
final eventCountResultEntity =
EventCountResultEntity.fromEventMessage(responseMessage, key: pubkey);
ref.read(ionConnectCacheProvider.notifier).cache(eventCountResultEntity);

return eventCountResultEntity.data.content as int;
} finally {
relay.unsubscribe(subscription.id);
}
}

Future<NostrRelay> _getRandomUserRelay() async {
final userRelays = await ref.read(currentUserRelayProvider.future);
if (userRelays == null) {
throw UserRelaysNotFoundException();
}

final relayUrl = userRelays.data.list.random.url;
return await ref.read(relayProvider(relayUrl).future);
}

Future<EventMessage> _buildRequestEvent({required String relayUrl}) async {
final followersCountRequest = EventCountRequestData(
relays: [relayUrl],
filters: [
RequestFilter(
kinds: const [FollowListEntity.kind],
authors: [pubkey],
),
],
);

return ref.read(ionConnectNotifierProvider.notifier).sign(followersCountRequest);
}
}

0 comments on commit 0db8977

Please sign in to comment.