added guardian login with views for their assigned childs
This commit is contained in:
@@ -3,6 +3,9 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../api/marianumconnect/marianumconnect_endpoint.dart' as mc;
|
||||
import '../../auth_link/guardian_link_listener.dart';
|
||||
import '../../auth_link/guardian_login_link.dart';
|
||||
import '../../background/widget_background_task.dart';
|
||||
import '../../state/app/modules/account/bloc/account_bloc.dart';
|
||||
import '../../state/app/modules/account/bloc/account_state.dart';
|
||||
@@ -12,8 +15,11 @@ import '../../storage/settings.dart' as model;
|
||||
import '../../theming/light_app_theme.dart';
|
||||
import '../../utils/haptics.dart';
|
||||
import '../pages/settings/widgets/endpoint_picker.dart';
|
||||
import 'guardian_login_controller.dart';
|
||||
import 'login_controller.dart';
|
||||
import 'post_login_splash.dart';
|
||||
import 'widgets/guardian_login_card.dart';
|
||||
import 'widgets/login_audience_card.dart';
|
||||
import 'widgets/login_branding.dart';
|
||||
import 'widgets/login_card.dart';
|
||||
|
||||
@@ -28,12 +34,45 @@ class _LoginState extends State<Login> with SingleTickerProviderStateMixin {
|
||||
static const _marianumRed = LightAppTheme.marianumRed;
|
||||
|
||||
final LoginController _controller = LoginController();
|
||||
final GuardianLoginController _guardianController = GuardianLoginController();
|
||||
late final Future<void> _guardianRestored;
|
||||
|
||||
/// Null while the user has not picked who is signing in.
|
||||
LoginAudience? _audience;
|
||||
late final AnimationController _fade = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 450),
|
||||
value: 1,
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_guardianRestored = _guardianController.restore().then((_) {
|
||||
if (!mounted || _guardianController.pending == null) return;
|
||||
setState(() => _audience = LoginAudience.guardian);
|
||||
});
|
||||
GuardianLinkListener.pending.addListener(_consumeGuardianLink);
|
||||
_consumeGuardianLink();
|
||||
}
|
||||
|
||||
/// A tapped mail link finishes the guardian login without typing the code.
|
||||
Future<void> _consumeGuardianLink() async {
|
||||
final uri = GuardianLinkListener.pending.value;
|
||||
if (uri == null) return;
|
||||
GuardianLinkListener.clear();
|
||||
final link = GuardianLoginLink.parse(
|
||||
uri,
|
||||
apiBase: Uri.parse(mc.MarianumConnectEndpoint.current()),
|
||||
);
|
||||
if (link == null) return;
|
||||
await _guardianRestored;
|
||||
if (!mounted) return;
|
||||
setState(() => _audience = LoginAudience.guardian);
|
||||
final signedIn = await _guardianController.submitLink(link);
|
||||
if (signedIn && mounted) _onLoginSuccess();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
@@ -43,8 +82,10 @@ class _LoginState extends State<Login> with SingleTickerProviderStateMixin {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
GuardianLinkListener.pending.removeListener(_consumeGuardianLink);
|
||||
_fade.dispose();
|
||||
_controller.dispose();
|
||||
_guardianController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -64,6 +105,20 @@ class _LoginState extends State<Login> with SingleTickerProviderStateMixin {
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildCard() => switch (_audience) {
|
||||
null => LoginAudienceCard(
|
||||
onSelected: (choice) => setState(() => _audience = choice),
|
||||
),
|
||||
LoginAudience.school => LoginCard(
|
||||
controller: _controller,
|
||||
onSuccess: _onLoginSuccess,
|
||||
),
|
||||
LoginAudience.guardian => GuardianLoginCard(
|
||||
controller: _guardianController,
|
||||
onSuccess: _onLoginSuccess,
|
||||
),
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
backgroundColor: _marianumRed,
|
||||
@@ -89,12 +144,33 @@ class _LoginState extends State<Login> with SingleTickerProviderStateMixin {
|
||||
children: [
|
||||
const LoginHeader(),
|
||||
const SizedBox(height: 28),
|
||||
LoginCard(
|
||||
controller: _controller,
|
||||
onSuccess: _onLoginSuccess,
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
const LoginDisclaimer(),
|
||||
_buildCard(),
|
||||
if (_audience != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
// Leaving mid-request would drop the form that
|
||||
// receives the result, so it is disabled then.
|
||||
child: ListenableBuilder(
|
||||
listenable: Listenable.merge([
|
||||
_controller,
|
||||
_guardianController,
|
||||
]),
|
||||
builder: (context, _) => TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
icon: const Icon(Icons.arrow_back, size: 18),
|
||||
label: const Text('Zurück zur Auswahl'),
|
||||
onPressed:
|
||||
_controller.loading ||
|
||||
_guardianController.loading
|
||||
? null
|
||||
: () => setState(() => _audience = null),
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
const Column(
|
||||
|
||||
Reference in New Issue
Block a user