kopia lustrzana https://gitlab.com/mysocialportal/relatica
289 wiersze
9.3 KiB
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;
|
|
}
|