Browse Source

fix(neon): Cleanup login pages layouts

pull/489/head
jld3103 2 years ago
parent
commit
ce2d95ee3e
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 2
      packages/neon/neon/lib/l10n/en.arb
  2. 12
      packages/neon/neon/lib/l10n/localizations.dart
  3. 6
      packages/neon/neon/lib/l10n/localizations_en.dart
  4. 94
      packages/neon/neon/lib/src/pages/login.dart
  5. 45
      packages/neon/neon/lib/src/pages/login_check_account.dart
  6. 36
      packages/neon/neon/lib/src/pages/login_check_server_status.dart
  7. 2
      packages/neon/neon/lib/src/pages/login_flow.dart
  8. 6
      packages/neon/neon/lib/src/theme/theme.dart
  9. 6
      packages/neon/neon/lib/src/widgets/exception.dart
  10. 2
      packages/neon/neon/lib/src/widgets/nextcloud_logo.dart
  11. 11
      packages/neon/neon/lib/src/widgets/validation_tile.dart

2
packages/neon/neon/lib/l10n/en.arb

@ -1,5 +1,7 @@
{
"@@locale": "en",
"nextcloud": "Nextcloud",
"nextcloudLogo": "Nextcloud logo",
"appImplementationName": "{app, select, nextcloud{Nextcloud} core{Server} files{Files} news{News} notes{Notes} notifications{Notifications} other{}}",
"@appImplementationName": {
"placeholders": {

12
packages/neon/neon/lib/l10n/localizations.dart

@ -89,6 +89,18 @@ abstract class AppLocalizations {
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[Locale('en')];
/// No description provided for @nextcloud.
///
/// In en, this message translates to:
/// **'Nextcloud'**
String get nextcloud;
/// No description provided for @nextcloudLogo.
///
/// In en, this message translates to:
/// **'Nextcloud logo'**
String get nextcloudLogo;
/// No description provided for @appImplementationName.
///
/// In en, this message translates to:

6
packages/neon/neon/lib/l10n/localizations_en.dart

@ -6,6 +6,12 @@ import 'localizations.dart';
class AppLocalizationsEn extends AppLocalizations {
AppLocalizationsEn([String locale = 'en']) : super(locale);
@override
String get nextcloud => 'Nextcloud';
@override
String get nextcloudLogo => 'Nextcloud logo';
@override
String appImplementationName(String app) {
String _temp0 = intl.Intl.selectLogic(

94
packages/neon/neon/lib/src/pages/login.dart

@ -20,18 +20,23 @@ class LoginPage extends StatefulWidget {
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
final _focusNode = FocusNode();
@override
void initState() {
super.initState();
}
final _controller = TextEditingController();
@override
void dispose() {
_focusNode.dispose();
_controller.dispose();
super.dispose();
}
void login(final String url) {
if (_formKey.currentState!.validate()) {
LoginCheckServerStatusRoute(serverUrl: url).go(context);
} else {
_focusNode.requestFocus();
}
}
@override
Widget build(final BuildContext context) {
final branding = Branding.of(context);
@ -39,77 +44,76 @@ class _LoginPageState extends State<LoginPage> {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
leading: Navigator.canPop(context) ? const CloseButton() : null,
),
appBar: Navigator.canPop(context)
? AppBar(
leading: const CloseButton(),
)
: null,
body: Center(
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: Scrollbar(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(vertical: 40, horizontal: 20),
padding: const EdgeInsets.all(10),
primary: true,
child: Column(
children: [
branding.logo,
ExcludeSemantics(
child: branding.logo,
),
Text(
branding.name,
style: Theme.of(context).textTheme.titleLarge,
),
if (branding.showLoginWithNextcloud) ...[
const SizedBox(
height: 20,
height: 10,
),
Text(AppLocalizations.of(context).loginWorksWith),
const SizedBox(
height: 20,
height: 10,
),
Semantics(
label: AppLocalizations.of(context).nextcloud,
child: const NextcloudLogo(),
),
const NextcloudLogo(),
],
const SizedBox(
height: 40,
height: 50,
),
if (platform.canUseCamera) ...[
ExcludeSemantics(
child: Center(
child: Text(AppLocalizations.of(context).loginUsingQrcode),
),
),
IconButton(
tooltip: AppLocalizations.of(context).loginUsingQrcode,
icon: const Icon(
Icons.qr_code_scanner,
size: 50,
),
onPressed: () => const LoginQrcodeRoute().go(context),
),
const SizedBox(
height: 20,
),
ExcludeSemantics(
child: Center(
child: Text(AppLocalizations.of(context).loginUsingServerAddress),
),
),
],
Form(
key: _formKey,
child: TextFormField(
focusNode: _focusNode,
decoration: const InputDecoration(
controller: _controller,
decoration: InputDecoration(
hintText: 'https://...',
labelText: AppLocalizations.of(context).loginUsingServerAddress,
suffixIcon: IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: () {
login(_controller.text);
},
),
),
keyboardType: TextInputType.url,
validator: (final input) => validateHttpUrl(context, input),
onFieldSubmitted: (final input) {
if (_formKey.currentState!.validate()) {
LoginCheckServerStatusRoute(serverUrl: input).go(context);
} else {
_focusNode.requestFocus();
}
},
onFieldSubmitted: login,
),
),
if (platform.canUseCamera || true) ...[
const SizedBox(
height: 50,
),
IconButton(
tooltip: AppLocalizations.of(context).loginUsingQrcode,
icon: const Icon(
Icons.qr_code_scanner_rounded,
size: 60,
),
onPressed: () => const LoginQrcodeRoute().go(context),
),
],
],
),
),

45
packages/neon/neon/lib/src/pages/login_check_account.dart

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result.dart';
@ -9,7 +11,6 @@ import 'package:neon/src/router.dart';
import 'package:neon/src/theme/dialog.dart';
import 'package:neon/src/widgets/account_tile.dart';
import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/linear_progress_indicator.dart';
import 'package:neon/src/widgets/validation_tile.dart';
import 'package:provider/provider.dart';
@ -62,13 +63,19 @@ class _LoginCheckAccountPageState extends State<LoginCheckAccountPage> {
builder: (final context, final state) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
NeonLinearProgressIndicator(
visible: state.isLoading,
),
NeonException(
state.error,
onRetry: bloc.refresh,
),
if (state.hasError) ...[
Builder(
builder: (final context) {
final details = NeonException.getDetails(context, state.error);
return NeonValidationTile(
title: details.isUnauthorized
? AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch
: details.text,
state: ValidationState.failure,
);
},
),
],
_buildAccountTile(state),
Align(
alignment: Alignment.bottomRight,
@ -81,8 +88,19 @@ class _LoginCheckAccountPageState extends State<LoginCheckAccountPage> {
const HomeRoute().go(context);
}
: null,
child: Text(AppLocalizations.of(context).actionContinue),
: () {
if (state.hasError && NeonException.getDetails(context, state.error).isUnauthorized) {
Navigator.pop(context);
return;
}
unawaited(bloc.refresh());
},
child: Text(
state.hasData
? AppLocalizations.of(context).actionContinue
: AppLocalizations.of(context).actionRetry,
),
),
),
],
@ -94,6 +112,13 @@ class _LoginCheckAccountPageState extends State<LoginCheckAccountPage> {
);
Widget _buildAccountTile(final Result<Account> result) {
if (result.hasError) {
return NeonValidationTile(
title: AppLocalizations.of(context).loginCheckingAccount,
state: ValidationState.canceled,
);
}
if (result.hasData) {
return NeonAccountTile(
account: result.requireData,

36
packages/neon/neon/lib/src/pages/login_check_server_status.dart

@ -6,7 +6,6 @@ import 'package:neon/src/blocs/login_check_server_status.dart';
import 'package:neon/src/router.dart';
import 'package:neon/src/theme/dialog.dart';
import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/linear_progress_indicator.dart';
import 'package:neon/src/widgets/validation_tile.dart';
import 'package:nextcloud/nextcloud.dart';
@ -64,20 +63,23 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
NeonLinearProgressIndicator(
visible: state.isLoading,
),
NeonException(
state.error,
onRetry: bloc.refresh,
),
if (state.hasError) ...[
NeonValidationTile(
title: NeonException.getDetails(context, state.error).text,
state: ValidationState.failure,
),
],
_buildServerVersionTile(state),
_buildMaintenanceModeTile(state),
Align(
alignment: Alignment.bottomRight,
child: ElevatedButton(
onPressed: success ? _onContinue : null,
child: Text(AppLocalizations.of(context).actionContinue),
onPressed: success ? _onContinue : bloc.refresh,
child: Text(
success
? AppLocalizations.of(context).actionContinue
: AppLocalizations.of(context).actionRetry,
),
),
),
],
@ -102,6 +104,13 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
}
Widget _buildServerVersionTile(final Result<CoreServerStatus> result) {
if (result.hasError) {
return NeonValidationTile(
title: AppLocalizations.of(context).loginCheckingServerVersion,
state: ValidationState.canceled,
);
}
if (!result.hasData) {
return NeonValidationTile(
title: AppLocalizations.of(context).loginCheckingServerVersion,
@ -123,6 +132,13 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
}
Widget _buildMaintenanceModeTile(final Result<CoreServerStatus> result) {
if (result.hasError) {
return NeonValidationTile(
title: AppLocalizations.of(context).loginCheckingMaintenanceMode,
state: ValidationState.canceled,
);
}
if (!result.hasData) {
return NeonValidationTile(
title: AppLocalizations.of(context).loginCheckingMaintenanceMode,

2
packages/neon/neon/lib/src/pages/login_flow.dart

@ -57,7 +57,7 @@ class _LoginFlowPageState extends State<LoginFlowPage> {
appBar: AppBar(),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
padding: const EdgeInsets.all(10),
child: ResultBuilder.behaviorSubject(
stream: bloc.init,
builder: (final context, final init) => Column(

6
packages/neon/neon/lib/src/theme/theme.dart

@ -48,6 +48,7 @@ class AppTheme {
snackBarTheme: _snackBarTheme,
dividerTheme: _dividerTheme,
scrollbarTheme: _scrollbarTheme,
inputDecorationTheme: _inputDecorationTheme,
extensions: [
neonTheme,
...?appThemes,
@ -70,4 +71,9 @@ class AppTheme {
static const _scrollbarTheme = ScrollbarThemeData(
interactive: true,
);
static const _inputDecorationTheme = InputDecorationTheme(
border: OutlineInputBorder(),
floatingLabelBehavior: FloatingLabelBehavior.always,
);
}

6
packages/neon/neon/lib/src/widgets/exception.dart

@ -27,7 +27,7 @@ class NeonException extends StatelessWidget {
final Color? color;
static void showSnackbar(final BuildContext context, final dynamic exception) {
final details = _getExceptionDetails(context, exception);
final details = getDetails(context, exception);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@ -48,7 +48,7 @@ class NeonException extends StatelessWidget {
return const SizedBox();
}
final details = _getExceptionDetails(context, exception);
final details = getDetails(context, exception);
final color = this.color ?? Theme.of(context).colorScheme.error;
final errorIcon = Icon(
@ -109,7 +109,7 @@ class NeonException extends StatelessWidget {
);
}
static _ExceptionDetails _getExceptionDetails(final BuildContext context, final dynamic exception) {
static _ExceptionDetails getDetails(final BuildContext context, final dynamic exception) {
if (exception is String) {
return _ExceptionDetails(
text: exception,

2
packages/neon/neon/lib/src/widgets/nextcloud_logo.dart

@ -1,5 +1,6 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:neon/l10n/localizations.dart';
class NextcloudLogo extends StatelessWidget {
const NextcloudLogo({
@ -12,5 +13,6 @@ class NextcloudLogo extends StatelessWidget {
package: 'neon',
width: 100,
height: 100,
semanticsLabel: AppLocalizations.of(context).nextcloudLogo,
);
}

11
packages/neon/neon/lib/src/widgets/validation_tile.dart

@ -27,6 +27,11 @@ class NeonValidationTile extends StatelessWidget {
color: Theme.of(context).colorScheme.error,
size: size,
),
ValidationState.canceled => Icon(
Icons.cancel_outlined,
color: Theme.of(context).disabledColor,
size: size,
),
ValidationState.success => Icon(
Icons.check_circle,
color: Theme.of(context).colorScheme.primary,
@ -35,7 +40,10 @@ class NeonValidationTile extends StatelessWidget {
};
return ListTile(
leading: leading,
title: Text(title),
title: Text(
title,
style: state == ValidationState.canceled ? TextStyle(color: Theme.of(context).disabledColor) : null,
),
);
}
}
@ -43,5 +51,6 @@ class NeonValidationTile extends StatelessWidget {
enum ValidationState {
loading,
failure,
canceled,
success,
}

Loading…
Cancel
Save