Skip to content

Commit

Permalink
followers api
Browse files Browse the repository at this point in the history
  • Loading branch information
dinaraparanid committed Jan 4, 2025
1 parent 4caa48c commit d71ea96
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 2 deletions.
6 changes: 6 additions & 0 deletions lib/core/data/firestore/utils/firestore_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import 'package:cloud_firestore/cloud_firestore.dart';

extension FirestoreExt on Query<Map<String, dynamic>> {
Future<QueryDocumentSnapshot<Map<String, dynamic>>?> get firstDocOrNull =>
get().then((snapshots) => snapshots.docs.firstOrNull);
}
64 changes: 64 additions & 0 deletions lib/core/data/profile/data_source/follower_api_impl.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fpdart/fpdart.dart';
import 'package:poster/core/data/firestore/utils/firestore_ext.dart';
import 'package:poster/core/data/paging/firestore_paging.dart';
import 'package:poster/core/data/paging/page_mapper.dart';
import 'package:poster/core/domain/paging/page_data.dart';
import 'package:poster/core/domain/paging/paging_config.dart';
import 'package:poster/core/domain/profile/data_source/follower_api.dart';
import 'package:poster/core/domain/profile/entity/following_data.dart';
import 'package:poster/core/utils/functions/try_future.dart';

const _collectionFollowers = 'followers';

final class FollowerApiImpl with FollowerApi {
@override
Future<Either<Exception, FollowingData>> subscribe({
required String profileEmail,
required String followerEmail,
}) => tryFuture(() async {
final data = FollowingData(followerEmail: followerEmail, profileEmail: profileEmail);

await FirebaseFirestore.instance
.collection(_collectionFollowers)
.add(data.toJson());

return data;
});

@override
Future<Either<Exception, void>> unsubscribe({
required String profileEmail,
required String followerEmail,
}) => tryFuture(() async {
final subscribeDoc = await FirebaseFirestore.instance
.collection(_collectionFollowers)
.where(FollowingData.fieldProfileEmail, isEqualTo: profileEmail)
.where(FollowingData.fieldFollowerEmail, isEqualTo: followerEmail)
.firstDocOrNull;

await subscribeDoc?.reference.delete();
});

@override
Future<Either<Exception, PageData<FollowingData>>> subscribersPage({
required String profileEmail,
int page = PagingConfig.initialPage,
int perPage = PagingConfig.defaultPageSize,
}) => tryFuture(() async {
final snapshots = await FirebaseFirestore.instance
.collection(_collectionFollowers)
.where(FollowingData.fieldProfileEmail, isEqualTo: profileEmail)
.pageAt(page: page, perPage: perPage);

return snapshots.toPageData(
page: page,
perPage: perPage,
transform: (q) => q.toFollowingData(),
);
});
}

extension _FollowingMapper on QueryDocumentSnapshot<Map<String, dynamic>> {
FollowingData toFollowingData() => FollowingData.fromJson(data());
}
9 changes: 8 additions & 1 deletion lib/core/data/profile/di/profile_module.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'package:get_it/get_it.dart';
import 'package:poster/core/data/profile/data_source/follower_api_impl.dart';
import 'package:poster/core/data/profile/data_source/profile_api_impl.dart';
import 'package:poster/core/data/profile/data_source/profile_store_impl.dart';
import 'package:poster/core/data/profile/repository/profile_repository_impl.dart';
import 'package:poster/core/di/provide_singleton.dart';
import 'package:poster/core/domain/profile/data_source/follower_api.dart';
import 'package:poster/core/domain/profile/data_source/profile_api.dart';
import 'package:poster/core/domain/profile/data_source/profile_store.dart';
import 'package:poster/core/domain/profile/repository/profile_repository.dart';
Expand All @@ -11,6 +13,11 @@ extension ProfileModule on GetIt {
List<Type> registerProfileModule() => [
provideSingleton<ProfileApi>(() => ProfileApiImpl()),
provideSingleton<ProfileStore>(() => ProfileStoreImpl()),
provideSingleton<ProfileRepository>(() => ProfileRepositoryImpl(api: this(), store: this())),
provideSingleton<FollowerApi>(() => FollowerApiImpl()),
provideSingleton<ProfileRepository>(() => ProfileRepositoryImpl(
api: this(),
store: this(),
followerApi: this(),
)),
];
}
31 changes: 30 additions & 1 deletion lib/core/data/profile/repository/profile_repository_impl.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import 'package:fpdart/fpdart.dart';
import 'package:poster/core/domain/paging/page_data.dart';
import 'package:poster/core/domain/paging/paging_config.dart';
import 'package:poster/core/domain/profile/data_source/follower_api.dart';
import 'package:poster/core/domain/profile/data_source/profile_api.dart';
import 'package:poster/core/domain/profile/data_source/profile_store.dart';
import 'package:poster/core/domain/profile/entity/following_data.dart';
import 'package:poster/core/domain/profile/entity/profile.dart';
import 'package:poster/core/domain/profile/repository/profile_repository.dart';

