Renaming groups to circles everywhere in code and labels

codemagic-setup
Hank Grabowski 2023-11-15 16:05:45 -05:00
rodzic b3fceec72f
commit 20396c7393
25 zmienionych plików z 472 dodań i 472 usunięć

Wyświetl plik

@ -86,8 +86,8 @@ class StandardAppDrawer extends StatelessWidget {
),
buildMenuButton(
context,
'Groups Management',
() => context.pushNamed(ScreenPaths.groupManagement),
'Circles Management',
() => context.pushNamed(ScreenPaths.circleManagement),
),
buildMenuButton(
context,

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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);
}
}

Wyświetl plik

@ -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,

Wyświetl plik

@ -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:

Wyświetl plik

@ -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:

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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']!),
),
],
),

Wyświetl plik

@ -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),
),
)
],

Wyświetl plik

@ -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'),
),
],

Wyświetl plik

@ -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),
),
);

Wyświetl plik

@ -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,
),
),
),

Wyświetl plik

@ -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,
),
],
);

Wyświetl plik

@ -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);
}),

Wyświetl plik

@ -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,
],
);
}

Wyświetl plik

@ -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),
};
}

Wyświetl plik

@ -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'],
);
}

Wyświetl plik

@ -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'],
);
}

Wyświetl plik

@ -1,4 +1,4 @@
import 'package:relatica/models/group_data.dart';
import 'package:relatica/models/circle_data.dart';
import '../../models/visibility.dart';
@ -9,14 +9,14 @@ 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';
}
}
}

Wyświetl plik

@ -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');
},
);
}

Wyświetl plik

@ -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) {