relatica/lib/riverpod_controllers/circles_repo_services.dart

253 wiersze
8.4 KiB
Dart
Czysty Zwykły widok Historia

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
import 'package:result_monad/result_monad.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../data/interfaces/circles_repo_intf.dart';
import '../data/memory/memory_circles_repo.dart';
import '../models/auth/profile.dart';
import '../models/connection.dart';
import '../models/exec_error.dart';
import '../models/networking/paging_data.dart';
import '../models/timeline_grouping_list_data.dart';
import 'connection_manager_services.dart';
import 'networking/friendica_timeline_grouping_client_services.dart';
part 'circles_repo_services.g.dart';
final _crLogger = Logger('CirclesRepoProvider');
@Riverpod(keepAlive: true)
class _CirclesRepo extends _$CirclesRepo {
@override
ICirclesRepo build(Profile profile) {
_crLogger.info('Creating for $profile');
return MemoryCirclesRepo();
}
Future<Result<List<TimelineGroupingListData>, ExecError>>
refreshCircleData() async {
_crLogger.info('Refreshing member circle data ');
return await ref
.read(timelineGroupingListDataClientProvider(profile).future)
.andThen((circles) {
state.clearMyCircles();
state.addAllCircles(circles);
ref.notifyListeners();
return Result.ok(state.getMyCircles());
}).withError(
(error) {
_crLogger.severe('Error getting list data: $error');
},
).execErrorCastAsync();
}
}
final _cpLogger = Logger('CirclesProvider');
@Riverpod(keepAlive: true)
class Circles extends _$Circles {
@override
List<TimelineGroupingListData> build(Profile profile) {
_cpLogger.info('Creating for $profile');
profile = profile;
final circles = ref.watch(_circlesRepoProvider(profile)).getMyCircles();
if (circles.isEmpty) {
Future.delayed(const Duration(milliseconds: 1), refresh);
}
return circles;
}
Future<void> refresh() async {
_cpLogger.info('Refreshing circles provider');
await ref
.read(_circlesRepoProvider(profile).notifier)
.refreshCircleData()
.withResult((circles) => state = circles);
}
void upsertCircle(TimelineGroupingListData circle) {
ref.read(_circlesRepoProvider(profile)).upsertCircle(circle);
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
}
FutureResult<TimelineGroupingListData, ExecError> createCircle(
String newName) async {
final result = await ref
.read(createCircleProvider(profile, newName).future)
.withResult((newCircle) {
ref.read(_circlesRepoProvider(profile)).upsertCircle(newCircle);
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
});
return result.execErrorCast();
}
FutureResult<bool, ExecError> deleteCircle(
TimelineGroupingListData circleData) async {
final result = await ref
.read(deleteCircleProvider(profile, circleData).future)
.withResult((_) {
ref.read(_circlesRepoProvider(profile)).deleteCircle(circleData);
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
});
return result.execErrorCast();
}
void clear() {
ref.read(_circlesRepoProvider(profile)).clear();
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
}
void addAllCircles(List<TimelineGroupingListData> circles) {
ref.read(_circlesRepoProvider(profile)).addAllCircles(circles);
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
}
FutureResult<bool, ExecError> addConnectionToCircle(
TimelineGroupingListData circle,
Connection connection,
) async {
_cpLogger.finest('Adding ${connection.name} to circle: ${circle.name}');
return await ref
.read(addConnectionToCircleProvider(profile, circle, connection).future)
.withResult((_) => ref
.read(_circlesRepoProvider(profile))
.addConnectionToCircle(circle, connection))
.withResultAsync((_) async => await refreshCircleMemberships(circle))
.withResult((_) =>
state = ref.read(_circlesRepoProvider(profile)).getMyCircles())
.mapError((error) {
_cpLogger.severe(
'Error adding ${connection.name} from circle: ${circle.name}');
return error;
});
}
FutureResult<bool, ExecError> removeConnectionFromCircle(
TimelineGroupingListData circle, Connection connection) async {
return ref
.read(removeConnectionFromCircleProvider(profile, circle, connection)
.future)
.withResultAsync((_) async => await refreshCircleMemberships(circle))
.withResult((_) =>
state = ref.read(_circlesRepoProvider(profile)).getMyCircles())
.mapError(
(error) {
_cpLogger.severe(
'Error removing ${connection.name} from circle: ${circle.name}');
return error;
},
);
}
Result<List<Connection>, ExecError> getCircleMembers(
TimelineGroupingListData circle) =>
ref.read(_circlesRepoProvider(profile)).getCircleMembers(circle);
Result<List<TimelineGroupingListData>, ExecError> getCirclesForUser(
String id) {
final result =
ref.read(_circlesRepoProvider(profile)).getCirclesForUser(id);
if (result.isSuccess) {
_cpLogger.finer("Circles for user $id: $result");
return result;
}
if (result.isFailure && result.error.type != ErrorType.notFound) {
return result;
}
refreshConnectionCircleData(id, true);
return Result.ok([]);
}
bool updateConnectionCircleData(
String id, List<TimelineGroupingListData> currentCircles) =>
ref
.read(_circlesRepoProvider(profile))
.updateConnectionCircleData(id, currentCircles);
FutureResult<TimelineGroupingListData, ExecError> renameCircle(
String id, String newName) async {
final result = await ref
.read(renameCircleProvider(profile, id, newName).future)
.withResult((renamedCircle) {
ref.read(_circlesRepoProvider(profile)).upsertCircle(renamedCircle);
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
});
return result.execErrorCast();
}
Future<void> refreshCircleMemberships(TimelineGroupingListData circle) async {
var page = PagingData(limit: 50);
final allResults = <Connection>{};
var moreResults = true;
while (moreResults) {
await ref.read(circleMembersProvider(profile, circle, page).future).match(
onSuccess: (results) {
moreResults = results.data.isNotEmpty && results.next != null;
page = results.next ?? page;
allResults.addAll(results.data);
}, onError: (error) {
_cpLogger.severe('Error getting circle listing data: $error');
moreResults = false;
});
}
ref.read(_circlesRepoProvider(profile)).deleteCircle(circle);
ref.read(_circlesRepoProvider(profile)).upsertCircle(circle);
for (final c in allResults) {
await ref
.read(connectionModifierProvider(profile, c).notifier)
.upsertConnection(c);
ref.read(_circlesRepoProvider(profile)).addConnectionToCircle(circle, c);
}
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
}
Future<void> refreshConnectionCircleData(
String id, bool withNotification) async {
_cpLogger.finest('Refreshing member list data for Connection $id');
await ref
.read(memberCirclesForConnectionProvider(profile, id).future)
.match(
onSuccess: (circles) {
updateConnectionCircleData(id, circles);
if (withNotification) {
state = ref.read(_circlesRepoProvider(profile)).getMyCircles();
}
},
onError: (error) {
_cpLogger.severe('Error getting list data for $id: $error');
},
);
}
}
final _tglLogger = Logger('TimelineGroupingListProvider');
@riverpod
Result<TimelineGroupingListData, ExecError> circleData(
Ref ref, Profile profile, String id) {
final circles = ref.watch(circlesProvider(profile)).where((c) => c.id == id);
if (circles.isEmpty) {
return buildErrorResult(
type: ErrorType.notFound,
message: 'Circle not found: $id',
);
}
return Result.ok(circles.first);
}
@riverpod
List<TimelineGroupingListData> timelineGroupingList(
Ref ref, Profile profile, GroupingType type) {
_tglLogger.info('Creating for $type for $profile');
final circles = ref.watch(circlesProvider(profile));
final result = circles.where((e) => e.groupingType == type).toList();
result.sort((g1, g2) => g1.name.compareTo(g2.name));
return result;
}