Refactor code for improved readability and consistency in multiple files

This commit is contained in:
Leon Bösche
2026-01-23 23:21:46 +01:00
parent 20bc0ac757
commit 98e7bbdb9e
8 changed files with 206 additions and 145 deletions

View File

@@ -18,7 +18,9 @@ class PermissionBloc extends Bloc<PermissionEvent, PermissionState> {
) async { ) async {
emit(PermissionLoading()); emit(PermissionLoading());
try { try {
final response = await apiClient.getRaw('/orgs/${event.orgId}/permissions'); final response = await apiClient.getRaw(
'/orgs/${event.orgId}/permissions',
);
final capabilities = Capabilities( final capabilities = Capabilities(
canRead: response['canRead'] ?? false, canRead: response['canRead'] ?? false,
canWrite: response['canWrite'] ?? false, canWrite: response['canWrite'] ?? false,

View File

@@ -56,7 +56,9 @@ class Invitation {
role: json['role'], role: json['role'],
createdAt: DateTime.parse(json['createdAt']), createdAt: DateTime.parse(json['createdAt']),
expiresAt: DateTime.parse(json['expiresAt']), expiresAt: DateTime.parse(json['expiresAt']),
acceptedAt: json['acceptedAt'] != null ? DateTime.parse(json['acceptedAt']) : null, acceptedAt: json['acceptedAt'] != null
? DateTime.parse(json['acceptedAt'])
: null,
); );
} }
} }

View File

