feat: accepting invites in the front end
This commit is contained in:
parent
b65c8a7340
commit
57edd7ab14
@ -10,4 +10,5 @@ class ApiConstants {
|
|||||||
|
|
||||||
// Invitation endpoints
|
// Invitation endpoints
|
||||||
static const String getAllInvitationsEndpoint = '/invitations/all';
|
static const String getAllInvitationsEndpoint = '/invitations/all';
|
||||||
|
static const String acceptInvitationEndpoint = '/invitations/accept';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,14 @@ class InvitationsPage extends StatefulWidget {
|
|||||||
_InvitationsPageState createState() => _InvitationsPageState();
|
_InvitationsPageState createState() => _InvitationsPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InvitationsPageState extends State<InvitationsPage> {
|
class _InvitationsPageState extends State<InvitationsPage>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
InvitationsData? _invitationsData;
|
InvitationsData? _invitationsData;
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
String? _errorMessage;
|
String? _errorMessage;
|
||||||
|
Map<String, bool> _acceptingInvitations = {};
|
||||||
|
Map<String, bool> _acceptedInvitations = {};
|
||||||
|
Map<String, AnimationController> _animationControllers = {};
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -19,6 +23,14 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
_loadInvitations();
|
_loadInvitations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
for (final controller in _animationControllers.values) {
|
||||||
|
controller.dispose();
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _loadInvitations() async {
|
Future<void> _loadInvitations() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoading = true;
|
_isLoading = true;
|
||||||
@ -44,11 +56,7 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(Icons.event_busy, size: 80, color: Colors.grey[400]),
|
||||||
Icons.event_busy,
|
|
||||||
size: 80,
|
|
||||||
color: Colors.grey[400],
|
|
||||||
),
|
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
'Nothing here!',
|
'Nothing here!',
|
||||||
@ -61,10 +69,7 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
'Create the first invitation now!',
|
'Create the first invitation now!',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 16, color: Colors.grey[500]),
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.grey[500],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -76,18 +81,11 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(Icons.error_outline, size: 80, color: Colors.red[400]),
|
||||||
Icons.error_outline,
|
|
||||||
size: 80,
|
|
||||||
color: Colors.red[400],
|
|
||||||
),
|
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
Text(
|
Text(
|
||||||
_errorMessage ?? 'Something went wrong',
|
_errorMessage ?? 'Something went wrong',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 16, color: Colors.red[600]),
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.red[600],
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
SizedBox(height: 16),
|
||||||
@ -104,9 +102,54 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _acceptInvitation(Invitation invitation) async {
|
||||||
|
final invitationKey = invitation.id.toString();
|
||||||
|
setState(() {
|
||||||
|
_acceptingInvitations[invitationKey] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
final result = await InvitationsService.acceptInvitation(invitation.id);
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
_acceptingInvitations[invitationKey] = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result['success']) {
|
||||||
|
setState(() {
|
||||||
|
_acceptedInvitations[invitationKey] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
final controller = AnimationController(
|
||||||
|
duration: Duration(milliseconds: 1500),
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
_animationControllers[invitationKey] = controller;
|
||||||
|
controller.forward();
|
||||||
|
|
||||||
|
Future.delayed(Duration(milliseconds: 2000), () {
|
||||||
|
if (mounted) {
|
||||||
|
_loadInvitations();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(result['message'] ?? 'Failed to accept invitation'),
|
||||||
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildInvitationCard(Invitation invitation, String section) {
|
Widget _buildInvitationCard(Invitation invitation, String section) {
|
||||||
|
final invitationKey = invitation.id.toString();
|
||||||
bool isOwned = section == 'created';
|
bool isOwned = section == 'created';
|
||||||
bool isAccepted = section == 'accepted';
|
bool isAccepted = section == 'accepted';
|
||||||
|
bool isAccepting = _acceptingInvitations[invitationKey] ?? false;
|
||||||
|
bool hasBeenAccepted = _acceptedInvitations[invitationKey] ?? false;
|
||||||
|
AnimationController? animController = _animationControllers[invitationKey];
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.only(bottom: 16),
|
margin: EdgeInsets.only(bottom: 16),
|
||||||
@ -121,10 +164,7 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
offset: Offset(0, 2),
|
offset: Offset(0, 2),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
border: Border.all(
|
border: Border.all(color: Colors.grey.withOpacity(0.2), width: 1),
|
||||||
color: Colors.grey.withOpacity(0.2),
|
|
||||||
width: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -134,12 +174,16 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: InvitationUtils.getColorFromHex(invitation.tag.colorHex).withOpacity(0.1),
|
color: InvitationUtils.getColorFromHex(
|
||||||
|
invitation.tag.colorHex,
|
||||||
|
).withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
InvitationUtils.getIconFromName(invitation.tag.iconName),
|
InvitationUtils.getIconFromName(invitation.tag.iconName),
|
||||||
color: InvitationUtils.getColorFromHex(invitation.tag.colorHex),
|
color: InvitationUtils.getColorFromHex(
|
||||||
|
invitation.tag.colorHex,
|
||||||
|
),
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -158,10 +202,7 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'by ${invitation.creator.displayName}',
|
'by ${invitation.creator.displayName}',
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
|
||||||
fontSize: 12,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -169,7 +210,9 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: InvitationUtils.getColorFromHex(invitation.tag.colorHex).withOpacity(0.1),
|
color: InvitationUtils.getColorFromHex(
|
||||||
|
invitation.tag.colorHex,
|
||||||
|
).withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -177,13 +220,16 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: InvitationUtils.getColorFromHex(invitation.tag.colorHex),
|
color: InvitationUtils.getColorFromHex(
|
||||||
|
invitation.tag.colorHex,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (invitation.description != null && invitation.description!.isNotEmpty) ...[
|
if (invitation.description != null &&
|
||||||
|
invitation.description!.isNotEmpty) ...[
|
||||||
SizedBox(height: 12),
|
SizedBox(height: 12),
|
||||||
Text(
|
Text(
|
||||||
InvitationUtils.truncateDescription(invitation.description),
|
InvitationUtils.truncateDescription(invitation.description),
|
||||||
@ -202,10 +248,7 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
invitation.location!,
|
invitation.location!,
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 13, color: Colors.grey[600]),
|
||||||
fontSize: 13,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -218,10 +261,7 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
InvitationUtils.getRelativeDateTime(invitation.dateTime!),
|
InvitationUtils.getRelativeDateTime(invitation.dateTime!),
|
||||||
style: TextStyle(
|
style: TextStyle(fontSize: 13, color: Colors.grey[600]),
|
||||||
fontSize: 13,
|
|
||||||
color: Colors.grey[600],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -234,21 +274,21 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: InvitationUtils.getParticipantsStatusColor(
|
color: InvitationUtils.getParticipantsStatusColor(
|
||||||
invitation.currentAttendees,
|
invitation.currentAttendees,
|
||||||
invitation.maxParticipants
|
invitation.maxParticipants,
|
||||||
).withOpacity(0.1),
|
).withOpacity(0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
InvitationUtils.getParticipantsStatus(
|
InvitationUtils.getParticipantsStatus(
|
||||||
invitation.currentAttendees,
|
invitation.currentAttendees,
|
||||||
invitation.maxParticipants
|
invitation.maxParticipants,
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: InvitationUtils.getParticipantsStatusColor(
|
color: InvitationUtils.getParticipantsStatusColor(
|
||||||
invitation.currentAttendees,
|
invitation.currentAttendees,
|
||||||
invitation.maxParticipants
|
invitation.maxParticipants,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -265,21 +305,58 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 44,
|
height: 44,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: isOwned || isAccepted ? null : () {
|
onPressed: isOwned || isAccepted || isAccepting || hasBeenAccepted
|
||||||
// TODO: Implement accept invitation
|
? null
|
||||||
|
: () {
|
||||||
|
_acceptInvitation(invitation);
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: isAccepted
|
backgroundColor: (isAccepted || hasBeenAccepted)
|
||||||
? Colors.green
|
? Colors.green
|
||||||
: (isOwned ? Colors.grey[400] : Color(0xFF6A4C93)),
|
: (isOwned ? Colors.grey[400] : Color(0xFF6A4C93)),
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
elevation: isOwned || isAccepted ? 0 : 2,
|
elevation: isOwned || isAccepted || hasBeenAccepted ? 0 : 2,
|
||||||
),
|
),
|
||||||
|
child: isAccepting
|
||||||
|
? SizedBox(
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: hasBeenAccepted && animController != null
|
||||||
|
? AnimatedBuilder(
|
||||||
|
animation: animController,
|
||||||
|
builder: (context, child) {
|
||||||
|
if (animController.value < 0.5) {
|
||||||
|
return Text(
|
||||||
|
'Accepted ✓',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Opacity(
|
||||||
|
opacity: 1.0 - ((animController.value - 0.5) * 2),
|
||||||
child: Text(
|
child: Text(
|
||||||
isAccepted
|
'Accepted ✓',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
(isAccepted || hasBeenAccepted)
|
||||||
? 'Accepted ✓'
|
? 'Accepted ✓'
|
||||||
: (isOwned ? 'View/Edit' : 'Accept Invite'),
|
: (isOwned ? 'View/Edit' : 'Accept Invite'),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -294,7 +371,11 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildInvitationSection(String title, List<Invitation> invitations, String section) {
|
Widget _buildInvitationSection(
|
||||||
|
String title,
|
||||||
|
List<Invitation> invitations,
|
||||||
|
String section,
|
||||||
|
) {
|
||||||
if (invitations.isEmpty) return SizedBox.shrink();
|
if (invitations.isEmpty) return SizedBox.shrink();
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
@ -311,10 +392,14 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...invitations.map((invitation) => Padding(
|
...invitations
|
||||||
|
.map(
|
||||||
|
(invitation) => Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: _buildInvitationCard(invitation, section),
|
child: _buildInvitationCard(invitation, section),
|
||||||
)).toList(),
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -336,10 +421,7 @@ class _InvitationsPageState extends State<InvitationsPage> {
|
|||||||
),
|
),
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(icon: Icon(Icons.refresh), onPressed: _loadInvitations),
|
||||||
icon: Icon(Icons.refresh),
|
|
||||||
onPressed: _loadInvitations,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: _isLoading
|
body: _isLoading
|
||||||
|
|||||||
@ -6,21 +6,21 @@ import 'http_service.dart';
|
|||||||
class InvitationsService {
|
class InvitationsService {
|
||||||
static Future<Map<String, dynamic>> getAllInvitations() async {
|
static Future<Map<String, dynamic>> getAllInvitations() async {
|
||||||
try {
|
try {
|
||||||
final response = await HttpService.get(ApiConstants.getAllInvitationsEndpoint);
|
final response = await HttpService.get(
|
||||||
|
ApiConstants.getAllInvitationsEndpoint,
|
||||||
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final responseData = jsonDecode(response.body);
|
final responseData = jsonDecode(response.body);
|
||||||
final invitationsResponse = InvitationsResponse.fromJson(responseData);
|
final invitationsResponse = InvitationsResponse.fromJson(responseData);
|
||||||
|
|
||||||
if (invitationsResponse.status) {
|
if (invitationsResponse.status) {
|
||||||
return {
|
return {'success': true, 'data': invitationsResponse.data};
|
||||||
'success': true,
|
|
||||||
'data': invitationsResponse.data,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
'success': false,
|
'success': false,
|
||||||
'message': invitationsResponse.message ?? 'Failed to fetch invitations',
|
'message':
|
||||||
|
invitationsResponse.message ?? 'Failed to fetch invitations',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (response.statusCode == 401) {
|
} else if (response.statusCode == 401) {
|
||||||
@ -47,4 +47,42 @@ class InvitationsService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<Map<String, dynamic>> acceptInvitation(int invitationId) async {
|
||||||
|
try {
|
||||||
|
final response = await HttpService.post(
|
||||||
|
ApiConstants.acceptInvitationEndpoint,
|
||||||
|
{'id': invitationId},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final responseData = jsonDecode(response.body);
|
||||||
|
return {
|
||||||
|
'success': responseData['status'] ?? false,
|
||||||
|
'message': responseData['message'] ?? 'Request completed',
|
||||||
|
};
|
||||||
|
} else if (response.statusCode == 401) {
|
||||||
|
return {
|
||||||
|
'success': false,
|
||||||
|
'message': 'Session expired. Please login again.',
|
||||||
|
};
|
||||||
|
} else if (response.statusCode == 403) {
|
||||||
|
return {
|
||||||
|
'success': false,
|
||||||
|
'message': 'Access denied. Invalid credentials.',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'success': false,
|
||||||
|
'message': 'Server error (${response.statusCode})',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('Error accepting invitation: $e');
|
||||||
|
return {
|
||||||
|
'success': false,
|
||||||
|
'message': 'Network error. Please check your connection.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -71,7 +71,8 @@ class InvitationUtils {
|
|||||||
return 'today at $timeString';
|
return 'today at $timeString';
|
||||||
} else if (eventDate == tomorrow) {
|
} else if (eventDate == tomorrow) {
|
||||||
return 'tomorrow at $timeString';
|
return 'tomorrow at $timeString';
|
||||||
} else if (eventDate.isAfter(today) && eventDate.isBefore(today.add(Duration(days: 7)))) {
|
} else if (eventDate.isAfter(today) &&
|
||||||
|
eventDate.isBefore(today.add(Duration(days: 7)))) {
|
||||||
List<String> weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
List<String> weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||||
return '${weekdays[eventDate.weekday - 1]} at $timeString';
|
return '${weekdays[eventDate.weekday - 1]} at $timeString';
|
||||||
} else {
|
} else {
|
||||||
@ -94,13 +95,8 @@ class InvitationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static String getParticipantsStatus(int current, int max) {
|
static String getParticipantsStatus(int current, int max) {
|
||||||
int needed = max - current;
|
|
||||||
if (needed <= 2 && needed > 0) {
|
|
||||||
return '$needed more person${needed == 1 ? '' : 's'} needed';
|
|
||||||
} else {
|
|
||||||
return '$current/$max';
|
return '$current/$max';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static Color getParticipantsStatusColor(int current, int max) {
|
static Color getParticipantsStatusColor(int current, int max) {
|
||||||
int needed = max - current;
|
int needed = max - current;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user