relatica/lib/riverpod_controllers/circles_repo_services.dart

289 wiersze
9.3 KiB
Dart

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 'package:stack_trace/stack_trace.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', Trace.current());
},
).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();
}
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 {
// TODO retire old members provider?
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> 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',
Trace.current(),
);
},
);
}
}
@Riverpod(keepAlive: true)
class CircleMembers extends _$CircleMembers {
@override
List<Connection> build(Profile profile, TimelineGroupingListData circle) {
return ref
.read(_circlesRepoProvider(profile))
.getCircleMembers(circle)
.getValueOrElse(() => []);
}
FutureResult<bool, ExecError> addConnectionToCircle(
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);
_setState(ref
.read(_circlesRepoProvider(profile))
.getCircleMembers(circle)
.getValueOrElse(() => []));
}).mapError((error) {
_cpLogger.severe(
'Error adding ${connection.name} from circle: ${circle.name}',
Trace.current(),
);
return error;
});
}
FutureResult<bool, ExecError> removeConnectionFromCircle(
Connection connection) async {
return ref
.read(removeConnectionFromCircleProvider(profile, circle, connection)
.future)
.withResult((_) => ref
.read(_circlesRepoProvider(profile))
.removeConnectionFromCircle(circle, connection))
.withResult((_) => _setState(ref
.read(_circlesRepoProvider(profile))
.getCircleMembers(circle)
.getValueOrElse(() => [])))
.mapError(
(error) {
_cpLogger.severe(
'Error removing ${connection.name} from circle: ${circle.name}',
Trace.current(),
);
return error;
},
);
}
Future<void> refreshCircleMemberships() async {
_cpLogger.info('Refreshing Circle Memberships for ${circle.name}');
var page = const PagingData(limit: 50);
final allResults = <Connection>{};
var moreResults = true;
while (moreResults) {
await ref
.read(circleMembersNetworkProvider(profile, circle, page).future)
.match(onSuccess: (results) {
_cpLogger
.info('Received next set of memberships for Circle ${circle.name}');
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',
Trace.current(),
);
moreResults = false;
});
}
ref.read(_circlesRepoProvider(profile)).deleteCircle(circle);
ref.read(_circlesRepoProvider(profile)).upsertCircle(circle);
_cpLogger.info(
'Adding ${allResults.length} Updated Memberships for ${circle.name}');
for (final c in allResults) {
await ref
.read(connectionModifierProvider(profile, c).notifier)
.upsertConnection(c);
ref.read(_circlesRepoProvider(profile)).addConnectionToCircle(circle, c);
}
_setState(allResults.toList());
_cpLogger.info(
'Completed ${allResults.length} updating memberships for ${circle.name}');
}
void _setState(List<Connection> newState) {
newState.sort((c1, c2) => c1.name.compareTo(c2.name));
state = newState;
}
}
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;
}