@@ -370,13 +370,20 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
); );
} }
Widget _buildNavButton(String label, IconData icon, {bool isAvatar = false, VoidCallback? onTap}) { Widget _buildNavButton(
String label,
IconData icon, {
bool isAvatar = false,
VoidCallback? onTap,
}) {
final isSelected = _selectedTab == label; final isSelected = _selectedTab == label;
final highlightColor = const Color.fromARGB(255, 100, 200, 255); final highlightColor = const Color.fromARGB(255, 100, 200, 255);
final defaultColor = AppTheme.secondaryText; final defaultColor = AppTheme.secondaryText;
return GestureDetector( return GestureDetector(
onTap: onTap ?? () { onTap:
onTap ??
() {
setState(() { setState(() {
_selectedTab = label; _selectedTab = label;
}); });
@@ -495,7 +502,12 @@ class _HomePageState extends State<HomePage> with TickerProviderStateMixin {
const SizedBox(width: 16), const SizedBox(width: 16),
_buildNavButton('Add', Icons.add), _buildNavButton('Add', Icons.add),
const SizedBox(width: 16), const SizedBox(width: 16),
_buildNavButton('Settings', Icons.settings, onTap: () => _showOrganizationSettings(context)), _buildNavButton(
'Settings',
Icons.settings,
onTap: () =>
_showOrganizationSettings(context),
),
const SizedBox(width: 16), const SizedBox(width: 16),
_buildNavButton( _buildNavButton(
'Profile', 'Profile',

View File

@@ -71,7 +71,10 @@ class ApiClient {
} }
} }
Future<Map<String, dynamic>> getRaw(String path, {Map<String, dynamic>? queryParameters}) async { Future<Map<String, dynamic>> getRaw(
String path, {
Map<String, dynamic>? queryParameters,
}) async {
try { try {
final response = await _dio.get(path, queryParameters: queryParameters); final response = await _dio.get(path, queryParameters: queryParameters);
return response.data; return response.data;

View File

@@ -39,7 +39,11 @@ class OrgApi {
); );
} }
Future<void> updateMemberRole(String orgId, String userId, String role) async { Future<void> updateMemberRole(
String orgId,
String userId,
String role,
) async {
await _apiClient.patch( await _apiClient.patch(
'/orgs/$orgId/members/$userId', '/orgs/$orgId/members/$userId',
data: {'role': role}, data: {'role': role},
@@ -58,7 +62,11 @@ class OrgApi {
); );
} }
Future<Invitation> createInvitation(String orgId, String username, String role) async { Future<Invitation> createInvitation(
String orgId,
String username,
String role,
) async {
final result = await _apiClient.post( final result = await _apiClient.post(
'/orgs/$orgId/invitations', '/orgs/$orgId/invitations',
data: {'username': username, 'role': role}, data: {'username': username, 'role': role},
@@ -78,7 +86,10 @@ class OrgApi {
await _apiClient.delete('/orgs/$orgId/invitations/$invitationId'); await _apiClient.delete('/orgs/$orgId/invitations/$invitationId');
} }
Future<JoinRequest> createJoinRequest(String orgId, {String? inviteToken}) async { Future<JoinRequest> createJoinRequest(
String orgId, {
String? inviteToken,
}) async {
final data = {'orgId': orgId}; final data = {'orgId': orgId};
if (inviteToken != null) { if (inviteToken != null) {
data['inviteToken'] = inviteToken; data['inviteToken'] = inviteToken;
@@ -98,7 +109,11 @@ class OrgApi {
); );
} }
Future<void> acceptJoinRequest(String orgId, String requestId, String role) async { Future<void> acceptJoinRequest(
String orgId,
String requestId,
String role,
) async {
await _apiClient.post( await _apiClient.post(
'/orgs/$orgId/join-requests/$requestId/accept', '/orgs/$orgId/join-requests/$requestId/accept',
data: {'role': role}, data: {'role': role},

View File

@@ -20,10 +20,12 @@ class OrganizationSettingsDialog extends StatefulWidget {
}); });
@override @override
State<OrganizationSettingsDialog> createState() => _OrganizationSettingsDialogState(); State<OrganizationSettingsDialog> createState() =>
_OrganizationSettingsDialogState();
} }
class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog> with TickerProviderStateMixin { class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
with TickerProviderStateMixin {
late TabController _tabController; late TabController _tabController;
List<Member> _members = []; List<Member> _members = [];
List<Invitation> _invitations = []; List<Invitation> _invitations = [];
@@ -77,46 +79,54 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
Future<void> _updateMemberRole(String userId, String newRole) async { Future<void> _updateMemberRole(String userId, String newRole) async {
try { try {
await widget.orgApi.updateMemberRole(widget.organization.id, userId, newRole); await widget.orgApi.updateMemberRole(
widget.organization.id,
userId,
newRole,
);
await _loadData(); // Refresh await _loadData(); // Refresh
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to update role: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to update role: $e')));
} }
} }
Future<void> _removeMember(String userId) async { Future<void> _removeMember(String userId) async {
try { try {
await widget.orgApi.removeMember(widget.organization.id, userId); await widget.orgApi.removeMember(widget.organization.id, userId);
await _loadData(); // Refresh await _loadData(); // Refresh
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to remove member: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to remove member: $e')));
} }
} }
Future<void> _inviteUser(String username, String role) async { Future<void> _inviteUser(String username, String role) async {
try { try {
await widget.orgApi.createInvitation(
await widget.orgApi.createInvitation(widget.organization.id, username, role); widget.organization.id,
username,
role,
);
await _loadData(); // Refresh await _loadData(); // Refresh
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to send invitation: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to send invitation: $e')));
} }
} }
Future<void> _cancelInvitation(String invitationId) async { Future<void> _cancelInvitation(String invitationId) async {
try { try {
await widget.orgApi.cancelInvitation(
await widget.orgApi.cancelInvitation(widget.organization.id, invitationId); widget.organization.id,
invitationId,
);
await _loadData(); // Refresh await _loadData(); // Refresh
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
@@ -128,40 +138,43 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
Future<void> _acceptJoinRequest(String requestId, String role) async { Future<void> _acceptJoinRequest(String requestId, String role) async {
try { try {
await widget.orgApi.acceptJoinRequest(
await widget.orgApi.acceptJoinRequest(widget.organization.id, requestId, role); widget.organization.id,
requestId,
role,
);
await _loadData(); // Refresh await _loadData(); // Refresh
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to accept request: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to accept request: $e')));
} }
} }
Future<void> _rejectJoinRequest(String requestId) async { Future<void> _rejectJoinRequest(String requestId) async {
try { try {
await widget.orgApi.rejectJoinRequest(widget.organization.id, requestId); await widget.orgApi.rejectJoinRequest(widget.organization.id, requestId);
await _loadData(); // Refresh await _loadData(); // Refresh
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to reject request: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to reject request: $e')));
} }
} }
Future<void> _regenerateInviteLink() async { Future<void> _regenerateInviteLink() async {
try { try {
final newLink = await widget.orgApi.regenerateInviteLink(
final newLink = await widget.orgApi.regenerateInviteLink(widget.organization.id); widget.organization.id,
);
setState(() => _inviteLink = newLink); setState(() => _inviteLink = newLink);
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(
SnackBar(content: Text('Failed to regenerate link: $e')), context,
); ).showSnackBar(SnackBar(content: Text('Failed to regenerate link: $e')));
} }
} }
@@ -174,7 +187,8 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
} }
} }
bool get _canManage => widget.permissionState is PermissionLoaded && bool get _canManage =>
widget.permissionState is PermissionLoaded &&
(widget.permissionState as PermissionLoaded).capabilities.canAdmin; (widget.permissionState as PermissionLoaded).capabilities.canAdmin;
@override @override
@@ -232,7 +246,10 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text(_error!, style: TextStyle(color: AppTheme.errorColor)), Text(
_error!,
style: TextStyle(color: AppTheme.errorColor),
),
const SizedBox(height: 16), const SizedBox(height: 16),
ModernGlassButton( ModernGlassButton(
onPressed: _loadData, onPressed: _loadData,
@@ -271,7 +288,8 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
member.role, member.role,
style: TextStyle(color: AppTheme.secondaryText), style: TextStyle(color: AppTheme.secondaryText),
), ),
trailing: _canManage ? Row( trailing: _canManage
? Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (member.role != 'owner') if (member.role != 'owner')
@@ -291,11 +309,15 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
), ),
if (member.role != 'owner') if (member.role != 'owner')
IconButton( IconButton(
icon: Icon(Icons.remove_circle, color: AppTheme.errorColor), icon: Icon(
Icons.remove_circle,
color: AppTheme.errorColor,
),
onPressed: () => _removeMember(member.userId), onPressed: () => _removeMember(member.userId),
), ),
], ],
) : null, )
: null,
); );
}, },
); );
@@ -323,8 +345,14 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
itemBuilder: (context, index) { itemBuilder: (context, index) {
final inv = _invitations[index]; final inv = _invitations[index];
return ListTile( return ListTile(
title: Text(inv.username, style: TextStyle(color: AppTheme.primaryText)), title: Text(
subtitle: Text('Role: ${inv.role}', style: TextStyle(color: AppTheme.secondaryText)), inv.username,
style: TextStyle(color: AppTheme.primaryText),
),
subtitle: Text(
'Role: ${inv.role}',
style: TextStyle(color: AppTheme.secondaryText),
),
trailing: IconButton( trailing: IconButton(
icon: Icon(Icons.cancel, color: AppTheme.errorColor), icon: Icon(Icons.cancel, color: AppTheme.errorColor),
onPressed: () => _cancelInvitation(inv.id), onPressed: () => _cancelInvitation(inv.id),
@@ -358,10 +386,7 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
DropdownButtonFormField<String>( DropdownButtonFormField<String>(
initialValue: selectedRole, initialValue: selectedRole,
items: ['admin', 'member'].map((role) { items: ['admin', 'member'].map((role) {
return DropdownMenuItem( return DropdownMenuItem(value: role, child: Text(role));
value: role,
child: Text(role),
);
}).toList(), }).toList(),
onChanged: (value) => selectedRole = value ?? 'member', onChanged: (value) => selectedRole = value ?? 'member',
decoration: const InputDecoration( decoration: const InputDecoration(
@@ -399,7 +424,8 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
'Requested to join', 'Requested to join',
style: TextStyle(color: AppTheme.secondaryText), style: TextStyle(color: AppTheme.secondaryText),
), ),
trailing: _canManage ? Row( trailing: _canManage
? Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
TextButton( TextButton(
@@ -408,10 +434,14 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
), ),
TextButton( TextButton(
onPressed: () => _rejectJoinRequest(req.id), onPressed: () => _rejectJoinRequest(req.id),
child: Text('Reject', style: TextStyle(color: AppTheme.errorColor)), child: Text(
'Reject',
style: TextStyle(color: AppTheme.errorColor),
),
), ),
], ],
) : null, )
: null,
); );
}, },
); );
@@ -429,10 +459,7 @@ class _OrganizationSettingsDialogState extends State<OrganizationSettingsDialog>
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(_inviteLink!, style: TextStyle(color: AppTheme.secondaryText)),
_inviteLink!,
style: TextStyle(color: AppTheme.secondaryText),
),
const SizedBox(height: 16), const SizedBox(height: 16),
Row( Row(
children: [ children: [