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