onBootstrap static method
Implementation
static Widget onBootstrap(
BuildContext context,
Function(bool) complete,
) {
final organizationsService = context.read<OrganizationService>();
final feature = OrganizationFeature.instance();
return Consumer<nav.RootState>(
builder: (context, state, _) {
return FutureBuilder(
future: organizationsService.loadOrgs(),
builder: (context, snapshot) {
final l10n = context.l10n;
if (snapshot.connectionState == ConnectionState.done) {
final organizations = snapshot.data;
if (organizations == null) {
// if no organization list is returned then
// an error occurred and the user should be
// be logged out and an error message
// displayed
state.setBusy();
UserIdentityFeature.logout();
} else {
// Determine the default organization
// from the list of organizations
final defaultOrg = UserIdentityFeature.getDefaultOrg();
Organization? organization;
for (final org in organizations) {
if (org.orgId == defaultOrg) {
organization = org;
break;
}
}
if (feature.config.membershipRequired) {
// if membership is required then ensure
// the user is associated with an organization
if (organizations.isEmpty) {
// if the user is not associated with any
// organizations then show the create
// organization dialog.
return OrganizationProfile(
buildContainer: (child, width, height) {
return SizedBox(
width: width + 40, // margins (4*2) + padding (16*2)
height: height + 40, // margins (4*2) + padding (16*2)
child: Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: child,
),
),
);
},
dismissDialog: () {
state.setBusy();
UserIdentityFeature.logout();
Future.microtask(() => complete(false));
},
createOrgOnly: true,
);
} else if (organization == null) {
// if membership is required then the user must
// be associated with at least one organization
// and a default must be set.
// pick the first verified organization or the
// first organization if none are verified
organization = organizations.firstWhere(
(org) => org.isVerified,
orElse: () => organizations.first,
);
return BootstrapNotification.buildNotification(
title: feature.getLocalizedValue(
context,
'defaultOrganizationSet',
),
body: l10n.defaultOrganizationSetNotification,
dialogSize: BootstrapNotification.dialogSize(480, 188),
logout: () {
UserIdentityFeature.setDefaultOrg(
organization!.orgId,
logout: true,
);
Future.microtask(() => complete(false));
},
);
}
}
if (organization != null) {
dismissDialog() {
if (feature.config.canChangeDefaultOrg &&
(organizations.length > 1 ||
!feature.config.membershipRequired)) {
// if there are multiple organizations then
// set the default organization to an empty
// string and log out the user so they can
// select a new organization
state.setBusy();
UserIdentityFeature.setDefaultOrg(
'',
logout: true,
);
} else {
// if there is only one organization then
// log out the user without changing the
// default organization
UserIdentityFeature.logout();
}
Future.microtask(() => complete(false));
}
if (!organization.isVerified) {
// if the organization is not verified then show the
// unverified organization alert dialog and allow the
// user to logout
return BootstrapNotification.buildNotification(
title: feature.getLocalizedValue(
context,
'unverifiedTitle',
),
body: l10n.awaitingVerification(
feature.getLocalizedValue(
context,
'orgTypeText',
),
organization.name,
),
dialogSize: BootstrapNotification.dialogSize(480, 272),
logout: dismissDialog,
);
}
// if the feature has a subscription active status
// pattern configured then check if the organization
// in context has a valid subscription
final activeStatusPattern =
feature.config.billing?.activeStatusPattern;
if (activeStatusPattern != null) {
final status = organization.subscriptionStatus?.name;
if (status == null ||
!RegExp(
activeStatusPattern,
).hasMatch(
status,
)) {
final billingFeature = FeatureRegistry.instance()
.getFeature<BillingFeature>(
BillingFeature.featureName);
if (billingFeature == null || !organization.isOwner) {
// if the organization has no active subscription
// then show the inactive subscription alert
// dialog and log out the user
return BootstrapNotification.buildNotification(
title: feature.getLocalizedValue(
context,
'noActiveSubscription',
),
body: l10n.noActiveSubcriptionForOrganization,
dialogSize:
BootstrapNotification.dialogSize(480, 209),
logout: dismissDialog,
);
} else {
final product =
feature.config.billing?.productName != null
? billingFeature.getBillingProduct(
feature.config.billing!.productName,
)
: null;
assert(
product != null,
'Product not found for product name: '
'${feature.config.billing!.productName}',
);
return billingFeature.planSelectionDialogCard(
context,
product!,
subtitle: feature.getLocalizedValue(
context,
'orgBillingPlanSubtitleBootstrap',
),
orgId: organization.orgId,
currentPlanId: organization.subscriptionPlan,
currentSubscriptionId: organization.subscriptionId,
currentPlanStatus: organization.subscriptionStatus,
dismissOnPaymentTimeout: true,
waitForPaymentCompletion:
_waitForSubscriptionActivation(
context,
organization,
complete,
),
dismissDialog: dismissDialog,
);
}
}
// load the organization details and complete
// the bootstrapping process once the details
// are loaded
organizationsService.readOrgData(organization).then((_) {
Future.microtask(() => complete(true));
});
return const SizedBox();
}
}
}
// bootstrapping is complete and
// the user can proceed to the app
Future.microtask(() => complete(true));
return const SizedBox();
} else {
// show loading indicator while
// organizations user is associated
// with are being loaded
return const CircularProgressIndicator();
}
},
);
},
);
}