Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/content_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'models/destination.dart';
import 'models/user/user.dart';
import 'widgets/bottom_app_bar.dart';
import 'widgets/verification_banner.dart';
import 'package:fsek_mobile/l10n/app_localizations.dart';
import 'package:audioplayers/audioplayers.dart';

Expand Down Expand Up @@ -201,6 +202,7 @@ class _ContentWrapperState extends State<ContentWrapper>
view); //move offstage to ensure they aren't painted when not visible
}
}).toList())),
VerificationBanner(),
])),
bottomNavigationBar: BottomAppBar(
shape: CircularNotchedRectangle(),
Expand Down
6 changes: 6 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
"other" : "Other",
"introduction" : "Introduction",

"verifyEmailBanner": "Your account is not verified. Verify your email to get full access.",
"verifyEmailAction": "Send email",
"verifyEmailSent": "A verification email has been sent.",
"verifyEmailSentShort": "Sent",
"verifyEmailFailed": "Could not send verification email, please try again later.",

"loginRequiredField": "Required field",
"loginEmail": "Email",
"loginPassword": "Password",
Expand Down
30 changes: 30 additions & 0 deletions lib/l10n/app_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,36 @@ abstract class AppLocalizations {
/// **'Nollning'**
String get introduction;

/// No description provided for @verifyEmailBanner.
///
/// In sv, this message translates to:
/// **'Ditt konto är inte verifierat. Verifiera din e-post för att få full åtkomst.'**
String get verifyEmailBanner;

/// No description provided for @verifyEmailAction.
///
/// In sv, this message translates to:
/// **'Skicka mejl'**
String get verifyEmailAction;

/// No description provided for @verifyEmailSent.
///
/// In sv, this message translates to:
/// **'Ett verifieringsmejl har skickats.'**
String get verifyEmailSent;

/// No description provided for @verifyEmailSentShort.
///
/// In sv, this message translates to:
/// **'Skickat'**
String get verifyEmailSentShort;

/// No description provided for @verifyEmailFailed.
///
/// In sv, this message translates to:
/// **'Kunde inte skicka verifieringsmejl, försök igen senare.'**
String get verifyEmailFailed;

/// No description provided for @loginRequiredField.
///
/// In sv, this message translates to:
Expand Down
17 changes: 17 additions & 0 deletions lib/l10n/app_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get introduction => 'Introduction';

@override
String get verifyEmailBanner =>
'Your account is not verified. Verify your email to get full access.';

@override
String get verifyEmailAction => 'Send email';

@override
String get verifyEmailSent => 'A verification email has been sent.';

@override
String get verifyEmailSentShort => 'Sent';

@override
String get verifyEmailFailed =>
'Could not send verification email, please try again later.';

@override
String get loginRequiredField => 'Required field';

Expand Down
17 changes: 17 additions & 0 deletions lib/l10n/app_localizations_sv.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get introduction => 'Nollning';

@override
String get verifyEmailBanner =>
'Ditt konto är inte verifierat. Verifiera din e-post för att få full åtkomst.';

@override
String get verifyEmailAction => 'Skicka mejl';

@override
String get verifyEmailSent => 'Ett verifieringsmejl har skickats.';

@override
String get verifyEmailSentShort => 'Skickat';

@override
String get verifyEmailFailed =>
'Kunde inte skicka verifieringsmejl, försök igen senare.';

@override
String get loginRequiredField => 'Obligatoriskt fält';

Expand Down
6 changes: 6 additions & 0 deletions lib/l10n/app_sv.arb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
"other" : "Övrigt",
"introduction" : "Nollning",

"verifyEmailBanner": "Ditt konto är inte verifierat. Verifiera din e-post för att få full åtkomst.",
"verifyEmailAction": "Skicka mejl",
"verifyEmailSent": "Ett verifieringsmejl har skickats.",
"verifyEmailSentShort": "Skickat",
"verifyEmailFailed": "Kunde inte skicka verifieringsmejl, försök igen senare.",

"loginRequiredField": "Obligatoriskt fält",
"loginEmail": "E-postadress",
"loginPassword": "Lösenord",
Expand Down
10 changes: 10 additions & 0 deletions lib/screens/nollning/schedule.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fsek_mobile/l10n/app_localizations.dart';
import 'package:fsek_mobile/screens/nollning/englishSchedule.dart';
import 'package:fsek_mobile/util/app_exception.dart';
Expand All @@ -16,6 +17,15 @@ class _ScheduleScreenState extends State<ScheduleScreenPage> {
@override
void initState(){
super.initState();
// hide the android navigation bar while the schedule is showing
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
}

@override
void dispose() {
// before leaving, show the android navigation bar again
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
super.dispose();
}

@override
Expand Down
140 changes: 140 additions & 0 deletions lib/widgets/verification_banner.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:fsek_mobile/api_client/lib/api_client.dart';
import 'package:fsek_mobile/services/api.service.dart';
import 'package:fsek_mobile/l10n/app_localizations.dart';

// Banner shown above the navbar when the user has not verified their email.
class VerificationBanner extends StatefulWidget {
const VerificationBanner({Key? key}) : super(key: key);

@override
_VerificationBannerState createState() => _VerificationBannerState();
}

class _VerificationBannerState extends State<VerificationBanner>
with WidgetsBindingObserver {
AdminUserRead? user;
bool sending = false;
bool sent = false;

@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_loadUser();
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// Re-check when coming back to the app
if (state == AppLifecycleState.resumed) {
_loadUser();
}
}

Future<void> _loadUser() async {
try {
final response = await ApiService.apiClient.getUsersApi().usersGetMe();
if (!mounted) return;
setState(() {
user = response.data;
});
} catch (error) {
// If we can't fetch the user we simply don't show the banner
}
}

Future<void> _requestVerification() async {
final email = user?.email;
if (email == null || sending) return;
setState(() {
sending = true;
});
var t = AppLocalizations.of(context)!;
try {
await ApiService.apiClient.getAuthApi().authVerifyRequestToken(
bodyAuthVerifyRequestToken: BodyAuthVerifyRequestToken(
(b) => b..email = email,
),
);
if (!mounted) return;
setState(() {
sending = false;
sent = true;
});
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.verifyEmailSent)));
} catch (error) {
if (!mounted) return;
setState(() {
sending = false;
});
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(t.verifyEmailFailed)));
}
}

@override
Widget build(BuildContext context) {
// Only show the banner once we know the user isn't verified
if (user == null || user!.isVerified != false) {
return SizedBox.shrink();
}

var t = AppLocalizations.of(context)!;
ColorScheme colors = Theme.of(context).colorScheme;

return Material(
color: colors.errorContainer,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
Icon(
Icons.warning_amber_rounded,
color: colors.onErrorContainer,
size: 22,
),
SizedBox(width: 10),
Expanded(
child: Text(
t.verifyEmailBanner,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colors.onErrorContainer,
),
),
),
SizedBox(width: 8),
TextButton(
onPressed: (sending || sent) ? null : _requestVerification,
style: TextButton.styleFrom(
foregroundColor: colors.onPrimary,
padding: EdgeInsets.symmetric(horizontal: 8),
minimumSize: Size(0, 32),
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
child: sending
? SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: colors.onErrorContainer,
),
)
: Text(sent ? t.verifyEmailSentShort : t.verifyEmailAction),
),
],
),
),
);
}
}
Loading