MotorSignal-App/lib/features/social/clubs/club_membership_presentatio...

245 lines
8.5 KiB
Dart

import 'package:flutter/material.dart';
import '../../../services/clubs/club_models.dart';
/// Semantic state representing the viewer's relationship to a Club.
enum ClubMembershipPresentationState {
unauthenticated,
outsiderOpen,
outsiderRequest,
outsiderInviteOnly,
invited,
requested,
activeMember,
activeAdmin,
activeOwner,
archived,
unavailable,
}
/// Action intent triggered by the rider on a Club.
enum ClubMembershipAction {
joinOpen,
requestToJoin,
withdrawRequest,
acceptInvite,
declineInvite,
leaveClub,
signIn,
}
/// Badge color categorization for automotive UI styling.
enum PresentationBadgeType {
cyan,
green,
amber,
neutral,
}
/// Pure presentation model mapping Club metadata, membership state, and auth context
/// into authoritative, unambiguous UX components.
@immutable
class ClubMembershipPresentation {
final ClubMembershipPresentationState state;
final String statusBadge;
final PresentationBadgeType badgeType;
final String statusHeadline;
final String statusDescription;
final ClubMembershipAction? primaryAction;
final String? primaryActionLabel;
final IconData? primaryActionIcon;
final bool isPrimaryDestructive;
final ClubMembershipAction? secondaryAction;
final String? secondaryActionLabel;
final IconData? secondaryActionIcon;
final bool isSecondaryDestructive;
final bool canLeave;
final bool isActionable;
const ClubMembershipPresentation({
required this.state,
required this.statusBadge,
required this.badgeType,
required this.statusHeadline,
required this.statusDescription,
this.primaryAction,
this.primaryActionLabel,
this.primaryActionIcon,
this.isPrimaryDestructive = false,
this.secondaryAction,
this.secondaryActionLabel,
this.secondaryActionIcon,
this.isSecondaryDestructive = false,
this.canLeave = false,
this.isActionable = false,
});
/// Resolves the pure presentation state from domain models and auth context.
static ClubMembershipPresentation resolve({
required Club? club,
required ClubMember? member,
required String? currentUserId,
required bool isAuthenticated,
}) {
if (club == null) {
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.unavailable,
statusBadge: 'UNAVAILABLE',
badgeType: PresentationBadgeType.neutral,
statusHeadline: 'Club Not Found',
statusDescription: 'This club could not be loaded or does not exist.',
isActionable: false,
);
}
if (club.isArchived) {
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.archived,
statusBadge: 'ARCHIVED',
badgeType: PresentationBadgeType.amber,
statusHeadline: 'Club Archived',
statusDescription:
'This club has been archived by leadership and is no longer accepting members.',
canLeave: false,
isActionable: false,
);
}
if (!isAuthenticated || currentUserId == null || currentUserId.isEmpty) {
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.unauthenticated,
statusBadge: 'GUEST',
badgeType: PresentationBadgeType.neutral,
statusHeadline: 'Sign in to Join',
statusDescription:
'Sign in with a registered account to join this club and ride with members.',
primaryAction: ClubMembershipAction.signIn,
primaryActionLabel: 'Sign In / Register',
primaryActionIcon: Icons.login,
isActionable: true,
);
}
// Authenticated user with an active membership
if (member != null && member.status == ClubMemberStatus.active) {
final isOwner = member.role == ClubRole.owner || club.ownerUid == currentUserId;
if (isOwner) {
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.activeOwner,
statusBadge: 'OWNER',
badgeType: PresentationBadgeType.green,
statusHeadline: 'Club Owner',
statusDescription:
'You are the owner of this club. Ownership must be transferred before leaving.',
canLeave: false,
isActionable: false,
);
}
if (member.role == ClubRole.admin) {
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.activeAdmin,
statusBadge: 'ADMIN',
badgeType: PresentationBadgeType.cyan,
statusHeadline: 'Club Administrator',
statusDescription: 'You are an active administrator of this club.',
primaryAction: ClubMembershipAction.leaveClub,
primaryActionLabel: 'Leave Club',
primaryActionIcon: Icons.logout,
isPrimaryDestructive: true,
canLeave: true,
isActionable: true,
);
}
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.activeMember,
statusBadge: 'MEMBER',
badgeType: PresentationBadgeType.cyan,
statusHeadline: 'Active Member',
statusDescription: 'You are an active member of this club.',
primaryAction: ClubMembershipAction.leaveClub,
primaryActionLabel: 'Leave Club',
primaryActionIcon: Icons.logout,
isPrimaryDestructive: true,
canLeave: true,
isActionable: true,
);
}
// Authenticated user with an incoming invitation
if (member != null && member.status == ClubMemberStatus.invited) {
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.invited,
statusBadge: 'INVITED',
badgeType: PresentationBadgeType.green,
statusHeadline: 'You Are Invited',
statusDescription: 'You have been invited to join this club by club leadership.',
primaryAction: ClubMembershipAction.acceptInvite,
primaryActionLabel: 'Accept Invitation',
primaryActionIcon: Icons.check_circle_outline,
secondaryAction: ClubMembershipAction.declineInvite,
secondaryActionLabel: 'Decline',
secondaryActionIcon: Icons.close,
isSecondaryDestructive: true,
isActionable: true,
);
}
// Authenticated user with a pending join request
if (member != null && member.status == ClubMemberStatus.requested) {
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.requested,
statusBadge: 'REQUEST PENDING',
badgeType: PresentationBadgeType.amber,
statusHeadline: 'Membership Requested',
statusDescription: 'Your request to join is awaiting approval from club leadership.',
primaryAction: ClubMembershipAction.withdrawRequest,
primaryActionLabel: 'Withdraw Request',
primaryActionIcon: Icons.cancel_outlined,
isPrimaryDestructive: true,
isActionable: true,
);
}
// Outsider (no member doc or historical doc: left, declined, removed)
switch (club.admissionPolicy) {
case ClubAdmissionPolicy.open:
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.outsiderOpen,
statusBadge: 'OPEN ADMISSION',
badgeType: PresentationBadgeType.cyan,
statusHeadline: 'Open Membership',
statusDescription: 'This club is open to all riders. Join now to participate in club rides.',
primaryAction: ClubMembershipAction.joinOpen,
primaryActionLabel: 'Join Club',
primaryActionIcon: Icons.group_add,
isActionable: true,
);
case ClubAdmissionPolicy.request:
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.outsiderRequest,
statusBadge: 'REQUEST REQUIRED',
badgeType: PresentationBadgeType.cyan,
statusHeadline: 'Request Admission',
statusDescription: 'Membership requires review and approval from club leadership.',
primaryAction: ClubMembershipAction.requestToJoin,
primaryActionLabel: 'Request to Join',
primaryActionIcon: Icons.send_outlined,
isActionable: true,
);
case ClubAdmissionPolicy.inviteOnly:
return const ClubMembershipPresentation(
state: ClubMembershipPresentationState.outsiderInviteOnly,
statusBadge: 'INVITE ONLY',
badgeType: PresentationBadgeType.neutral,
statusHeadline: 'Invite-Only Club',
statusDescription:
'This club is invite-only. You must receive an invitation from leadership to join.',
isActionable: false,
);
}
}
}