Roadbound-BRR/lib/pages/invite/page.dart

132 lines
3.9 KiB
Dart

import "dart:async";
import "package:bus_running_record/provider/collaboration_state.dart";
import "package:bus_running_record/provider/supabase_state.dart";
import "package:flutter/foundation.dart";
import "package:go_router/go_router.dart";
import "package:provider/provider.dart";
import "package:shadcn_flutter/shadcn_flutter.dart";
class InvitePage extends StatefulWidget {
const InvitePage({required this.token, super.key});
final String token;
static final GoRoute route = GoRoute(
path: "/invite/:token",
builder: (context, state) {
final token = state.pathParameters["token"] ?? "";
return InvitePage(token: token);
},
);
@override
State<InvitePage> createState() => _InvitePageState();
}
class _InvitePageState extends State<InvitePage> {
bool _accepting = false;
bool _accepted = false;
String? _error;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return;
final isLoggedIn = context.read<SupabaseProvider>().isAuthenticated;
if (isLoggedIn) {
unawaited(_acceptInvite());
}
});
}
@override
Widget build(BuildContext context) {
final supabase = context.watch<SupabaseProvider>();
final isLoggedIn = supabase.isAuthenticated;
return Scaffold(
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 480),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Organization Invite").x2Large.semiBold,
const Gap(8),
Text("Token: ${widget.token}").xSmall.muted,
const Gap(16),
if (!isLoggedIn) ...[
Text("Sign in to accept this invite.").small,
const Gap(12),
Button.primary(
onPressed: () {
final next = Uri.encodeComponent("/invite/${widget.token}");
context.go("/login?next=$next");
},
child: const Text("Sign in"),
),
] else if (_accepting) ...[
Row(
children: [
const CircularProgressIndicator(),
const Gap(10),
Text("Accepting invite...").small,
],
),
] else if (_accepted) ...[
Text("Invite accepted.").small,
const Gap(12),
Button.primary(
onPressed: () => context.go("/"),
child: const Text("Open workspace"),
),
] else ...[
if (_error != null) Text(_error!).small,
const Gap(12),
Button.primary(
onPressed: () => unawaited(_acceptInvite()),
child: const Text("Accept invite"),
),
],
],
),
),
),
),
);
}
Future<void> _acceptInvite() async {
if (_accepting) return;
setState(() {
_accepting = true;
_error = null;
});
try {
await context.read<CollaborationProvider>().acceptInviteToken(widget.token);
if (!mounted) return;
setState(() {
_accepted = true;
});
} catch (error, stackTrace) {
debugPrint("[InvitePage] acceptInvite failed: $error");
debugPrintStack(stackTrace: stackTrace);
if (!mounted) return;
setState(() {
_error = error.toString();
});
} finally {
if (mounted) {
setState(() {
_accepting = false;
});
}
}
}
}