final class ProfileRepositoryImpl extends ProfileRepository {
final ProfileApi _api;
final ProfileStore _store;
final FollowerApi _followerApi;

ProfileRepositoryImpl({
required ProfileApi api,
required ProfileStore store,
}) : _api = api, _store = store;
required FollowerApi followerApi,
}) : _api = api, _store = store, _followerApi = followerApi;

@override
Future<Profile?> get profile => _store.profile;
Expand All @@ -30,4 +36,27 @@ final class ProfileRepositoryImpl extends ProfileRepository {
_store.storeProfile(profile);
return Either.right(null);
}

@override
Future<Either<Exception, FollowingData>> subscribe({
required String profileEmail,
required String followerEmail,
}) => _followerApi.subscribe(profileEmail: profileEmail, followerEmail: followerEmail);

@override
Future<Either<Exception, void>> unsubscribe({
required String profileEmail,
required String followerEmail,
}) => _followerApi.unsubscribe(profileEmail: profileEmail, followerEmail: followerEmail);

@override
Future<Either<Exception, PageData<FollowingData>>> subscribersPage({
required String profileEmail,
int page = PagingConfig.initialPage,
int perPage = PagingConfig.defaultPageSize,
}) => _followerApi.subscribersPage(
profileEmail: profileEmail,
page: page,
perPage: perPage,
);
}
22 changes: 22 additions & 0 deletions lib/core/domain/profile/data_source/follower_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:fpdart/fpdart.dart';
import 'package:poster/core/domain/paging/page_data.dart';
import 'package:poster/core/domain/paging/paging_config.dart';
import 'package:poster/core/domain/profile/entity/following_data.dart';

mixin FollowerApi {
Future<Either<Exception, FollowingData>> subscribe({
required String profileEmail,
required String followerEmail,
});

Future<Either<Exception, void>> unsubscribe({
required String profileEmail,
required String followerEmail,
});

Future<Either<Exception, PageData<FollowingData>>> subscribersPage({
required String profileEmail,
int page = PagingConfig.initialPage,
int perPage = PagingConfig.defaultPageSize,
});
}
18 changes: 18 additions & 0 deletions lib/core/domain/profile/entity/following_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:freezed_annotation/freezed_annotation.dart';

part 'following_data.freezed.dart';
part 'following_data.g.dart';

@freezed
abstract class FollowingData with _$FollowingData {
static const fieldFollowerEmail = 'follower_email';
static const fieldProfileEmail = 'profile_email';

const factory FollowingData({
@JsonKey(name: FollowingData.fieldFollowerEmail) required String followerEmail,
@JsonKey(name: FollowingData.fieldProfileEmail) required String profileEmail,
}) = _FollowingData;

factory FollowingData.fromJson(Map<String, dynamic> json) =>
_$FollowingDataFromJson(json);
}
19 changes: 19 additions & 0 deletions lib/core/domain/profile/repository/profile_repository.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import 'package:fpdart/fpdart.dart';
import 'package:poster/core/domain/paging/page_data.dart';
import 'package:poster/core/domain/paging/paging_config.dart';
import 'package:poster/core/domain/profile/entity/following_data.dart';
import 'package:poster/core/domain/profile/entity/profile.dart';

abstract class ProfileRepository {
Expand All @@ -10,4 +13,20 @@ abstract class ProfileRepository {
});

Future<Either<Exception, void>> saveProfile({required String email});

Future<Either<Exception, FollowingData>> subscribe({
required String profileEmail,
required String followerEmail,
});

Future<Either<Exception, void>> unsubscribe({
required String profileEmail,
required String followerEmail,
});

Future<Either<Exception, PageData<FollowingData>>> subscribersPage({
required String profileEmail,
int page = PagingConfig.initialPage,
int perPage = PagingConfig.defaultPageSize,
});
}
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ dependencies:
cloudinary: ^1.2.0
envied: ^1.0.0
image_picker: ^1.1.2
super_paging: ^0.2.0

dev_dependencies:
flutter_test:
Expand Down

0 comments on commit d71ea96

Please sign in to comment.