mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 13:33:32 +00:00
Merge branch 'rename-groups-to-circles' into 'main'
Renaming groups to circles everywhere in code and labels See merge request mysocialportal/relatica!46
This commit is contained in:
commit
b39b5673dc
25 changed files with 472 additions and 472 deletions
|
@ -86,8 +86,8 @@ class StandardAppDrawer extends StatelessWidget {
|
||||||
),
|
),
|
||||||
buildMenuButton(
|
buildMenuButton(
|
||||||
context,
|
context,
|
||||||
'Groups Management',
|
'Circles Management',
|
||||||
() => context.pushNamed(ScreenPaths.groupManagement),
|
() => context.pushNamed(ScreenPaths.circleManagement),
|
||||||
),
|
),
|
||||||
buildMenuButton(
|
buildMenuButton(
|
||||||
context,
|
context,
|
||||||
|
|
27
lib/data/interfaces/circles_repo_intf.dart
Normal file
27
lib/data/interfaces/circles_repo_intf.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
|
import '../../models/circle_data.dart';
|
||||||
|
import '../../models/connection.dart';
|
||||||
|
import '../../models/exec_error.dart';
|
||||||
|
|
||||||
|
abstract class ICirclesRepo {
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void addAllCircles(List<CircleData> circles);
|
||||||
|
|
||||||
|
void addConnectionToCircle(CircleData circle, Connection connection);
|
||||||
|
|
||||||
|
void clearMyCircles();
|
||||||
|
|
||||||
|
void upsertCircle(CircleData circle);
|
||||||
|
|
||||||
|
void deleteCircle(CircleData circle);
|
||||||
|
|
||||||
|
List<CircleData> getMyCircles();
|
||||||
|
|
||||||
|
Result<List<Connection>, ExecError> getCircleMembers(CircleData circle);
|
||||||
|
|
||||||
|
Result<List<CircleData>, ExecError> getCirclesForUser(String id);
|
||||||
|
|
||||||
|
bool updateConnectionCircleData(String id, List<CircleData> currentCircless);
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
import 'package:result_monad/result_monad.dart';
|
|
||||||
|
|
||||||
import '../../models/connection.dart';
|
|
||||||
import '../../models/exec_error.dart';
|
|
||||||
import '../../models/group_data.dart';
|
|
||||||
|
|
||||||
abstract class IGroupsRepo {
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
void addAllGroups(List<GroupData> groups);
|
|
||||||
|
|
||||||
void addConnectionToGroup(GroupData group, Connection connection);
|
|
||||||
|
|
||||||
void clearMyGroups();
|
|
||||||
|
|
||||||
void upsertGroup(GroupData group);
|
|
||||||
|
|
||||||
void deleteGroup(GroupData group);
|
|
||||||
|
|
||||||
List<GroupData> getMyGroups();
|
|
||||||
|
|
||||||
Result<List<Connection>, ExecError> getGroupMembers(GroupData group);
|
|
||||||
|
|
||||||
Result<List<GroupData>, ExecError> getGroupsForUser(String id);
|
|
||||||
|
|
||||||
bool updateConnectionGroupData(String id, List<GroupData> currentGroups);
|
|
||||||
}
|
|
86
lib/data/memory/memory_circles_repo.dart
Normal file
86
lib/data/memory/memory_circles_repo.dart
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
|
import '../../models/circle_data.dart';
|
||||||
|
import '../../models/connection.dart';
|
||||||
|
import '../../models/exec_error.dart';
|
||||||
|
import '../interfaces/circles_repo_intf.dart';
|
||||||
|
|
||||||
|
class MemoryCirclesRepo implements ICirclesRepo {
|
||||||
|
final _circlesForConnection = <String, List<CircleData>>{};
|
||||||
|
final _connectionsForCircle = <String, Set<Connection>>{};
|
||||||
|
final _myCircles = <CircleData>{};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clear() {
|
||||||
|
_circlesForConnection.clear();
|
||||||
|
_connectionsForCircle.clear();
|
||||||
|
_myCircles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Result<List<CircleData>, ExecError> getCirclesForUser(String id) {
|
||||||
|
if (!_circlesForConnection.containsKey(id)) {
|
||||||
|
return Result.error(ExecError(
|
||||||
|
type: ErrorType.notFound,
|
||||||
|
message: '$id not a known user ID',
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(_circlesForConnection[id]!);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<CircleData> getMyCircles() {
|
||||||
|
return _myCircles.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Result<List<Connection>, ExecError> getCircleMembers(CircleData circle) {
|
||||||
|
if (_connectionsForCircle.containsKey(circle.id)) {
|
||||||
|
return Result.ok(_connectionsForCircle[circle.id]!.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildErrorResult(
|
||||||
|
type: ErrorType.notFound,
|
||||||
|
message: 'Circle ${circle.id} not found',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void clearMyCircles() {
|
||||||
|
_myCircles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addConnectionToCircle(CircleData circle, Connection connection) {
|
||||||
|
_connectionsForCircle.putIfAbsent(circle.id, () => {}).add(connection);
|
||||||
|
_circlesForConnection[connection.id]?.add(circle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void addAllCircles(List<CircleData> circle) {
|
||||||
|
_myCircles.addAll(circle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool updateConnectionCircleData(String id, List<CircleData> currentCircles) {
|
||||||
|
_circlesForConnection[id] = currentCircles;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void upsertCircle(CircleData circle) {
|
||||||
|
_connectionsForCircle.putIfAbsent(circle.id, () => {});
|
||||||
|
_myCircles.remove(circle);
|
||||||
|
_myCircles.add(circle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void deleteCircle(CircleData circle) {
|
||||||
|
for (final conCircles in _circlesForConnection.values) {
|
||||||
|
conCircles.remove(circle);
|
||||||
|
}
|
||||||
|
_connectionsForCircle.remove(circle.id);
|
||||||
|
_myCircles.remove(circle);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,86 +0,0 @@
|
||||||
import 'package:result_monad/result_monad.dart';
|
|
||||||
|
|
||||||
import '../../models/connection.dart';
|
|
||||||
import '../../models/exec_error.dart';
|
|
||||||
import '../../models/group_data.dart';
|
|
||||||
import '../interfaces/groups_repo.intf.dart';
|
|
||||||
|
|
||||||
class MemoryGroupsRepo implements IGroupsRepo {
|
|
||||||
final _groupsForConnection = <String, List<GroupData>>{};
|
|
||||||
final _connectionsForGroup = <String, Set<Connection>>{};
|
|
||||||
final _myGroups = <GroupData>{};
|
|
||||||
|
|
||||||
@override
|
|
||||||
void clear() {
|
|
||||||
_groupsForConnection.clear();
|
|
||||||
_connectionsForGroup.clear();
|
|
||||||
_myGroups.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Result<List<GroupData>, ExecError> getGroupsForUser(String id) {
|
|
||||||
if (!_groupsForConnection.containsKey(id)) {
|
|
||||||
return Result.error(ExecError(
|
|
||||||
type: ErrorType.notFound,
|
|
||||||
message: '$id not a known user ID',
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.ok(_groupsForConnection[id]!);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<GroupData> getMyGroups() {
|
|
||||||
return _myGroups.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Result<List<Connection>, ExecError> getGroupMembers(GroupData group) {
|
|
||||||
if (_connectionsForGroup.containsKey(group.id)) {
|
|
||||||
return Result.ok(_connectionsForGroup[group.id]!.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildErrorResult(
|
|
||||||
type: ErrorType.notFound,
|
|
||||||
message: 'Group ${group.id} not found',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void clearMyGroups() {
|
|
||||||
_myGroups.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void addConnectionToGroup(GroupData group, Connection connection) {
|
|
||||||
_connectionsForGroup.putIfAbsent(group.id, () => {}).add(connection);
|
|
||||||
_groupsForConnection[connection.id]?.add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void addAllGroups(List<GroupData> groups) {
|
|
||||||
_myGroups.addAll(groups);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool updateConnectionGroupData(String id, List<GroupData> currentGroups) {
|
|
||||||
_groupsForConnection[id] = currentGroups;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void upsertGroup(GroupData group) {
|
|
||||||
_connectionsForGroup.putIfAbsent(group.id, () => {});
|
|
||||||
_myGroups.remove(group);
|
|
||||||
_myGroups.add(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void deleteGroup(GroupData group) {
|
|
||||||
for (final conGroups in _groupsForConnection.values) {
|
|
||||||
conGroups.remove(group);
|
|
||||||
}
|
|
||||||
_connectionsForGroup.remove(group.id);
|
|
||||||
_myGroups.remove(group);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,10 +4,10 @@ import 'package:logging/logging.dart';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
import 'data/interfaces/circles_repo_intf.dart';
|
||||||
import 'data/interfaces/connections_repo_intf.dart';
|
import 'data/interfaces/connections_repo_intf.dart';
|
||||||
import 'data/interfaces/groups_repo.intf.dart';
|
|
||||||
import 'data/interfaces/hashtag_repo_intf.dart';
|
import 'data/interfaces/hashtag_repo_intf.dart';
|
||||||
import 'data/memory/memory_groups_repo.dart';
|
import 'data/memory/memory_circles_repo.dart';
|
||||||
import 'data/objectbox/objectbox_cache.dart';
|
import 'data/objectbox/objectbox_cache.dart';
|
||||||
import 'data/objectbox/objectbox_connections_repo.dart';
|
import 'data/objectbox/objectbox_connections_repo.dart';
|
||||||
import 'data/objectbox/objectbox_hashtag_repo.dart';
|
import 'data/objectbox/objectbox_hashtag_repo.dart';
|
||||||
|
@ -96,8 +96,8 @@ Future<void> dependencyInjectionInitialization() async {
|
||||||
ActiveProfileSelector((p) => ReshareViaService())
|
ActiveProfileSelector((p) => ReshareViaService())
|
||||||
..subscribeToProfileSwaps());
|
..subscribeToProfileSwaps());
|
||||||
|
|
||||||
getIt.registerSingleton<ActiveProfileSelector<IGroupsRepo>>(
|
getIt.registerSingleton<ActiveProfileSelector<ICirclesRepo>>(
|
||||||
ActiveProfileSelector((p) => MemoryGroupsRepo())
|
ActiveProfileSelector((p) => MemoryCirclesRepo())
|
||||||
..subscribeToProfileSwaps());
|
..subscribeToProfileSwaps());
|
||||||
|
|
||||||
getIt.registerSingleton<ActiveProfileSelector<ConnectionsManager>>(
|
getIt.registerSingleton<ActiveProfileSelector<ConnectionsManager>>(
|
||||||
|
@ -105,7 +105,7 @@ Future<void> dependencyInjectionInitialization() async {
|
||||||
(p) => ConnectionsManager(
|
(p) => ConnectionsManager(
|
||||||
p,
|
p,
|
||||||
getIt<ActiveProfileSelector<IConnectionsRepo>>().getForProfile(p).value,
|
getIt<ActiveProfileSelector<IConnectionsRepo>>().getForProfile(p).value,
|
||||||
getIt<ActiveProfileSelector<IGroupsRepo>>().getForProfile(p).value,
|
getIt<ActiveProfileSelector<ICirclesRepo>>().getForProfile(p).value,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ Future<void> dependencyInjectionInitialization() async {
|
||||||
getIt.registerSingleton<ActiveProfileSelector<TimelineManager>>(
|
getIt.registerSingleton<ActiveProfileSelector<TimelineManager>>(
|
||||||
ActiveProfileSelector((p) => TimelineManager(
|
ActiveProfileSelector((p) => TimelineManager(
|
||||||
p,
|
p,
|
||||||
getIt<ActiveProfileSelector<IGroupsRepo>>().getForProfile(p).value,
|
getIt<ActiveProfileSelector<ICirclesRepo>>().getForProfile(p).value,
|
||||||
getIt<ActiveProfileSelector<EntryManagerService>>()
|
getIt<ActiveProfileSelector<EntryManagerService>>()
|
||||||
.getForProfile(p)
|
.getForProfile(p)
|
||||||
.value,
|
.value,
|
||||||
|
|
|
@ -10,12 +10,12 @@ import '../friendica_client/paged_response.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../models/TimelineIdentifiers.dart';
|
import '../models/TimelineIdentifiers.dart';
|
||||||
import '../models/auth/profile.dart';
|
import '../models/auth/profile.dart';
|
||||||
|
import '../models/circle_data.dart';
|
||||||
import '../models/connection.dart';
|
import '../models/connection.dart';
|
||||||
import '../models/direct_message.dart';
|
import '../models/direct_message.dart';
|
||||||
import '../models/exec_error.dart';
|
import '../models/exec_error.dart';
|
||||||
import '../models/follow_request.dart';
|
import '../models/follow_request.dart';
|
||||||
import '../models/gallery_data.dart';
|
import '../models/gallery_data.dart';
|
||||||
import '../models/group_data.dart';
|
|
||||||
import '../models/image_entry.dart';
|
import '../models/image_entry.dart';
|
||||||
import '../models/instance_info.dart';
|
import '../models/instance_info.dart';
|
||||||
import '../models/media_attachment_uploads/image_types_enum.dart';
|
import '../models/media_attachment_uploads/image_types_enum.dart';
|
||||||
|
@ -28,9 +28,9 @@ import '../serializers/friendica/direct_message_friendica_extensions.dart';
|
||||||
import '../serializers/friendica/gallery_data_friendica_extensions.dart';
|
import '../serializers/friendica/gallery_data_friendica_extensions.dart';
|
||||||
import '../serializers/friendica/image_entry_friendica_extensions.dart';
|
import '../serializers/friendica/image_entry_friendica_extensions.dart';
|
||||||
import '../serializers/friendica/visibility_friendica_extensions.dart';
|
import '../serializers/friendica/visibility_friendica_extensions.dart';
|
||||||
|
import '../serializers/mastodon/circle_data_mastodon_extensions.dart';
|
||||||
import '../serializers/mastodon/connection_mastodon_extensions.dart';
|
import '../serializers/mastodon/connection_mastodon_extensions.dart';
|
||||||
import '../serializers/mastodon/follow_request_mastodon_extensions.dart';
|
import '../serializers/mastodon/follow_request_mastodon_extensions.dart';
|
||||||
import '../serializers/mastodon/group_data_mastodon_extensions.dart';
|
|
||||||
import '../serializers/mastodon/instance_info_mastodon_extensions.dart';
|
import '../serializers/mastodon/instance_info_mastodon_extensions.dart';
|
||||||
import '../serializers/mastodon/notification_mastodon_extension.dart';
|
import '../serializers/mastodon/notification_mastodon_extension.dart';
|
||||||
import '../serializers/mastodon/search_result_mastodon_extensions.dart';
|
import '../serializers/mastodon/search_result_mastodon_extensions.dart';
|
||||||
|
@ -168,32 +168,33 @@ class GalleryClient extends FriendicaClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupsClient extends FriendicaClient {
|
class CirclesClient extends FriendicaClient {
|
||||||
static final _logger = Logger('$GroupsClient');
|
static final _logger = Logger('$CirclesClient');
|
||||||
|
|
||||||
GroupsClient(super.credentials) : super();
|
CirclesClient(super.credentials) : super();
|
||||||
|
|
||||||
FutureResult<List<GroupData>, ExecError> getGroups() async {
|
FutureResult<List<CircleData>, ExecError> getCircles() async {
|
||||||
_logger.finest(() => 'Getting group (Mastodon List) data');
|
_logger.finest(() => 'Getting circle (Mastodon List) data');
|
||||||
final url = 'https://$serverName/api/v1/lists';
|
final url = 'https://$serverName/api/v1/lists';
|
||||||
final request = Uri.parse(url);
|
final request = Uri.parse(url);
|
||||||
return (await _getApiListRequest(request).andThenSuccessAsync(
|
return (await _getApiListRequest(request).andThenSuccessAsync(
|
||||||
(listsJson) async => listsJson.data
|
(listsJson) async => listsJson.data
|
||||||
.map((json) => GroupDataMastodonExtensions.fromJson(json))
|
.map((json) => CircleDataMastodonExtensions.fromJson(json))
|
||||||
.toList()))
|
.toList()))
|
||||||
.mapError((error) => error is ExecError
|
.mapError((error) => error is ExecError
|
||||||
? error
|
? error
|
||||||
: ExecError(type: ErrorType.localError, message: error.toString()));
|
: ExecError(type: ErrorType.localError, message: error.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<PagedResponse<List<Connection>>, ExecError> getGroupMembers(
|
FutureResult<PagedResponse<List<Connection>>, ExecError> getCircleMembers(
|
||||||
GroupData groupData,
|
CircleData circleData,
|
||||||
PagingData page,
|
PagingData page,
|
||||||
) async {
|
) async {
|
||||||
_networkStatusService.startConnectionUpdateStatus();
|
_networkStatusService.startConnectionUpdateStatus();
|
||||||
_logger.finest(() =>
|
_logger.finest(() =>
|
||||||
'Getting members for group (Mastodon List) of name ${groupData.name} with paging: $page');
|
'Getting members for circle (Mastodon List) of name ${circleData.name} with paging: $page');
|
||||||
final baseUrl = 'https://$serverName/api/v1/lists/${groupData.id}/accounts';
|
final baseUrl =
|
||||||
|
'https://$serverName/api/v1/lists/${circleData.id}/accounts';
|
||||||
final url = Uri.parse('$baseUrl?${page.toQueryParameters()}');
|
final url = Uri.parse('$baseUrl?${page.toQueryParameters()}');
|
||||||
final result = await _getApiPagedRequest(url);
|
final result = await _getApiPagedRequest(url);
|
||||||
_networkStatusService.finishConnectionUpdateStatus();
|
_networkStatusService.finishConnectionUpdateStatus();
|
||||||
|
@ -205,8 +206,8 @@ class GroupsClient extends FriendicaClient {
|
||||||
.execErrorCast();
|
.execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<GroupData, ExecError> createGroup(String title) async {
|
FutureResult<CircleData, ExecError> createCircle(String title) async {
|
||||||
_logger.finest(() => 'Creating group (Mastodon List) of name $title');
|
_logger.finest(() => 'Creating circle (Mastodon List) of name $title');
|
||||||
final url = 'https://$serverName/api/v1/lists';
|
final url = 'https://$serverName/api/v1/lists';
|
||||||
final body = {
|
final body = {
|
||||||
'title': title,
|
'title': title,
|
||||||
|
@ -215,14 +216,14 @@ class GroupsClient extends FriendicaClient {
|
||||||
Uri.parse(url),
|
Uri.parse(url),
|
||||||
body,
|
body,
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
).andThenSuccessAsync(
|
).andThenSuccessAsync((data) async =>
|
||||||
(data) async => GroupDataMastodonExtensions.fromJson(jsonDecode(data)));
|
CircleDataMastodonExtensions.fromJson(jsonDecode(data)));
|
||||||
return result.execErrorCast();
|
return result.execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<GroupData, ExecError> renameGroup(
|
FutureResult<CircleData, ExecError> renameCircle(
|
||||||
String id, String title) async {
|
String id, String title) async {
|
||||||
_logger.finest(() => 'Reanming group (Mastodon List) to name $title');
|
_logger.finest(() => 'Reanming circle (Mastodon List) to name $title');
|
||||||
final url = 'https://$serverName/api/v1/lists/$id';
|
final url = 'https://$serverName/api/v1/lists/$id';
|
||||||
final body = {
|
final body = {
|
||||||
'title': title,
|
'title': title,
|
||||||
|
@ -233,38 +234,38 @@ class GroupsClient extends FriendicaClient {
|
||||||
headers: _headers,
|
headers: _headers,
|
||||||
).andThenSuccessAsync((data) async {
|
).andThenSuccessAsync((data) async {
|
||||||
final json = jsonDecode(data);
|
final json = jsonDecode(data);
|
||||||
return GroupDataMastodonExtensions.fromJson(json);
|
return CircleDataMastodonExtensions.fromJson(json);
|
||||||
});
|
});
|
||||||
return result.execErrorCast();
|
return result.execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<bool, ExecError> deleteGroup(GroupData groupData) async {
|
FutureResult<bool, ExecError> deleteCircle(CircleData circleData) async {
|
||||||
_logger.finest(
|
_logger.finest(
|
||||||
() => 'Reanming group (Mastodon List) to name ${groupData.name}');
|
() => 'Reanming circle (Mastodon List) to name ${circleData.name}');
|
||||||
final url = 'https://$serverName/api/v1/lists/${groupData.id}';
|
final url = 'https://$serverName/api/v1/lists/${circleData.id}';
|
||||||
final result = await deleteUrl(Uri.parse(url), {}, headers: _headers);
|
final result = await deleteUrl(Uri.parse(url), {}, headers: _headers);
|
||||||
return result.mapValue((_) => true).execErrorCast();
|
return result.mapValue((_) => true).execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<List<GroupData>, ExecError> getMemberGroupsForConnection(
|
FutureResult<List<CircleData>, ExecError> getMemberCirclesForConnection(
|
||||||
String connectionId) async {
|
String connectionId) async {
|
||||||
_logger.finest(() =>
|
_logger.finest(() =>
|
||||||
'Getting groups (Mastodon Lists) containing connection: $connectionId');
|
'Getting circles (Mastodon Lists) containing connection: $connectionId');
|
||||||
final url = 'https://$serverName/api/v1/accounts/$connectionId/lists';
|
final url = 'https://$serverName/api/v1/accounts/$connectionId/lists';
|
||||||
final request = Uri.parse(url);
|
final request = Uri.parse(url);
|
||||||
return (await _getApiListRequest(request).andThenSuccessAsync(
|
return (await _getApiListRequest(request).andThenSuccessAsync(
|
||||||
(listsJson) async => listsJson.data
|
(listsJson) async => listsJson.data
|
||||||
.map((json) => GroupDataMastodonExtensions.fromJson(json))
|
.map((json) => CircleDataMastodonExtensions.fromJson(json))
|
||||||
.toList()))
|
.toList()))
|
||||||
.mapError((error) => error as ExecError);
|
.mapError((error) => error as ExecError);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<bool, ExecError> addConnectionToGroup(
|
FutureResult<bool, ExecError> addConnectionToCircle(
|
||||||
GroupData group,
|
CircleData circle,
|
||||||
Connection connection,
|
Connection connection,
|
||||||
) async {
|
) async {
|
||||||
_logger.finest(() => 'Adding connection to group');
|
_logger.finest(() => 'Adding connection to circle');
|
||||||
final url = 'https://$serverName/api/v1/lists/${group.id}/accounts';
|
final url = 'https://$serverName/api/v1/lists/${circle.id}/accounts';
|
||||||
final request = Uri.parse(url);
|
final request = Uri.parse(url);
|
||||||
final requestData = {
|
final requestData = {
|
||||||
'account_ids': [connection.id]
|
'account_ids': [connection.id]
|
||||||
|
@ -273,12 +274,12 @@ class GroupsClient extends FriendicaClient {
|
||||||
.mapValue((_) => true);
|
.mapValue((_) => true);
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<bool, ExecError> removeConnectionFromGroup(
|
FutureResult<bool, ExecError> removeConnectionFromCircle(
|
||||||
GroupData group,
|
CircleData circle,
|
||||||
Connection connection,
|
Connection connection,
|
||||||
) async {
|
) async {
|
||||||
_logger.finest(() => 'Adding connection to group');
|
_logger.finest(() => 'Adding connection to circle');
|
||||||
final url = 'https://$serverName/api/v1/lists/${group.id}/accounts';
|
final url = 'https://$serverName/api/v1/lists/${circle.id}/accounts';
|
||||||
final request = Uri.parse(url);
|
final request = Uri.parse(url);
|
||||||
final requestData = {
|
final requestData = {
|
||||||
'account_ids': [connection.id]
|
'account_ids': [connection.id]
|
||||||
|
@ -549,7 +550,7 @@ class RelationshipsClient extends FriendicaClient {
|
||||||
|
|
||||||
FutureResult<Connection, ExecError> getConnectionWithStatus(
|
FutureResult<Connection, ExecError> getConnectionWithStatus(
|
||||||
Connection connection) async {
|
Connection connection) async {
|
||||||
_logger.finest(() => 'Getting group (Mastodon List) data');
|
_logger.finest(() => 'Getting circle (Mastodon List) data');
|
||||||
_networkStatusService.startConnectionUpdateStatus();
|
_networkStatusService.startConnectionUpdateStatus();
|
||||||
final myId = profile.userId;
|
final myId = profile.userId;
|
||||||
final id = int.parse(connection.id);
|
final id = int.parse(connection.id);
|
||||||
|
@ -1044,7 +1045,7 @@ class TimelineClient extends FriendicaClient {
|
||||||
return 'timelines/public';
|
return 'timelines/public';
|
||||||
case TimelineType.local:
|
case TimelineType.local:
|
||||||
return 'timelines/public';
|
return 'timelines/public';
|
||||||
case TimelineType.group:
|
case TimelineType.circle:
|
||||||
return 'timelines/list/${type.auxData}';
|
return 'timelines/list/${type.auxData}';
|
||||||
case TimelineType.profile:
|
case TimelineType.profile:
|
||||||
return '/accounts/${type.auxData}/statuses';
|
return '/accounts/${type.auxData}/statuses';
|
||||||
|
@ -1059,7 +1060,7 @@ class TimelineClient extends FriendicaClient {
|
||||||
case TimelineType.home:
|
case TimelineType.home:
|
||||||
case TimelineType.global:
|
case TimelineType.global:
|
||||||
case TimelineType.profile:
|
case TimelineType.profile:
|
||||||
case TimelineType.group:
|
case TimelineType.circle:
|
||||||
case TimelineType.self:
|
case TimelineType.self:
|
||||||
return '';
|
return '';
|
||||||
case TimelineType.local:
|
case TimelineType.local:
|
||||||
|
|
|
@ -2,7 +2,7 @@ enum TimelineType {
|
||||||
home,
|
home,
|
||||||
global,
|
global,
|
||||||
local,
|
local,
|
||||||
group,
|
circle,
|
||||||
profile,
|
profile,
|
||||||
self;
|
self;
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ enum TimelineType {
|
||||||
return 'Global Fediverse';
|
return 'Global Fediverse';
|
||||||
case TimelineType.local:
|
case TimelineType.local:
|
||||||
return 'Local Fediverse';
|
return 'Local Fediverse';
|
||||||
case TimelineType.group:
|
case TimelineType.circle:
|
||||||
return 'Groups (Lists)';
|
return 'Circles';
|
||||||
case TimelineType.profile:
|
case TimelineType.profile:
|
||||||
return 'Profile';
|
return 'Profile';
|
||||||
case TimelineType.self:
|
case TimelineType.self:
|
||||||
|
|
22
lib/models/circle_data.dart
Normal file
22
lib/models/circle_data.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
class CircleData {
|
||||||
|
static final followersPseudoCircle = CircleData('~', 'Followers');
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
CircleData(this.id, this.name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'CircleData{id: $id, name: $name}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) =>
|
||||||
|
identical(this, other) ||
|
||||||
|
other is CircleData && runtimeType == other.runtimeType && id == other.id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => id.hashCode;
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
class GroupData {
|
|
||||||
static final followersPseudoGroup = GroupData('~', 'Followers');
|
|
||||||
|
|
||||||
final String id;
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
|
|
||||||
GroupData(this.id, this.name);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'GroupData{id: $id, name: $name}';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) =>
|
|
||||||
identical(this, other) ||
|
|
||||||
other is GroupData && runtimeType == other.runtimeType && id == other.id;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => id.hashCode;
|
|
||||||
}
|
|
|
@ -20,50 +20,48 @@ class Visibility {
|
||||||
|
|
||||||
final List<String> excludedUserIds;
|
final List<String> excludedUserIds;
|
||||||
|
|
||||||
final List<String> allowedGroupIds;
|
final List<String> allowedCircleIds;
|
||||||
|
|
||||||
final List<String> excludedGroupIds;
|
final List<String> excludedCircleIds;
|
||||||
|
|
||||||
bool get hasDetails =>
|
bool get hasDetails =>
|
||||||
allowedUserIds.isNotEmpty ||
|
allowedUserIds.isNotEmpty ||
|
||||||
excludedUserIds.isNotEmpty ||
|
excludedUserIds.isNotEmpty ||
|
||||||
allowedGroupIds.isNotEmpty ||
|
allowedCircleIds.isNotEmpty ||
|
||||||
excludedGroupIds.isNotEmpty;
|
excludedCircleIds.isNotEmpty;
|
||||||
|
|
||||||
const Visibility({
|
const Visibility({
|
||||||
required this.type,
|
required this.type,
|
||||||
this.allowedUserIds = const [],
|
this.allowedUserIds = const [],
|
||||||
this.excludedUserIds = const [],
|
this.excludedUserIds = const [],
|
||||||
this.allowedGroupIds = const [],
|
this.allowedCircleIds = const [],
|
||||||
this.excludedGroupIds = const [],
|
this.excludedCircleIds = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Visibility.public() =>
|
factory Visibility.public() => const Visibility(
|
||||||
const Visibility(
|
|
||||||
type: VisibilityType.public,
|
type: VisibilityType.public,
|
||||||
);
|
);
|
||||||
|
|
||||||
factory Visibility.private() =>
|
factory Visibility.private() => const Visibility(
|
||||||
const Visibility(
|
|
||||||
type: VisibilityType.private,
|
type: VisibilityType.private,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) =>
|
bool operator ==(Object other) =>
|
||||||
identical(this, other) ||
|
identical(this, other) ||
|
||||||
other is Visibility &&
|
other is Visibility &&
|
||||||
runtimeType == other.runtimeType &&
|
runtimeType == other.runtimeType &&
|
||||||
type == other.type &&
|
type == other.type &&
|
||||||
allowedUserIds == other.allowedUserIds &&
|
allowedUserIds == other.allowedUserIds &&
|
||||||
excludedUserIds == other.excludedUserIds &&
|
excludedUserIds == other.excludedUserIds &&
|
||||||
allowedGroupIds == other.allowedGroupIds &&
|
allowedCircleIds == other.allowedCircleIds &&
|
||||||
excludedGroupIds == other.excludedGroupIds;
|
excludedCircleIds == other.excludedCircleIds;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
type.hashCode ^
|
type.hashCode ^
|
||||||
allowedUserIds.hashCode ^
|
allowedUserIds.hashCode ^
|
||||||
excludedUserIds.hashCode ^
|
excludedUserIds.hashCode ^
|
||||||
allowedGroupIds.hashCode ^
|
allowedCircleIds.hashCode ^
|
||||||
excludedGroupIds.hashCode;
|
excludedCircleIds.hashCode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,10 @@ import 'package:go_router/go_router.dart';
|
||||||
import 'globals.dart';
|
import 'globals.dart';
|
||||||
import 'models/interaction_type_enum.dart';
|
import 'models/interaction_type_enum.dart';
|
||||||
import 'screens/blocks_screen.dart';
|
import 'screens/blocks_screen.dart';
|
||||||
|
import 'screens/circle_add_users_screen.dart';
|
||||||
|
import 'screens/circle_create_screen.dart';
|
||||||
|
import 'screens/circle_editor_screen.dart';
|
||||||
|
import 'screens/circle_management_screen.dart';
|
||||||
import 'screens/contacts_screen.dart';
|
import 'screens/contacts_screen.dart';
|
||||||
import 'screens/editor.dart';
|
import 'screens/editor.dart';
|
||||||
import 'screens/filter_editor_screen.dart';
|
import 'screens/filter_editor_screen.dart';
|
||||||
|
@ -10,10 +14,6 @@ import 'screens/filters_screen.dart';
|
||||||
import 'screens/follow_request_adjudication_screen.dart';
|
import 'screens/follow_request_adjudication_screen.dart';
|
||||||
import 'screens/gallery_browsers_screen.dart';
|
import 'screens/gallery_browsers_screen.dart';
|
||||||
import 'screens/gallery_screen.dart';
|
import 'screens/gallery_screen.dart';
|
||||||
import 'screens/group_add_users_screen.dart';
|
|
||||||
import 'screens/group_create_screen.dart';
|
|
||||||
import 'screens/group_editor_screen.dart';
|
|
||||||
import 'screens/group_management_screen.dart';
|
|
||||||
import 'screens/home.dart';
|
import 'screens/home.dart';
|
||||||
import 'screens/image_editor_screen.dart';
|
import 'screens/image_editor_screen.dart';
|
||||||
import 'screens/interactions_viewer_screen.dart';
|
import 'screens/interactions_viewer_screen.dart';
|
||||||
|
@ -44,7 +44,7 @@ class ScreenPaths {
|
||||||
static String notifications = '/notifications';
|
static String notifications = '/notifications';
|
||||||
static String signin = '/signin';
|
static String signin = '/signin';
|
||||||
static String manageProfiles = '/switchProfiles';
|
static String manageProfiles = '/switchProfiles';
|
||||||
static String groupManagement = '/group_management';
|
static String circleManagement = '/circle_management';
|
||||||
static String signup = '/signup';
|
static String signup = '/signup';
|
||||||
static String userProfile = '/user_profile';
|
static String userProfile = '/user_profile';
|
||||||
static String userPosts = '/user_posts';
|
static String userPosts = '/user_posts';
|
||||||
|
@ -127,8 +127,8 @@ final appRouter = GoRouter(
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/connect/:id',
|
path: '/connect/:id',
|
||||||
name: ScreenPaths.connectHandle,
|
name: ScreenPaths.connectHandle,
|
||||||
builder: (context, state) =>
|
builder: (context, state) => FollowRequestAdjudicationScreen(
|
||||||
FollowRequestAdjudicationScreen(userId: state.pathParameters['id']!),
|
userId: state.pathParameters['id']!),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: ScreenPaths.timelines,
|
path: ScreenPaths.timelines,
|
||||||
|
@ -156,28 +156,28 @@ final appRouter = GoRouter(
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: ScreenPaths.thread,
|
name: ScreenPaths.thread,
|
||||||
path: ScreenPaths.thread,
|
path: ScreenPaths.thread,
|
||||||
builder: (context, state) =>
|
builder: (context, state) => MessageThreadScreen(
|
||||||
MessageThreadScreen(parentThreadId: state.uri.queryParameters['uri']!),
|
parentThreadId: state.uri.queryParameters['uri']!),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: ScreenPaths.groupManagement,
|
name: ScreenPaths.circleManagement,
|
||||||
path: ScreenPaths.groupManagement,
|
path: ScreenPaths.circleManagement,
|
||||||
builder: (context, state) => const GroupManagementScreen(),
|
builder: (context, state) => const CircleManagementScreen(),
|
||||||
routes: [
|
routes: [
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'show/:id',
|
path: 'show/:id',
|
||||||
builder: (context, state) => GroupEditorScreen(
|
builder: (context, state) => CircleEditorScreen(
|
||||||
groupId: state.pathParameters['id']!,
|
circleId: state.pathParameters['id']!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'new',
|
path: 'new',
|
||||||
builder: (context, state) => const GroupCreateScreen(),
|
builder: (context, state) => const CircleCreateScreen(),
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'add_users/:id',
|
path: 'add_users/:id',
|
||||||
builder: (context, state) =>
|
builder: (context, state) =>
|
||||||
GroupAddUsersScreen(groupId: state.pathParameters['id']!),
|
CircleAddUsersScreen(circleId: state.pathParameters['id']!),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -8,47 +8,47 @@ import '../controls/linear_status_indicator.dart';
|
||||||
import '../controls/responsive_max_width.dart';
|
import '../controls/responsive_max_width.dart';
|
||||||
import '../controls/status_and_refresh_button.dart';
|
import '../controls/status_and_refresh_button.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
|
import '../models/circle_data.dart';
|
||||||
import '../models/connection.dart';
|
import '../models/connection.dart';
|
||||||
import '../models/exec_error.dart';
|
import '../models/exec_error.dart';
|
||||||
import '../models/group_data.dart';
|
|
||||||
import '../routes.dart';
|
import '../routes.dart';
|
||||||
import '../services/connections_manager.dart';
|
import '../services/connections_manager.dart';
|
||||||
import '../services/network_status_service.dart';
|
import '../services/network_status_service.dart';
|
||||||
import '../utils/active_profile_selector.dart';
|
import '../utils/active_profile_selector.dart';
|
||||||
import '../utils/snackbar_builder.dart';
|
import '../utils/snackbar_builder.dart';
|
||||||
|
|
||||||
class GroupAddUsersScreen extends StatefulWidget {
|
class CircleAddUsersScreen extends StatefulWidget {
|
||||||
final String groupId;
|
final String circleId;
|
||||||
|
|
||||||
const GroupAddUsersScreen({super.key, required this.groupId});
|
const CircleAddUsersScreen({super.key, required this.circleId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GroupAddUsersScreen> createState() => _GroupAddUsersScreenState();
|
State<CircleAddUsersScreen> createState() => _CircleAddUsersScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GroupAddUsersScreenState extends State<GroupAddUsersScreen> {
|
class _CircleAddUsersScreenState extends State<CircleAddUsersScreen> {
|
||||||
static final _logger = Logger('$GroupAddUsersScreen');
|
static final _logger = Logger('$CircleAddUsersScreen');
|
||||||
var filterText = '';
|
var filterText = '';
|
||||||
late GroupData groupData;
|
late CircleData circleData;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final manager =
|
final manager =
|
||||||
getIt<ActiveProfileSelector<ConnectionsManager>>().activeEntry.value;
|
getIt<ActiveProfileSelector<ConnectionsManager>>().activeEntry.value;
|
||||||
groupData =
|
circleData =
|
||||||
manager.getMyGroups().where((g) => g.id == widget.groupId).first;
|
manager.getMyCircles().where((g) => g.id == widget.circleId).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addUserToGroup(
|
Future<void> addUserToCircle(
|
||||||
ConnectionsManager manager,
|
ConnectionsManager manager,
|
||||||
Connection connection,
|
Connection connection,
|
||||||
) async {
|
) async {
|
||||||
final messageBase = '${connection.name} from ${groupData.name}';
|
final messageBase = '${connection.name} from ${circleData.name}';
|
||||||
final confirm = await showYesNoDialog(context, 'Add $messageBase?');
|
final confirm = await showYesNoDialog(context, 'Add $messageBase?');
|
||||||
if (context.mounted && confirm == true) {
|
if (context.mounted && confirm == true) {
|
||||||
final message = await manager
|
final message = await manager
|
||||||
.addUserToGroup(groupData, connection)
|
.addUserToCircle(circleData, connection)
|
||||||
.withResult((p0) => setState(() {}))
|
.withResult((p0) => setState(() {}))
|
||||||
.fold(
|
.fold(
|
||||||
onSuccess: (_) => 'Added $messageBase',
|
onSuccess: (_) => 'Added $messageBase',
|
||||||
|
@ -66,15 +66,15 @@ class _GroupAddUsersScreenState extends State<GroupAddUsersScreen> {
|
||||||
.watch<ActiveProfileSelector<ConnectionsManager>>()
|
.watch<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
.activeEntry
|
.activeEntry
|
||||||
.value;
|
.value;
|
||||||
final groupMembers = manager
|
final circleMembers = manager
|
||||||
.getGroupMembers(groupData)
|
.getCircleMembers(circleData)
|
||||||
.withError((e) => logError(e, _logger))
|
.withError((e) => logError(e, _logger))
|
||||||
.getValueOrElse(() => [])
|
.getValueOrElse(() => [])
|
||||||
.toSet();
|
.toSet();
|
||||||
final allContacts = manager.getMyContacts();
|
final allContacts = manager.getMyContacts();
|
||||||
final filterTextLC = filterText.toLowerCase();
|
final filterTextLC = filterText.toLowerCase();
|
||||||
final contacts = allContacts
|
final contacts = allContacts
|
||||||
.where((c) => !groupMembers.contains(c))
|
.where((c) => !circleMembers.contains(c))
|
||||||
.where((c) =>
|
.where((c) =>
|
||||||
filterText.isEmpty ||
|
filterText.isEmpty ||
|
||||||
c.name.toLowerCase().contains(filterTextLC) ||
|
c.name.toLowerCase().contains(filterTextLC) ||
|
||||||
|
@ -83,7 +83,7 @@ class _GroupAddUsersScreenState extends State<GroupAddUsersScreen> {
|
||||||
contacts.sort((c1, c2) => c1.name.compareTo(c2.name));
|
contacts.sort((c1, c2) => c1.name.compareTo(c2.name));
|
||||||
_logger.finer(
|
_logger.finer(
|
||||||
() =>
|
() =>
|
||||||
'# in group: ${groupMembers.length} # Contacts: ${allContacts.length}, #filtered: ${contacts.length}',
|
'# in circle: ${circleMembers.length} # Contacts: ${allContacts.length}, #filtered: ${contacts.length}',
|
||||||
);
|
);
|
||||||
late Widget body;
|
late Widget body;
|
||||||
if (contacts.isEmpty) {
|
if (contacts.isEmpty) {
|
||||||
|
@ -111,7 +111,8 @@ class _GroupAddUsersScreenState extends State<GroupAddUsersScreen> {
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
onPressed: () async => await addUserToGroup(manager, contact),
|
onPressed: () async =>
|
||||||
|
await addUserToCircle(manager, contact),
|
||||||
icon: const Icon(Icons.add)),
|
icon: const Icon(Icons.add)),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -129,14 +130,14 @@ class _GroupAddUsersScreenState extends State<GroupAddUsersScreen> {
|
||||||
if (nss.connectionUpdateStatus.value) {
|
if (nss.connectionUpdateStatus.value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
manager.refreshGroupMemberships(groupData);
|
manager.refreshCircleMemberships(circleData);
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
child: ResponsiveMaxWidth(
|
child: ResponsiveMaxWidth(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Group: ${groupData.name}',
|
'Circle: ${circleData.name}',
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
),
|
),
|
||||||
|
@ -169,7 +170,7 @@ class _GroupAddUsersScreenState extends State<GroupAddUsersScreen> {
|
||||||
child: StatusAndRefreshButton(
|
child: StatusAndRefreshButton(
|
||||||
valueListenable: nss.connectionUpdateStatus,
|
valueListenable: nss.connectionUpdateStatus,
|
||||||
refreshFunction: () async =>
|
refreshFunction: () async =>
|
||||||
manager.refreshGroupMemberships(groupData),
|
manager.refreshCircleMemberships(circleData),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
|
@ -8,28 +8,28 @@ import '../services/connections_manager.dart';
|
||||||
import '../utils/active_profile_selector.dart';
|
import '../utils/active_profile_selector.dart';
|
||||||
import '../utils/snackbar_builder.dart';
|
import '../utils/snackbar_builder.dart';
|
||||||
|
|
||||||
class GroupCreateScreen extends StatefulWidget {
|
class CircleCreateScreen extends StatefulWidget {
|
||||||
const GroupCreateScreen({super.key});
|
const CircleCreateScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GroupCreateScreen> createState() => _GroupCreateScreenState();
|
State<CircleCreateScreen> createState() => _CircleCreateScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GroupCreateScreenState extends State<GroupCreateScreen> {
|
class _CircleCreateScreenState extends State<CircleCreateScreen> {
|
||||||
final groupTextController = TextEditingController();
|
final circleTextController = TextEditingController();
|
||||||
|
|
||||||
Future<void> createGroup(ConnectionsManager manager) async {
|
Future<void> createCircle(ConnectionsManager manager) async {
|
||||||
if (groupTextController.text.isEmpty) {
|
if (circleTextController.text.isEmpty) {
|
||||||
buildSnackbar(context, "Group name can't be empty");
|
buildSnackbar(context, "Circle name can't be empty");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final result = await manager.createGroup(groupTextController.text);
|
final result = await manager.createCircle(circleTextController.text);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
result.match(
|
result.match(
|
||||||
onSuccess: (_) => context.canPop() ? context.pop() : null,
|
onSuccess: (_) => context.canPop() ? context.pop() : null,
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
buildSnackbar(context, 'Error trying to create new group: $error');
|
buildSnackbar(context, 'Error trying to create new circle: $error');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ class _GroupCreateScreenState extends State<GroupCreateScreen> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: StandardAppBar.build(
|
appBar: StandardAppBar.build(
|
||||||
context,
|
context,
|
||||||
'New group',
|
'New circle',
|
||||||
withHome: false,
|
withHome: false,
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
|
@ -52,10 +52,10 @@ class _GroupCreateScreenState extends State<GroupCreateScreen> {
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: groupTextController,
|
controller: circleTextController,
|
||||||
textCapitalization: TextCapitalization.sentences,
|
textCapitalization: TextCapitalization.sentences,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Group Name',
|
labelText: 'Circle Name',
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderSide: const BorderSide(),
|
borderSide: const BorderSide(),
|
||||||
borderRadius: BorderRadius.circular(5.0),
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
@ -64,7 +64,7 @@ class _GroupCreateScreenState extends State<GroupCreateScreen> {
|
||||||
),
|
),
|
||||||
const VerticalPadding(),
|
const VerticalPadding(),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () => createGroup(manager),
|
onPressed: () => createCircle(manager),
|
||||||
child: const Text('Create'),
|
child: const Text('Create'),
|
||||||
),
|
),
|
||||||
],
|
],
|
|
@ -9,75 +9,75 @@ import '../controls/responsive_max_width.dart';
|
||||||
import '../controls/standard_appbar.dart';
|
import '../controls/standard_appbar.dart';
|
||||||
import '../controls/status_and_refresh_button.dart';
|
import '../controls/status_and_refresh_button.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
|
import '../models/circle_data.dart';
|
||||||
import '../models/connection.dart';
|
import '../models/connection.dart';
|
||||||
import '../models/group_data.dart';
|
|
||||||
import '../routes.dart';
|
import '../routes.dart';
|
||||||
import '../services/connections_manager.dart';
|
import '../services/connections_manager.dart';
|
||||||
import '../services/network_status_service.dart';
|
import '../services/network_status_service.dart';
|
||||||
import '../utils/active_profile_selector.dart';
|
import '../utils/active_profile_selector.dart';
|
||||||
import '../utils/snackbar_builder.dart';
|
import '../utils/snackbar_builder.dart';
|
||||||
|
|
||||||
class GroupEditorScreen extends StatefulWidget {
|
class CircleEditorScreen extends StatefulWidget {
|
||||||
final String groupId;
|
final String circleId;
|
||||||
|
|
||||||
const GroupEditorScreen({super.key, required this.groupId});
|
const CircleEditorScreen({super.key, required this.circleId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GroupEditorScreen> createState() => _GroupEditorScreenState();
|
State<CircleEditorScreen> createState() => _CircleEditorScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
class _CircleEditorScreenState extends State<CircleEditorScreen> {
|
||||||
final groupTextController = TextEditingController();
|
final circleTextController = TextEditingController();
|
||||||
var processingUpdate = false;
|
var processingUpdate = false;
|
||||||
var allowNameEditing = false;
|
var allowNameEditing = false;
|
||||||
var filterText = '';
|
var filterText = '';
|
||||||
late GroupData groupData;
|
late CircleData circleData;
|
||||||
|
|
||||||
Future<void> updateGroupName(
|
Future<void> updateCircleName(
|
||||||
BuildContext context, ConnectionsManager manager) async {
|
BuildContext context, ConnectionsManager manager) async {
|
||||||
processingUpdate = true;
|
processingUpdate = true;
|
||||||
final updated = groupTextController.text;
|
final updated = circleTextController.text;
|
||||||
if (groupTextController.text != groupData.name) {
|
if (circleTextController.text != circleData.name) {
|
||||||
final confirm = await showYesNoDialog(
|
final confirm = await showYesNoDialog(context,
|
||||||
context, 'Change the group name from ${groupData.name} to $updated?');
|
'Change the circle name from ${circleData.name} to $updated?');
|
||||||
if (context.mounted && confirm == true) {
|
if (context.mounted && confirm == true) {
|
||||||
await manager.renameGroup(widget.groupId, updated).match(
|
await manager.renameCircle(widget.circleId, updated).match(
|
||||||
onSuccess: (updatedGroupData) {
|
onSuccess: (updatedCircleData) {
|
||||||
groupData = updatedGroupData;
|
circleData = updatedCircleData;
|
||||||
setState(() {
|
setState(() {
|
||||||
allowNameEditing = false;
|
allowNameEditing = false;
|
||||||
});
|
});
|
||||||
}, onError: (error) {
|
}, onError: (error) {
|
||||||
buildSnackbar(context, 'Error renaming group: $error');
|
buildSnackbar(context, 'Error renaming circle: $error');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
groupTextController.text = groupData.name;
|
circleTextController.text = circleData.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processingUpdate = false;
|
processingUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteGroup(ConnectionsManager manager) async {
|
Future<void> deleteCircle(ConnectionsManager manager) async {
|
||||||
final confirm = await showYesNoDialog(context,
|
final confirm = await showYesNoDialog(context,
|
||||||
"Permanently delete group ${groupData.name}? This can't be undone.");
|
"Permanently delete circle ${circleData.name}? This can't be undone.");
|
||||||
if (context.mounted && confirm == true) {
|
if (context.mounted && confirm == true) {
|
||||||
await manager.deleteGroup(groupData).match(
|
await manager.deleteCircle(circleData).match(
|
||||||
onSuccess: (_) => context.canPop() ? context.pop() : null,
|
onSuccess: (_) => context.canPop() ? context.pop() : null,
|
||||||
onError: (error) =>
|
onError: (error) =>
|
||||||
buildSnackbar(context, 'Error trying to delete group: $error'),
|
buildSnackbar(context, 'Error trying to delete circle: $error'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeUserFromGroup(
|
Future<void> removeUserFromCircle(
|
||||||
ConnectionsManager manager,
|
ConnectionsManager manager,
|
||||||
Connection connection,
|
Connection connection,
|
||||||
) async {
|
) async {
|
||||||
final messageBase = '${connection.name} from ${groupData.name}';
|
final messageBase = '${connection.name} from ${circleData.name}';
|
||||||
final confirm = await showYesNoDialog(context, 'Remove $messageBase?');
|
final confirm = await showYesNoDialog(context, 'Remove $messageBase?');
|
||||||
if (context.mounted && confirm == true) {
|
if (context.mounted && confirm == true) {
|
||||||
final message =
|
final message =
|
||||||
await manager.removeUserFromGroup(groupData, connection).fold(
|
await manager.removeUserFromCircle(circleData, connection).fold(
|
||||||
onSuccess: (_) => 'Removed $messageBase',
|
onSuccess: (_) => 'Removed $messageBase',
|
||||||
onError: (error) => 'Error removing $messageBase: $error',
|
onError: (error) => 'Error removing $messageBase: $error',
|
||||||
);
|
);
|
||||||
|
@ -90,14 +90,14 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
super.initState();
|
super.initState();
|
||||||
final manager =
|
final manager =
|
||||||
getIt<ActiveProfileSelector<ConnectionsManager>>().activeEntry.value;
|
getIt<ActiveProfileSelector<ConnectionsManager>>().activeEntry.value;
|
||||||
groupData = manager
|
circleData = manager
|
||||||
.getMyGroups()
|
.getMyCircles()
|
||||||
.where(
|
.where(
|
||||||
(g) => g.id == widget.groupId,
|
(g) => g.id == widget.circleId,
|
||||||
)
|
)
|
||||||
.first;
|
.first;
|
||||||
manager.refreshGroupMemberships(groupData);
|
manager.refreshCircleMemberships(circleData);
|
||||||
groupTextController.text = groupData.name;
|
circleTextController.text = circleData.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -110,7 +110,7 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
|
|
||||||
final filterTextLC = filterText.toLowerCase();
|
final filterTextLC = filterText.toLowerCase();
|
||||||
final members = manager
|
final members = manager
|
||||||
.getGroupMembers(groupData)
|
.getCircleMembers(circleData)
|
||||||
.transform((ms) => ms
|
.transform((ms) => ms
|
||||||
.where((m) =>
|
.where((m) =>
|
||||||
filterText.isEmpty ||
|
filterText.isEmpty ||
|
||||||
|
@ -122,11 +122,11 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: StandardAppBar.build(
|
appBar: StandardAppBar.build(
|
||||||
context,
|
context,
|
||||||
'Group Editor',
|
'Circle Editor',
|
||||||
withHome: false,
|
withHome: false,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => deleteGroup(manager),
|
onPressed: () => deleteCircle(manager),
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -135,7 +135,7 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
manager.refreshGroups();
|
manager.refreshCircles();
|
||||||
},
|
},
|
||||||
child: ResponsiveMaxWidth(
|
child: ResponsiveMaxWidth(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -153,18 +153,18 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
if (processingUpdate) {
|
if (processingUpdate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateGroupName(context, manager);
|
updateCircleName(context, manager);
|
||||||
},
|
},
|
||||||
onTapOutside: (_) async {
|
onTapOutside: (_) async {
|
||||||
if (processingUpdate) {
|
if (processingUpdate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateGroupName(context, manager);
|
updateCircleName(context, manager);
|
||||||
},
|
},
|
||||||
controller: groupTextController,
|
controller: circleTextController,
|
||||||
textCapitalization: TextCapitalization.sentences,
|
textCapitalization: TextCapitalization.sentences,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Group Name',
|
labelText: 'Circle Name',
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderSide: const BorderSide(),
|
borderSide: const BorderSide(),
|
||||||
borderRadius: BorderRadius.circular(5.0),
|
borderRadius: BorderRadius.circular(5.0),
|
||||||
|
@ -176,7 +176,7 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (allowNameEditing) {
|
if (allowNameEditing) {
|
||||||
groupTextController.text = groupData.name;
|
circleTextController.text = circleData.name;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
allowNameEditing = !allowNameEditing;
|
allowNameEditing = !allowNameEditing;
|
||||||
|
@ -191,12 +191,12 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text('Group Members:',
|
Text('Circle Members:',
|
||||||
style: Theme.of(context).textTheme.headlineSmall),
|
style: Theme.of(context).textTheme.headlineSmall),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.push(
|
context.push(
|
||||||
'${ScreenPaths.groupManagement}/add_users/${widget.groupId}');
|
'${ScreenPaths.circleManagement}/add_users/${widget.circleId}');
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.add)),
|
icon: const Icon(Icons.add)),
|
||||||
],
|
],
|
||||||
|
@ -230,7 +230,7 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
child: StatusAndRefreshButton(
|
child: StatusAndRefreshButton(
|
||||||
valueListenable: nss.connectionUpdateStatus,
|
valueListenable: nss.connectionUpdateStatus,
|
||||||
refreshFunction: () async =>
|
refreshFunction: () async =>
|
||||||
manager.refreshGroupMemberships(groupData),
|
manager.refreshCircleMemberships(circleData),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -255,7 +255,7 @@ class _GroupEditorScreenState extends State<GroupEditorScreen> {
|
||||||
),
|
),
|
||||||
trailing: IconButton(
|
trailing: IconButton(
|
||||||
onPressed: () async =>
|
onPressed: () async =>
|
||||||
removeUserFromGroup(manager, m),
|
removeUserFromCircle(manager, m),
|
||||||
icon: const Icon(Icons.remove),
|
icon: const Icon(Icons.remove),
|
||||||
),
|
),
|
||||||
);
|
);
|
|
@ -8,8 +8,8 @@ import '../routes.dart';
|
||||||
import '../services/connections_manager.dart';
|
import '../services/connections_manager.dart';
|
||||||
import '../utils/active_profile_selector.dart';
|
import '../utils/active_profile_selector.dart';
|
||||||
|
|
||||||
class GroupManagementScreen extends StatelessWidget {
|
class CircleManagementScreen extends StatelessWidget {
|
||||||
const GroupManagementScreen({super.key});
|
const CircleManagementScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -17,17 +17,17 @@ class GroupManagementScreen extends StatelessWidget {
|
||||||
.watch<ActiveProfileSelector<ConnectionsManager>>()
|
.watch<ActiveProfileSelector<ConnectionsManager>>()
|
||||||
.activeEntry
|
.activeEntry
|
||||||
.value;
|
.value;
|
||||||
final groups = manager.getMyGroups();
|
final circles = manager.getMyCircles();
|
||||||
groups.sort((g1, g2) => g1.name.compareTo(g2.name));
|
circles.sort((g1, g2) => g1.name.compareTo(g2.name));
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: StandardAppBar.build(
|
appBar: StandardAppBar.build(
|
||||||
context,
|
context,
|
||||||
'Groups Management',
|
'Circles Management',
|
||||||
withHome: false,
|
withHome: false,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => context.push(
|
onPressed: () => context.push(
|
||||||
'${ScreenPaths.groupManagement}/new',
|
'${ScreenPaths.circleManagement}/new',
|
||||||
),
|
),
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -36,21 +36,21 @@ class GroupManagementScreen extends StatelessWidget {
|
||||||
body: Center(
|
body: Center(
|
||||||
child: RefreshIndicator(
|
child: RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
manager.refreshGroups();
|
manager.refreshCircles();
|
||||||
},
|
},
|
||||||
child: ResponsiveMaxWidth(
|
child: ResponsiveMaxWidth(
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final group = groups[index];
|
final circle = circles[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(group.name),
|
title: Text(circle.name),
|
||||||
onTap: () => context.push(
|
onTap: () => context.push(
|
||||||
'${ScreenPaths.groupManagement}/show/${group.id}'),
|
'${ScreenPaths.circleManagement}/show/${circle.id}'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (_, __) => const Divider(),
|
separatorBuilder: (_, __) => const Divider(),
|
||||||
itemCount: groups.length,
|
itemCount: circles.length,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
|
@ -16,8 +16,8 @@ import '../controls/padding.dart';
|
||||||
import '../controls/standard_appbar.dart';
|
import '../controls/standard_appbar.dart';
|
||||||
import '../controls/timeline/status_header_control.dart';
|
import '../controls/timeline/status_header_control.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
|
import '../models/circle_data.dart';
|
||||||
import '../models/exec_error.dart';
|
import '../models/exec_error.dart';
|
||||||
import '../models/group_data.dart';
|
|
||||||
import '../models/image_entry.dart';
|
import '../models/image_entry.dart';
|
||||||
import '../models/link_preview_data.dart';
|
import '../models/link_preview_data.dart';
|
||||||
import '../models/media_attachment_uploads/new_entry_media_items.dart';
|
import '../models/media_attachment_uploads/new_entry_media_items.dart';
|
||||||
|
@ -56,7 +56,7 @@ class _EditorScreenState extends State<EditorScreen> {
|
||||||
final existingMediaItems = <ImageEntry>[];
|
final existingMediaItems = <ImageEntry>[];
|
||||||
final focusNode = FocusNode();
|
final focusNode = FocusNode();
|
||||||
Visibility visibility = Visibility.public();
|
Visibility visibility = Visibility.public();
|
||||||
GroupData? currentGroup;
|
CircleData? currentCircle;
|
||||||
|
|
||||||
var isSubmitting = false;
|
var isSubmitting = false;
|
||||||
|
|
||||||
|
@ -568,25 +568,25 @@ class _EditorScreenState extends State<EditorScreen> {
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
final groups = context
|
final circles = context
|
||||||
.watch<ActiveProfileSelector<TimelineManager>>()
|
.watch<ActiveProfileSelector<TimelineManager>>()
|
||||||
.activeEntry
|
.activeEntry
|
||||||
.andThen((tm) => tm.getGroups())
|
.andThen((tm) => tm.getCircles())
|
||||||
.getValueOrElse(() => []);
|
.getValueOrElse(() => []);
|
||||||
groups.sort((g1, g2) => g1.name.compareTo(g2.name));
|
circles.sort((g1, g2) => g1.name.compareTo(g2.name));
|
||||||
|
|
||||||
final groupMenuItems = <DropdownMenuEntry<GroupData>>[];
|
final circleMenuItems = <DropdownMenuEntry<CircleData>>[];
|
||||||
groupMenuItems.add(DropdownMenuEntry(
|
circleMenuItems.add(DropdownMenuEntry(
|
||||||
value: GroupData.followersPseudoGroup,
|
value: CircleData.followersPseudoCircle,
|
||||||
label: GroupData.followersPseudoGroup.name));
|
label: CircleData.followersPseudoCircle.name));
|
||||||
groupMenuItems.add(DropdownMenuEntry(
|
circleMenuItems.add(DropdownMenuEntry(
|
||||||
value: GroupData('', ''), label: '-', enabled: false));
|
value: CircleData('', ''), label: '-', enabled: false));
|
||||||
groupMenuItems.addAll(groups.map((g) => DropdownMenuEntry(
|
circleMenuItems.addAll(circles.map((g) => DropdownMenuEntry(
|
||||||
value: g,
|
value: g,
|
||||||
label: g.name,
|
label: g.name,
|
||||||
)));
|
)));
|
||||||
if (!groups.contains(currentGroup)) {
|
if (!circles.contains(currentCircle)) {
|
||||||
currentGroup = null;
|
currentCircle = null;
|
||||||
}
|
}
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
|
@ -602,14 +602,14 @@ class _EditorScreenState extends State<EditorScreen> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == VisibilityType.private && currentGroup == null) {
|
if (value == VisibilityType.private && currentCircle == null) {
|
||||||
visibility = Visibility.private();
|
visibility = Visibility.private();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
visibility = Visibility(
|
visibility = Visibility(
|
||||||
type: VisibilityType.private,
|
type: VisibilityType.private,
|
||||||
allowedGroupIds: [currentGroup!.id],
|
allowedCircleIds: [currentCircle!.id],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -622,20 +622,20 @@ class _EditorScreenState extends State<EditorScreen> {
|
||||||
),
|
),
|
||||||
const HorizontalPadding(),
|
const HorizontalPadding(),
|
||||||
if (visibility.type == VisibilityType.private)
|
if (visibility.type == VisibilityType.private)
|
||||||
DropdownMenu<GroupData>(
|
DropdownMenu<CircleData>(
|
||||||
enabled: !widget.forEditing,
|
enabled: !widget.forEditing,
|
||||||
initialSelection: currentGroup,
|
initialSelection: currentCircle,
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentGroup = value;
|
currentCircle = value;
|
||||||
visibility = Visibility(
|
visibility = Visibility(
|
||||||
type: VisibilityType.private,
|
type: VisibilityType.private,
|
||||||
allowedGroupIds:
|
allowedCircleIds:
|
||||||
currentGroup == null ? [] : [currentGroup!.id],
|
currentCircle == null ? [] : [currentCircle!.id],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
dropdownMenuEntries: groupMenuItems,
|
dropdownMenuEntries: circleMenuItems,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -12,7 +12,7 @@ import '../controls/standard_app_drawer.dart';
|
||||||
import '../controls/timeline/timeline_panel.dart';
|
import '../controls/timeline/timeline_panel.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../models/TimelineIdentifiers.dart';
|
import '../models/TimelineIdentifiers.dart';
|
||||||
import '../models/group_data.dart';
|
import '../models/circle_data.dart';
|
||||||
import '../services/auth_service.dart';
|
import '../services/auth_service.dart';
|
||||||
import '../services/network_status_service.dart';
|
import '../services/network_status_service.dart';
|
||||||
import '../services/timeline_manager.dart';
|
import '../services/timeline_manager.dart';
|
||||||
|
@ -30,18 +30,18 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
|
|
||||||
final postText = TextEditingController();
|
final postText = TextEditingController();
|
||||||
var currentType = TimelineType.home;
|
var currentType = TimelineType.home;
|
||||||
GroupData? currentGroup;
|
CircleData? currentCircle;
|
||||||
final types = [
|
final types = [
|
||||||
TimelineType.self,
|
TimelineType.self,
|
||||||
TimelineType.home,
|
TimelineType.home,
|
||||||
TimelineType.global,
|
TimelineType.circle,
|
||||||
TimelineType.local,
|
TimelineType.local,
|
||||||
TimelineType.group,
|
TimelineType.circle,
|
||||||
];
|
];
|
||||||
|
|
||||||
void updateTimeline(TimelineManager manager) {
|
void updateTimeline(TimelineManager manager) {
|
||||||
if (currentType == TimelineType.group && currentGroup == null) {
|
if (currentType == TimelineType.circle && currentCircle == null) {
|
||||||
_logger.finest('Group timeline but no group selected so not updating');
|
_logger.finest('Circle timeline but no circle selected so not updating');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_logger.finest('Updating timeline: $currentTimeline');
|
_logger.finest('Updating timeline: $currentTimeline');
|
||||||
|
@ -53,7 +53,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
|
|
||||||
TimelineIdentifiers get currentTimeline => TimelineIdentifiers(
|
TimelineIdentifiers get currentTimeline => TimelineIdentifiers(
|
||||||
timeline: currentType,
|
timeline: currentType,
|
||||||
auxData: currentGroup?.id ?? '',
|
auxData: currentCircle?.id ?? '',
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -73,10 +73,10 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
.watch<ActiveProfileSelector<TimelineManager>>()
|
.watch<ActiveProfileSelector<TimelineManager>>()
|
||||||
.activeEntry
|
.activeEntry
|
||||||
.value;
|
.value;
|
||||||
final groups = manager.getGroups().getValueOrElse(() => []).toList();
|
final circles = manager.getCircles().getValueOrElse(() => []).toList();
|
||||||
groups.sort((g1, g2) => g1.name.compareTo(g2.name));
|
circles.sort((g1, g2) => g1.name.compareTo(g2.name));
|
||||||
if (!groups.contains(currentGroup)) {
|
if (!circles.contains(currentCircle)) {
|
||||||
currentGroup = null;
|
currentCircle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final timeline = TimelinePanel(timeline: currentTimeline);
|
final timeline = TimelinePanel(timeline: currentTimeline);
|
||||||
|
@ -97,7 +97,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
title: Row(
|
title: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (currentType == TimelineType.group)
|
if (currentType == TimelineType.circle)
|
||||||
PopupMenuButton<TimelineType>(
|
PopupMenuButton<TimelineType>(
|
||||||
initialValue: currentType,
|
initialValue: currentType,
|
||||||
// Callback that sets the selected popup menu item.
|
// Callback that sets the selected popup menu item.
|
||||||
|
@ -113,7 +113,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
child: Text(e.toLabel()),
|
child: Text(e.toLabel()),
|
||||||
))
|
))
|
||||||
.toList()),
|
.toList()),
|
||||||
if (currentType != TimelineType.group)
|
if (currentType != TimelineType.circle)
|
||||||
DropdownButton<TimelineType>(
|
DropdownButton<TimelineType>(
|
||||||
value: currentType,
|
value: currentType,
|
||||||
items: types
|
items: types
|
||||||
|
@ -131,18 +131,18 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||||
const HorizontalPadding(
|
const HorizontalPadding(
|
||||||
width: 5.0,
|
width: 5.0,
|
||||||
),
|
),
|
||||||
if (currentType == TimelineType.group)
|
if (currentType == TimelineType.circle)
|
||||||
DropdownButton<GroupData>(
|
DropdownButton<CircleData>(
|
||||||
value: currentGroup,
|
value: currentCircle,
|
||||||
items: groups
|
items: circles
|
||||||
.map((g) => DropdownMenuItem<GroupData>(
|
.map((g) => DropdownMenuItem<CircleData>(
|
||||||
value: g,
|
value: g,
|
||||||
child: Text(g.name),
|
child: Text(g.name),
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentGroup = value;
|
currentCircle = value;
|
||||||
});
|
});
|
||||||
updateTimeline(manager);
|
updateTimeline(manager);
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -6,8 +6,8 @@ import '../controls/html_text_viewer_control.dart';
|
||||||
import '../controls/login_aware_cached_network_image.dart';
|
import '../controls/login_aware_cached_network_image.dart';
|
||||||
import '../controls/padding.dart';
|
import '../controls/padding.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
|
import '../models/circle_data.dart';
|
||||||
import '../models/connection.dart';
|
import '../models/connection.dart';
|
||||||
import '../models/group_data.dart';
|
|
||||||
import '../routes.dart';
|
import '../routes.dart';
|
||||||
import '../services/auth_service.dart';
|
import '../services/auth_service.dart';
|
||||||
import '../services/blocks_manager.dart';
|
import '../services/blocks_manager.dart';
|
||||||
|
@ -117,7 +117,7 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
const VerticalPadding(),
|
const VerticalPadding(),
|
||||||
if (profile.status == ConnectionStatus.mutual ||
|
if (profile.status == ConnectionStatus.mutual ||
|
||||||
profile.status == ConnectionStatus.youFollowThem)
|
profile.status == ConnectionStatus.youFollowThem)
|
||||||
buildGroups(context, profile, connectionManager),
|
buildCircles(context, profile, connectionManager),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -138,23 +138,23 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildGroups(
|
Widget buildCircles(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
Connection profile,
|
Connection profile,
|
||||||
ConnectionsManager manager,
|
ConnectionsManager manager,
|
||||||
) {
|
) {
|
||||||
final myGroups = manager.getMyGroups();
|
final myCircles = manager.getMyCircles();
|
||||||
final usersGroups = manager.getGroupsForUser(profile.id).fold(
|
final usersCircles = manager.getCirclesForUser(profile.id).fold(
|
||||||
onSuccess: (groups) => groups.toSet(),
|
onSuccess: (circles) => circles.toSet(),
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
buildSnackbar(context, 'Error getting group data: $error');
|
buildSnackbar(context, 'Error getting circle data: $error');
|
||||||
return <GroupData>{};
|
return <CircleData>{};
|
||||||
});
|
});
|
||||||
myGroups.sort((g1, g2) => g1.name.compareTo(g2.name));
|
myCircles.sort((g1, g2) => g1.name.compareTo(g2.name));
|
||||||
final groupsWidgets = myGroups.map((g) {
|
final circlesWidgets = myCircles.map((g) {
|
||||||
return CheckboxListTile(
|
return CheckboxListTile(
|
||||||
title: Text(g.name),
|
title: Text(g.name),
|
||||||
value: usersGroups.contains(g),
|
value: usersCircles.contains(g),
|
||||||
onChanged: (bool? value) async {
|
onChanged: (bool? value) async {
|
||||||
if (isUpdating) {
|
if (isUpdating) {
|
||||||
return;
|
return;
|
||||||
|
@ -173,9 +173,9 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
isUpdating = true;
|
isUpdating = true;
|
||||||
});
|
});
|
||||||
if (isAdding) {
|
if (isAdding) {
|
||||||
await manager.addUserToGroup(g, profile);
|
await manager.addUserToCircle(g, profile);
|
||||||
} else {
|
} else {
|
||||||
await manager.removeUserFromGroup(g, profile);
|
await manager.removeUserFromCircle(g, profile);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
isUpdating = false;
|
isUpdating = false;
|
||||||
|
@ -186,11 +186,11 @@ class _UserProfileScreenState extends State<UserProfileScreen> {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Groups: ',
|
'Circles: ',
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
const VerticalPadding(),
|
const VerticalPadding(),
|
||||||
...groupsWidgets,
|
...circlesWidgets,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,16 @@ import '../../models/visibility.dart';
|
||||||
extension VisibilityFriendicaExtensions on Visibility {
|
extension VisibilityFriendicaExtensions on Visibility {
|
||||||
static Visibility fromJson(Map<String, dynamic> json) {
|
static Visibility fromJson(Map<String, dynamic> json) {
|
||||||
final allowedUserIds = _parseAcl(json['allow_cid']);
|
final allowedUserIds = _parseAcl(json['allow_cid']);
|
||||||
final excludedGroupIds = _parseAcl(json['deny_cid']);
|
final excludedCircleIds = _parseAcl(json['deny_cid']);
|
||||||
final allowedGroupIds = _parseAcl(json['allow_gid']);
|
final allowedCircleIds = _parseAcl(json['allow_gid']);
|
||||||
final excludedUserIds = _parseAcl(json['deny_cid']);
|
final excludedUserIds = _parseAcl(json['deny_cid']);
|
||||||
final topLevelPrivate = json['friendica_private'];
|
final topLevelPrivate = json['friendica_private'];
|
||||||
late final VisibilityType type;
|
late final VisibilityType type;
|
||||||
if (topLevelPrivate == null) {
|
if (topLevelPrivate == null) {
|
||||||
type = allowedUserIds.isEmpty &&
|
type = allowedUserIds.isEmpty &&
|
||||||
excludedUserIds.isEmpty &&
|
excludedUserIds.isEmpty &&
|
||||||
allowedGroupIds.isEmpty &&
|
allowedCircleIds.isEmpty &&
|
||||||
excludedGroupIds.isEmpty
|
excludedCircleIds.isEmpty
|
||||||
? VisibilityType.public
|
? VisibilityType.public
|
||||||
: VisibilityType.private;
|
: VisibilityType.private;
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,8 +23,8 @@ extension VisibilityFriendicaExtensions on Visibility {
|
||||||
type: type,
|
type: type,
|
||||||
allowedUserIds: allowedUserIds,
|
allowedUserIds: allowedUserIds,
|
||||||
excludedUserIds: excludedUserIds,
|
excludedUserIds: excludedUserIds,
|
||||||
allowedGroupIds: allowedGroupIds,
|
allowedCircleIds: allowedCircleIds,
|
||||||
excludedGroupIds: excludedGroupIds,
|
excludedCircleIds: excludedCircleIds,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@ extension VisibilityFriendicaExtensions on Visibility {
|
||||||
return {
|
return {
|
||||||
'allow_cid': _idsListToAclString(allowedUserIds),
|
'allow_cid': _idsListToAclString(allowedUserIds),
|
||||||
'deny_cid': _idsListToAclString(excludedUserIds),
|
'deny_cid': _idsListToAclString(excludedUserIds),
|
||||||
'allow_gid': _idsListToAclString(allowedGroupIds),
|
'allow_gid': _idsListToAclString(allowedCircleIds),
|
||||||
'deny_gid': _idsListToAclString(excludedGroupIds),
|
'deny_gid': _idsListToAclString(excludedCircleIds),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import '../../models/circle_data.dart';
|
||||||
|
|
||||||
|
extension CircleDataMastodonExtensions on CircleData {
|
||||||
|
static CircleData fromJson(Map<String, dynamic> json) => CircleData(
|
||||||
|
json['id'],
|
||||||
|
json['title'],
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
import '../../models/group_data.dart';
|
|
||||||
|
|
||||||
extension GroupDataMastodonExtensions on GroupData {
|
|
||||||
static GroupData fromJson(Map<String, dynamic> json) => GroupData(
|
|
||||||
json['id'],
|
|
||||||
json['title'],
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:relatica/models/group_data.dart';
|
import 'package:relatica/models/circle_data.dart';
|
||||||
|
|
||||||
import '../../models/visibility.dart';
|
import '../../models/visibility.dart';
|
||||||
|
|
||||||
|
@ -9,14 +9,14 @@ extension VisibilityMastodonExtensions on Visibility {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasDetails) {
|
if (hasDetails) {
|
||||||
final groupId = allowedGroupIds.first;
|
final circleId = allowedCircleIds.first;
|
||||||
if (groupId == GroupData.followersPseudoGroup.id) {
|
if (circleId == CircleData.followersPseudoCircle.id) {
|
||||||
return 'private';
|
return 'private';
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupId;
|
return circleId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'private';
|
return 'private';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,24 +5,24 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:result_monad/result_monad.dart';
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
|
import '../data/interfaces/circles_repo_intf.dart';
|
||||||
import '../data/interfaces/connections_repo_intf.dart';
|
import '../data/interfaces/connections_repo_intf.dart';
|
||||||
import '../data/interfaces/groups_repo.intf.dart';
|
|
||||||
import '../friendica_client/friendica_client.dart';
|
import '../friendica_client/friendica_client.dart';
|
||||||
import '../friendica_client/paging_data.dart';
|
import '../friendica_client/paging_data.dart';
|
||||||
import '../globals.dart';
|
import '../globals.dart';
|
||||||
import '../models/auth/profile.dart';
|
import '../models/auth/profile.dart';
|
||||||
|
import '../models/circle_data.dart';
|
||||||
import '../models/connection.dart';
|
import '../models/connection.dart';
|
||||||
import '../models/exec_error.dart';
|
import '../models/exec_error.dart';
|
||||||
import '../models/group_data.dart';
|
|
||||||
import '../utils/active_profile_selector.dart';
|
import '../utils/active_profile_selector.dart';
|
||||||
import 'persistent_info_service.dart';
|
import 'persistent_info_service.dart';
|
||||||
|
|
||||||
class ConnectionsManager extends ChangeNotifier {
|
class ConnectionsManager extends ChangeNotifier {
|
||||||
static final _logger = Logger('$ConnectionsManager');
|
static final _logger = Logger('$ConnectionsManager');
|
||||||
late final IConnectionsRepo conRepo;
|
late final IConnectionsRepo conRepo;
|
||||||
late final IGroupsRepo groupsRepo;
|
late final ICirclesRepo circlesRepo;
|
||||||
late final Profile profile;
|
late final Profile profile;
|
||||||
var groupsNotInitialized = true;
|
var circlesNotInitialized = true;
|
||||||
var _lastUpdateStatus = '';
|
var _lastUpdateStatus = '';
|
||||||
|
|
||||||
String get lastUpdateStatus => _lastUpdateStatus.isNotEmpty
|
String get lastUpdateStatus => _lastUpdateStatus.isNotEmpty
|
||||||
|
@ -34,12 +34,12 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
.withResult((text) => _lastUpdateStatus = text)
|
.withResult((text) => _lastUpdateStatus = text)
|
||||||
.getValueOrElse(() => 'Unknown');
|
.getValueOrElse(() => 'Unknown');
|
||||||
|
|
||||||
ConnectionsManager(this.profile, this.conRepo, this.groupsRepo);
|
ConnectionsManager(this.profile, this.conRepo, this.circlesRepo);
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
conRepo.clear();
|
conRepo.clear();
|
||||||
groupsRepo.clear();
|
circlesRepo.clear();
|
||||||
groupsNotInitialized = true;
|
circlesNotInitialized = true;
|
||||||
_lastUpdateStatus = '';
|
_lastUpdateStatus = '';
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -234,18 +234,18 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GroupData> getMyGroups() {
|
List<CircleData> getMyCircles() {
|
||||||
if (groupsNotInitialized) {
|
if (circlesNotInitialized) {
|
||||||
groupsNotInitialized = false;
|
circlesNotInitialized = false;
|
||||||
_updateMyGroups(true);
|
_updateMyCircles(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return groupsRepo.getMyGroups();
|
return circlesRepo.getMyCircles();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<List<Connection>, ExecError> getGroupMembers(GroupData group) {
|
Result<List<Connection>, ExecError> getCircleMembers(CircleData circle) {
|
||||||
return groupsRepo
|
return circlesRepo
|
||||||
.getGroupMembers(group)
|
.getCircleMembers(circle)
|
||||||
.transform(
|
.transform(
|
||||||
(members) => members
|
(members) => members
|
||||||
..sort((c1, c2) =>
|
..sort((c1, c2) =>
|
||||||
|
@ -254,71 +254,71 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
.execErrorCast();
|
.execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<GroupData, ExecError> createGroup(String newName) async {
|
FutureResult<CircleData, ExecError> createCircle(String newName) async {
|
||||||
final result = await GroupsClient(profile)
|
final result = await CirclesClient(profile)
|
||||||
.createGroup(newName)
|
.createCircle(newName)
|
||||||
.withResultAsync((newGroup) async {
|
.withResultAsync((newCircle) async {
|
||||||
groupsRepo.upsertGroup(newGroup);
|
circlesRepo.upsertCircle(newCircle);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
return result.execErrorCast();
|
return result.execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<GroupData, ExecError> renameGroup(
|
FutureResult<CircleData, ExecError> renameCircle(
|
||||||
String id, String newName) async {
|
String id, String newName) async {
|
||||||
final result = await GroupsClient(profile)
|
final result = await CirclesClient(profile)
|
||||||
.renameGroup(id, newName)
|
.renameCircle(id, newName)
|
||||||
.withResultAsync((renamedGroup) async {
|
.withResultAsync((renamedCircle) async {
|
||||||
groupsRepo.upsertGroup(renamedGroup);
|
circlesRepo.upsertCircle(renamedCircle);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
return result.execErrorCast();
|
return result.execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<bool, ExecError> deleteGroup(GroupData groupData) async {
|
FutureResult<bool, ExecError> deleteCircle(CircleData circleData) async {
|
||||||
final result = await GroupsClient(profile)
|
final result = await CirclesClient(profile)
|
||||||
.deleteGroup(groupData)
|
.deleteCircle(circleData)
|
||||||
.withResultAsync((_) async {
|
.withResultAsync((_) async {
|
||||||
groupsRepo.deleteGroup(groupData);
|
circlesRepo.deleteCircle(circleData);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
});
|
});
|
||||||
return result.execErrorCast();
|
return result.execErrorCast();
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshGroups() {
|
void refreshCircles() {
|
||||||
_updateMyGroups(true);
|
_updateMyCircles(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> refreshGroupMemberships(GroupData group) async {
|
Future<void> refreshCircleMemberships(CircleData circle) async {
|
||||||
var page = PagingData(limit: 50);
|
var page = PagingData(limit: 50);
|
||||||
final client = GroupsClient(profile);
|
final client = CirclesClient(profile);
|
||||||
final allResults = <Connection>{};
|
final allResults = <Connection>{};
|
||||||
var moreResults = true;
|
var moreResults = true;
|
||||||
while (moreResults) {
|
while (moreResults) {
|
||||||
await client.getGroupMembers(group, page).match(onSuccess: (results) {
|
await client.getCircleMembers(circle, page).match(onSuccess: (results) {
|
||||||
moreResults = results.data.isNotEmpty && results.next != null;
|
moreResults = results.data.isNotEmpty && results.next != null;
|
||||||
page = results.next ?? page;
|
page = results.next ?? page;
|
||||||
allResults.addAll(results.data);
|
allResults.addAll(results.data);
|
||||||
}, onError: (error) {
|
}, onError: (error) {
|
||||||
_logger.severe('Error getting group listing data: $error');
|
_logger.severe('Error getting circle listing data: $error');
|
||||||
moreResults = false;
|
moreResults = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
groupsRepo.deleteGroup(group);
|
circlesRepo.deleteCircle(circle);
|
||||||
groupsRepo.upsertGroup(group);
|
circlesRepo.upsertCircle(circle);
|
||||||
for (final c in allResults) {
|
for (final c in allResults) {
|
||||||
upsertConnection(c);
|
upsertConnection(c);
|
||||||
groupsRepo.addConnectionToGroup(group, c);
|
circlesRepo.addConnectionToCircle(circle, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<List<GroupData>, ExecError> getGroupsForUser(String id) {
|
Result<List<CircleData>, ExecError> getCirclesForUser(String id) {
|
||||||
final result = groupsRepo.getGroupsForUser(id);
|
final result = circlesRepo.getCirclesForUser(id);
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
print("Groups for user $id: $result");
|
print("Circles for user $id: $result");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,35 +326,35 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_refreshGroupListData(id, true);
|
_refreshCircleListData(id, true);
|
||||||
return Result.ok(UnmodifiableListView([]));
|
return Result.ok(UnmodifiableListView([]));
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<bool, ExecError> addUserToGroup(
|
FutureResult<bool, ExecError> addUserToCircle(
|
||||||
GroupData group, Connection connection) async {
|
CircleData circle, Connection connection) async {
|
||||||
_logger.finest('Adding ${connection.name} to group: ${group.name}');
|
_logger.finest('Adding ${connection.name} to circle: ${circle.name}');
|
||||||
return await GroupsClient(profile)
|
return await CirclesClient(profile)
|
||||||
.addConnectionToGroup(group, connection)
|
.addConnectionToCircle(circle, connection)
|
||||||
.withResultAsync((_) async => await refreshGroupMemberships(group))
|
.withResultAsync((_) async => await refreshCircleMemberships(circle))
|
||||||
.withResult((_) => notifyListeners())
|
.withResult((_) => notifyListeners())
|
||||||
.mapError((error) {
|
.mapError((error) {
|
||||||
_logger
|
_logger.severe(
|
||||||
.severe('Error adding ${connection.name} from group: ${group.name}');
|
'Error adding ${connection.name} from circle: ${circle.name}');
|
||||||
return error;
|
return error;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureResult<bool, ExecError> removeUserFromGroup(
|
FutureResult<bool, ExecError> removeUserFromCircle(
|
||||||
GroupData group, Connection connection) async {
|
CircleData circle, Connection connection) async {
|
||||||
_logger.finest('Removing ${connection.name} from group: ${group.name}');
|
_logger.finest('Removing ${connection.name} from circle: ${circle.name}');
|
||||||
return GroupsClient(profile)
|
return CirclesClient(profile)
|
||||||
.removeConnectionFromGroup(group, connection)
|
.removeConnectionFromCircle(circle, connection)
|
||||||
.withResultAsync((_) async => await refreshGroupMemberships(group))
|
.withResultAsync((_) async => await refreshCircleMemberships(circle))
|
||||||
.withResult((_) => notifyListeners())
|
.withResult((_) => notifyListeners())
|
||||||
.mapError(
|
.mapError(
|
||||||
(error) {
|
(error) {
|
||||||
_logger.severe(
|
_logger.severe(
|
||||||
'Error removing ${connection.name} from group: ${group.name}');
|
'Error removing ${connection.name} from circle: ${circle.name}');
|
||||||
return error;
|
return error;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -391,19 +391,19 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
Connection connection, {
|
Connection connection, {
|
||||||
bool withNotifications = true,
|
bool withNotifications = true,
|
||||||
}) async {
|
}) async {
|
||||||
await _updateMyGroups(false);
|
await _updateMyCircles(false);
|
||||||
await _refreshGroupListData(connection.id, false);
|
await _refreshCircleListData(connection.id, false);
|
||||||
await _refreshConnection(connection, false);
|
await _refreshConnection(connection, false);
|
||||||
if (withNotifications) {
|
if (withNotifications) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshGroupListData(String id, bool withNotification) async {
|
Future<void> _refreshCircleListData(String id, bool withNotification) async {
|
||||||
_logger.finest('Refreshing member list data for Connection $id');
|
_logger.finest('Refreshing member list data for Connection $id');
|
||||||
await GroupsClient(profile).getMemberGroupsForConnection(id).match(
|
await CirclesClient(profile).getMemberCirclesForConnection(id).match(
|
||||||
onSuccess: (groups) {
|
onSuccess: (circles) {
|
||||||
groupsRepo.updateConnectionGroupData(id, groups);
|
circlesRepo.updateConnectionCircleData(id, circles);
|
||||||
if (withNotification) {
|
if (withNotification) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -432,19 +432,19 @@ class ConnectionsManager extends ChangeNotifier {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _updateMyGroups(bool withNotification) async {
|
Future<void> _updateMyCircles(bool withNotification) async {
|
||||||
_logger.finest('Refreshing my groups list');
|
_logger.finest('Refreshing my circles list');
|
||||||
await GroupsClient(profile).getGroups().match(
|
await CirclesClient(profile).getCircles().match(
|
||||||
onSuccess: (groups) {
|
onSuccess: (circles) {
|
||||||
_logger.finest('Got updated groups:${groups.map((e) => e.name)}');
|
_logger.finest('Got updated circles:${circles.map((e) => e.name)}');
|
||||||
groupsRepo.clearMyGroups();
|
circlesRepo.clearMyCircles();
|
||||||
groupsRepo.addAllGroups(groups);
|
circlesRepo.addAllCircles(circles);
|
||||||
if (withNotification) {
|
if (withNotification) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
_logger.severe('Error getting my groups: $error');
|
_logger.severe('Error getting my circles: $error');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ import 'package:flutter/material.dart' hide Visibility;
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:result_monad/result_monad.dart';
|
import 'package:result_monad/result_monad.dart';
|
||||||
|
|
||||||
import '../data/interfaces/groups_repo.intf.dart';
|
import '../data/interfaces/circles_repo_intf.dart';
|
||||||
import '../friendica_client/friendica_client.dart';
|
import '../friendica_client/friendica_client.dart';
|
||||||
import '../models/TimelineIdentifiers.dart';
|
import '../models/TimelineIdentifiers.dart';
|
||||||
import '../models/auth/profile.dart';
|
import '../models/auth/profile.dart';
|
||||||
|
import '../models/circle_data.dart';
|
||||||
import '../models/entry_tree_item.dart';
|
import '../models/entry_tree_item.dart';
|
||||||
import '../models/exec_error.dart';
|
import '../models/exec_error.dart';
|
||||||
import '../models/group_data.dart';
|
|
||||||
import '../models/image_entry.dart';
|
import '../models/image_entry.dart';
|
||||||
import '../models/media_attachment_uploads/new_entry_media_items.dart';
|
import '../models/media_attachment_uploads/new_entry_media_items.dart';
|
||||||
import '../models/timeline.dart';
|
import '../models/timeline.dart';
|
||||||
|
@ -25,38 +25,38 @@ enum TimelineRefreshType {
|
||||||
class TimelineManager extends ChangeNotifier {
|
class TimelineManager extends ChangeNotifier {
|
||||||
static final _logger = Logger('$TimelineManager');
|
static final _logger = Logger('$TimelineManager');
|
||||||
|
|
||||||
final IGroupsRepo groupsRepo;
|
final ICirclesRepo circlesRepo;
|
||||||
final EntryManagerService entryManagerService;
|
final EntryManagerService entryManagerService;
|
||||||
var groupsNotInitialized = true;
|
var circlesNotInitialized = true;
|
||||||
final Profile profile;
|
final Profile profile;
|
||||||
|
|
||||||
final cachedTimelines = <TimelineIdentifiers, Timeline>{};
|
final cachedTimelines = <TimelineIdentifiers, Timeline>{};
|
||||||
|
|
||||||
TimelineManager(this.profile, this.groupsRepo, this.entryManagerService);
|
TimelineManager(this.profile, this.circlesRepo, this.entryManagerService);
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
groupsNotInitialized = true;
|
circlesNotInitialized = true;
|
||||||
cachedTimelines.clear();
|
cachedTimelines.clear();
|
||||||
entryManagerService.clear();
|
entryManagerService.clear();
|
||||||
groupsRepo.clear();
|
circlesRepo.clear();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<List<GroupData>, ExecError> getGroups() {
|
Result<List<CircleData>, ExecError> getCircles() {
|
||||||
if (groupsNotInitialized) {
|
if (circlesNotInitialized) {
|
||||||
_refreshGroupData();
|
_refreshCircleData();
|
||||||
groupsNotInitialized = false;
|
circlesNotInitialized = false;
|
||||||
return Result.ok([]);
|
return Result.ok([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ok(groupsRepo.getMyGroups());
|
return Result.ok(circlesRepo.getMyCircles());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshGroupData() async {
|
Future<void> _refreshCircleData() async {
|
||||||
_logger.finest('Refreshing member group data ');
|
_logger.finest('Refreshing member circle data ');
|
||||||
await GroupsClient(profile).getGroups().match(
|
await CirclesClient(profile).getCircles().match(
|
||||||
onSuccess: (groups) {
|
onSuccess: (circles) {
|
||||||
groupsRepo.addAllGroups(groups);
|
circlesRepo.addAllCircles(circles);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
},
|
},
|
||||||
onError: (error) {
|
onError: (error) {
|
||||||
|
|
Loading…
Reference in a new issue