Browse Source

Merge pull request #1068 from nextcloud/fix/neon/notch-clipping

pull/1074/head
Kate 1 year ago committed by GitHub
parent
commit
b617373c56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      packages/neon/neon/lib/src/pages/account_settings.dart
  2. 4
      packages/neon/neon/lib/src/pages/home.dart
  3. 116
      packages/neon/neon/lib/src/pages/login.dart
  4. 94
      packages/neon/neon/lib/src/pages/login_check_account.dart
  5. 68
      packages/neon/neon/lib/src/pages/login_check_server_status.dart
  6. 48
      packages/neon/neon/lib/src/pages/login_flow.dart
  7. 66
      packages/neon/neon/lib/src/pages/login_qr_code.dart
  8. 10
      packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart
  9. 6
      packages/neon/neon/lib/src/pages/route_not_found.dart
  10. 10
      packages/neon/neon/lib/src/pages/settings.dart
  11. 4
      packages/neon/neon/lib/src/widgets/app_bar.dart
  12. 100
      packages/neon/neon_files/lib/pages/details.dart
  13. 44
      packages/neon/neon_news/lib/pages/article.dart
  14. 18
      packages/neon/neon_news/lib/pages/feed.dart
  15. 8
      packages/neon/neon_news/lib/pages/folder.dart
  16. 8
      packages/neon/neon_notes/lib/pages/category.dart
  17. 72
      packages/neon/neon_notes/lib/pages/note.dart

10
packages/neon/neon/lib/src/pages/account_settings.dart

@ -144,10 +144,12 @@ class AccountSettingsPage extends StatelessWidget {
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: appBar, appBar: appBar,
body: Center( body: SafeArea(
child: ConstrainedBox( child: Center(
constraints: NeonDialogTheme.of(context).constraints, child: ConstrainedBox(
child: body, constraints: NeonDialogTheme.of(context).constraints,
child: body,
),
), ),
), ),
); );

4
packages/neon/neon/lib/src/pages/home.dart

@ -157,7 +157,9 @@ class _HomePageState extends State<HomePage> {
return const SizedBox(); return const SizedBox();
} }
return activeAppIDSnapshot.requireData.page; return SafeArea(
child: activeAppIDSnapshot.requireData.page,
);
}, },
); );
}, },

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

@ -51,72 +51,74 @@ class _LoginPageState extends State<LoginPage> {
leading: const CloseButton(), leading: const CloseButton(),
) )
: null, : null,
body: Center( body: SafeArea(
child: ConstrainedBox( child: Center(
constraints: NeonDialogTheme.of(context).constraints, child: ConstrainedBox(
child: SingleChildScrollView( constraints: NeonDialogTheme.of(context).constraints,
padding: const EdgeInsets.all(10), child: SingleChildScrollView(
primary: true, padding: const EdgeInsets.all(10),
child: Column( primary: true,
children: [ child: Column(
ExcludeSemantics( children: [
child: branding.logo, ExcludeSemantics(
), child: branding.logo,
Text(
branding.name,
style: Theme.of(context).textTheme.titleLarge,
),
if (branding.showLoginWithNextcloud) ...[
const SizedBox(
height: 10,
),
Text(NeonLocalizations.of(context).loginWorksWith),
const SizedBox(
height: 10,
), ),
Semantics( Text(
label: NeonLocalizations.of(context).nextcloud, branding.name,
child: const NextcloudLogo(), style: Theme.of(context).textTheme.titleLarge,
), ),
], if (branding.showLoginWithNextcloud) ...[
const SizedBox( const SizedBox(
height: 50, height: 10,
),
Form(
key: _formKey,
child: TextFormField(
focusNode: _focusNode,
controller: _controller,
decoration: InputDecoration(
hintText: 'https://...',
labelText: NeonLocalizations.of(context).loginUsingServerAddress,
suffixIcon: IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: () {
login(_controller.text);
},
),
), ),
keyboardType: TextInputType.url, Text(NeonLocalizations.of(context).loginWorksWith),
validator: (final input) => validateHttpUrl(context, input), const SizedBox(
onFieldSubmitted: login, height: 10,
autofillHints: const [AutofillHints.url], ),
), Semantics(
), label: NeonLocalizations.of(context).nextcloud,
if (NeonPlatform.instance.canUseCamera) ...[ child: const NextcloudLogo(),
),
],
const SizedBox( const SizedBox(
height: 50, height: 50,
), ),
IconButton( Form(
tooltip: NeonLocalizations.of(context).loginUsingQRcode, key: _formKey,
icon: const Icon( child: TextFormField(
Icons.qr_code_scanner_rounded, focusNode: _focusNode,
size: 60, controller: _controller,
decoration: InputDecoration(
hintText: 'https://...',
labelText: NeonLocalizations.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: login,
autofillHints: const [AutofillHints.url],
), ),
onPressed: () => const LoginQRcodeRoute().go(context),
), ),
if (NeonPlatform.instance.canUseCamera) ...[
const SizedBox(
height: 50,
),
IconButton(
tooltip: NeonLocalizations.of(context).loginUsingQRcode,
icon: const Icon(
Icons.qr_code_scanner_rounded,
size: 60,
),
onPressed: () => const LoginQRcodeRoute().go(context),
),
],
], ],
], ),
), ),
), ),
), ),

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

@ -54,57 +54,59 @@ class _LoginCheckAccountPageState extends State<LoginCheckAccountPage> {
@override @override
Widget build(final BuildContext context) => Scaffold( Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(), appBar: AppBar(),
body: Center( body: SafeArea(
child: Padding( child: Center(
padding: const EdgeInsets.all(10), child: Padding(
child: ConstrainedBox( padding: const EdgeInsets.all(10),
constraints: NeonDialogTheme.of(context).constraints, child: ConstrainedBox(
child: ResultBuilder.behaviorSubject( constraints: NeonDialogTheme.of(context).constraints,
subject: bloc.state, child: ResultBuilder.behaviorSubject(
builder: (final context, final state) => Column( subject: bloc.state,
mainAxisAlignment: MainAxisAlignment.center, builder: (final context, final state) => Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
if (state.hasError) ...[ children: [
Builder( if (state.hasError) ...[
builder: (final context) { Builder(
final details = NeonError.getDetails(state.error); builder: (final context) {
return NeonValidationTile( final details = NeonError.getDetails(state.error);
title: details.isUnauthorized return NeonValidationTile(
? NeonLocalizations.of(context).errorCredentialsForAccountNoLongerMatch title: details.isUnauthorized
: details.getText(context), ? NeonLocalizations.of(context).errorCredentialsForAccountNoLongerMatch
state: ValidationState.failure, : details.getText(context),
); state: ValidationState.failure,
}, );
), },
], ),
_buildAccountTile(state), ],
Align( _buildAccountTile(state),
alignment: Alignment.bottomRight, Align(
child: ElevatedButton( alignment: Alignment.bottomRight,
onPressed: state.hasData child: ElevatedButton(
? () { onPressed: state.hasData
NeonProvider.of<AccountsBloc>(context) ? () {
..updateAccount(state.requireData) NeonProvider.of<AccountsBloc>(context)
..setActiveAccount(state.requireData); ..updateAccount(state.requireData)
..setActiveAccount(state.requireData);
const HomeRoute().go(context); const HomeRoute().go(context);
}
: () {
if (state.hasError && NeonError.getDetails(state.error).isUnauthorized) {
Navigator.pop(context);
return;
} }
: () {
if (state.hasError && NeonError.getDetails(state.error).isUnauthorized) {
Navigator.pop(context);
return;
}
unawaited(bloc.refresh()); unawaited(bloc.refresh());
}, },
child: Text( child: Text(
state.hasData state.hasData
? NeonLocalizations.of(context).actionContinue ? NeonLocalizations.of(context).actionContinue
: NeonLocalizations.of(context).actionRetry, : NeonLocalizations.of(context).actionRetry,
),
), ),
), ),
), ],
], ),
), ),
), ),
), ),

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

@ -51,41 +51,43 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
@override @override
Widget build(final BuildContext context) => Scaffold( Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(), appBar: AppBar(),
body: Center( body: SafeArea(
child: Padding( child: Center(
padding: const EdgeInsets.all(10), child: Padding(
child: ConstrainedBox( padding: const EdgeInsets.all(10),
constraints: NeonDialogTheme.of(context).constraints, child: ConstrainedBox(
child: ResultBuilder.behaviorSubject( constraints: NeonDialogTheme.of(context).constraints,
subject: bloc.state, child: ResultBuilder.behaviorSubject(
builder: (final context, final state) { subject: bloc.state,
final success = state.hasData && state.requireData.isSupported && !state.requireData.maintenance; builder: (final context, final state) {
final success = state.hasData && state.requireData.isSupported && !state.requireData.maintenance;
return Column(
mainAxisAlignment: MainAxisAlignment.center, return Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
if (state.hasError) ...[ children: [
NeonValidationTile( if (state.hasError) ...[
title: NeonError.getDetails(state.error).getText(context), NeonValidationTile(
state: ValidationState.failure, title: NeonError.getDetails(state.error).getText(context),
), state: ValidationState.failure,
], ),
_buildServerVersionTile(state), ],
_buildMaintenanceModeTile(state), _buildServerVersionTile(state),
Align( _buildMaintenanceModeTile(state),
alignment: Alignment.bottomRight, Align(
child: ElevatedButton( alignment: Alignment.bottomRight,
onPressed: success ? _onContinue : bloc.refresh, child: ElevatedButton(
child: Text( onPressed: success ? _onContinue : bloc.refresh,
success child: Text(
? NeonLocalizations.of(context).actionContinue success
: NeonLocalizations.of(context).actionRetry, ? NeonLocalizations.of(context).actionContinue
: NeonLocalizations.of(context).actionRetry,
),
), ),
), ),
), ],
], );
); },
}, ),
), ),
), ),
), ),

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

@ -57,32 +57,34 @@ class _LoginFlowPageState extends State<LoginFlowPage> {
@override @override
Widget build(final BuildContext context) => Scaffold( Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(), appBar: AppBar(),
body: Center( body: SafeArea(
child: Padding( child: Center(
padding: const EdgeInsets.all(10), child: Padding(
child: ResultBuilder.behaviorSubject( padding: const EdgeInsets.all(10),
subject: bloc.init, child: ResultBuilder.behaviorSubject(
builder: (final context, final init) => Column( subject: bloc.init,
mainAxisAlignment: MainAxisAlignment.center, builder: (final context, final init) => Column(
children: [ mainAxisAlignment: MainAxisAlignment.center,
NeonLinearProgressIndicator( children: [
visible: init.isLoading, NeonLinearProgressIndicator(
), visible: init.isLoading,
NeonError(
init.error,
onRetry: bloc.refresh,
),
if (init.hasData) ...[
Text(NeonLocalizations.of(context).loginSwitchToBrowserWindow),
const SizedBox(
height: 10,
), ),
ElevatedButton( NeonError(
onPressed: bloc.refresh, init.error,
child: Text(NeonLocalizations.of(context).loginOpenAgain), onRetry: bloc.refresh,
), ),
if (init.hasData) ...[
Text(NeonLocalizations.of(context).loginSwitchToBrowserWindow),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: bloc.refresh,
child: Text(NeonLocalizations.of(context).loginOpenAgain),
),
],
], ],
], ),
), ),
), ),
), ),

66
packages/neon/neon/lib/src/pages/login_qr_code.dart

@ -23,41 +23,43 @@ class _LoginQRcodePageState extends State<LoginQRcodePage> {
@override @override
Widget build(final BuildContext context) => Scaffold( Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(), appBar: AppBar(),
body: ReaderWidget( body: SafeArea(
codeFormat: Format.qrCode, child: ReaderWidget(
showGallery: false, codeFormat: Format.qrCode,
showToggleCamera: false, showGallery: false,
showScannerOverlay: false, showToggleCamera: false,
tryHarder: true, showScannerOverlay: false,
cropPercent: 0, tryHarder: true,
scanDelaySuccess: const Duration(seconds: 3), cropPercent: 0,
onScan: (final code) async { scanDelaySuccess: const Duration(seconds: 3),
String? url; onScan: (final code) async {
try { String? url;
url = code.text; try {
if (url == null) { url = code.text;
throw const InvalidQRcodeException(); if (url == null) {
} throw const InvalidQRcodeException();
final match = LoginQRcode.tryParse(url); }
if (match == null) { final match = LoginQRcode.tryParse(url);
throw const InvalidQRcodeException(); if (match == null) {
} throw const InvalidQRcodeException();
}
LoginCheckServerStatusRoute.withCredentials( LoginCheckServerStatusRoute.withCredentials(
serverUrl: match.serverURL, serverUrl: match.serverURL,
loginName: match.username, loginName: match.username,
password: match.password, password: match.password,
).pushReplacement(context); ).pushReplacement(context);
} catch (e, s) { } catch (e, s) {
if (_lastErrorURL != url) { if (_lastErrorURL != url) {
debugPrint(e.toString()); debugPrint(e.toString());
debugPrint(s.toString()); debugPrint(s.toString());
_lastErrorURL = url; _lastErrorURL = url;
NeonError.showSnackbar(context, e); NeonError.showSnackbar(context, e);
}
} }
} },
}, ),
), ),
); );
} }

10
packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart

@ -61,10 +61,12 @@ class NextcloudAppSettingsPage extends StatelessWidget {
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: appBar, appBar: appBar,
body: Center( body: SafeArea(
child: ConstrainedBox( child: Center(
constraints: NeonDialogTheme.of(context).constraints, child: ConstrainedBox(
child: body, constraints: NeonDialogTheme.of(context).constraints,
child: body,
),
), ),
), ),
); );

6
packages/neon/neon/lib/src/pages/route_not_found.dart

@ -59,8 +59,10 @@ class _RouteNotFoundPageState extends State<RouteNotFoundPage> {
}, },
), ),
), ),
body: Center( body: SafeArea(
child: Text(NeonLocalizations.of(context).errorRouteNotFound(widget.uri.toString())), child: Center(
child: Text(NeonLocalizations.of(context).errorRouteNotFound(widget.uri.toString())),
),
), ),
); );
} }

10
packages/neon/neon/lib/src/pages/settings.dart

@ -370,10 +370,12 @@ class _SettingsPageState extends State<SettingsPage> {
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: appBar, appBar: appBar,
body: Center( body: SafeArea(
child: ConstrainedBox( child: Center(
constraints: NeonDialogTheme.of(context).constraints, child: ConstrainedBox(
child: body, constraints: NeonDialogTheme.of(context).constraints,
child: body,
),
), ),
), ),
); );

4
packages/neon/neon/lib/src/widgets/app_bar.dart

@ -233,7 +233,9 @@ class _NotificationIconButtonState extends State<NotificationIconButton> {
], ],
), ),
), ),
body: app.page, body: SafeArea(
child: app.page,
),
); );
await Navigator.of(context).push( await Navigator.of(context).push(

100
packages/neon/neon_files/lib/pages/details.dart

@ -16,59 +16,61 @@ class FilesDetailsPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(details.name), title: Text(details.name),
), ),
body: ListView( body: SafeArea(
primary: true, child: ListView(
children: [ primary: true,
ColoredBox( children: [
color: Theme.of(context).colorScheme.primary, ColoredBox(
child: FilePreview( color: Theme.of(context).colorScheme.primary,
bloc: bloc, child: FilePreview(
details: details, bloc: bloc,
color: Theme.of(context).colorScheme.onPrimary, details: details,
size: Size( color: Theme.of(context).colorScheme.onPrimary,
MediaQuery.of(context).size.width, size: Size(
MediaQuery.of(context).size.height / 4, MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height / 4,
),
), ),
), ),
), DataTable(
DataTable( headingRowHeight: 0,
headingRowHeight: 0, columns: const [
columns: const [ DataColumn(label: SizedBox()),
DataColumn(label: SizedBox()), DataColumn(label: SizedBox()),
DataColumn(label: SizedBox()), ],
], rows: [
rows: [ for (final entry in {
for (final entry in {
details.isDirectory
? FilesLocalizations.of(context).detailsFolderName
: FilesLocalizations.of(context).detailsFileName: details.name,
FilesLocalizations.of(context).detailsParentFolder:
details.path.length == 1 ? '/' : details.path.sublist(0, details.path.length - 1).join('/'),
if (details.size != null) ...{
details.isDirectory details.isDirectory
? FilesLocalizations.of(context).detailsFolderSize ? FilesLocalizations.of(context).detailsFolderName
: FilesLocalizations.of(context).detailsFileSize: filesize(details.size, 1), : FilesLocalizations.of(context).detailsFileName: details.name,
}, FilesLocalizations.of(context).detailsParentFolder:
if (details.lastModified != null) ...{ details.path.length == 1 ? '/' : details.path.sublist(0, details.path.length - 1).join('/'),
FilesLocalizations.of(context).detailsLastModified: if (details.size != null) ...{
details.lastModified!.toLocal().toIso8601String(), details.isDirectory
}, ? FilesLocalizations.of(context).detailsFolderSize
if (details.isFavorite != null) ...{ : FilesLocalizations.of(context).detailsFileSize: filesize(details.size, 1),
FilesLocalizations.of(context).detailsIsFavorite: details.isFavorite! },
? FilesLocalizations.of(context).actionYes if (details.lastModified != null) ...{
: FilesLocalizations.of(context).actionNo, FilesLocalizations.of(context).detailsLastModified:
}, details.lastModified!.toLocal().toIso8601String(),
}.entries) ...[ },
DataRow( if (details.isFavorite != null) ...{
cells: [ FilesLocalizations.of(context).detailsIsFavorite: details.isFavorite!
DataCell(Text(entry.key)), ? FilesLocalizations.of(context).actionYes
DataCell(Text(entry.value)), : FilesLocalizations.of(context).actionNo,
], },
), }.entries) ...[
DataRow(
cells: [
DataCell(Text(entry.key)),
DataCell(Text(entry.value)),
],
),
],
], ],
], ),
), ],
], ),
), ),
); );
} }

44
packages/neon/neon_news/lib/pages/article.dart

@ -167,28 +167,30 @@ class _NewsArticlePageState extends State<NewsArticlePage> {
], ],
], ],
), ),
body: widget.useWebView body: SafeArea(
? WebViewWidget( child: widget.useWebView
controller: _webviewController!, ? WebViewWidget(
) controller: _webviewController!,
: SingleChildScrollView( )
padding: const EdgeInsets.all(10), : SingleChildScrollView(
child: Html( padding: const EdgeInsets.all(10),
data: widget.bodyData, child: Html(
onLinkTap: ( data: widget.bodyData,
final url, onLinkTap: (
final attributes, final url,
final element, final attributes,
) async { final element,
if (url != null) { ) async {
await launchUrlString( if (url != null) {
url, await launchUrlString(
mode: LaunchMode.externalApplication, url,
); mode: LaunchMode.externalApplication,
} );
}, }
},
),
), ),
), ),
), ),
); );
} }

18
packages/neon/neon_news/lib/pages/feed.dart

@ -16,15 +16,17 @@ class NewsFeedPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(feed.title), title: Text(feed.title),
), ),
body: NewsArticlesView( body: SafeArea(
bloc: NewsArticlesBloc( child: NewsArticlesView(
bloc, bloc: NewsArticlesBloc(
bloc.options, bloc,
bloc.account, bloc.options,
id: feed.id, bloc.account,
listType: ListType.feed, id: feed.id,
listType: ListType.feed,
),
newsBloc: bloc,
), ),
newsBloc: bloc,
), ),
); );
} }

8
packages/neon/neon_news/lib/pages/folder.dart

@ -16,9 +16,11 @@ class NewsFolderPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(folder.name), title: Text(folder.name),
), ),
body: NewsFolderView( body: SafeArea(
bloc: bloc, child: NewsFolderView(
folder: folder, bloc: bloc,
folder: folder,
),
), ),
floatingActionButton: NewsFeedFloatingActionButton( floatingActionButton: NewsFeedFloatingActionButton(
bloc: bloc, bloc: bloc,

8
packages/neon/neon_notes/lib/pages/category.dart

@ -16,9 +16,11 @@ class NotesCategoryPage extends StatelessWidget {
appBar: AppBar( appBar: AppBar(
title: Text(category.name.isNotEmpty ? category.name : NotesLocalizations.of(context).categoryUncategorized), title: Text(category.name.isNotEmpty ? category.name : NotesLocalizations.of(context).categoryUncategorized),
), ),
body: NotesView( body: SafeArea(
bloc: bloc, child: NotesView(
category: category.name, bloc: bloc,
category: category.name,
),
), ),
floatingActionButton: NotesFloatingActionButton( floatingActionButton: NotesFloatingActionButton(
bloc: bloc, bloc: bloc,

72
packages/neon/neon_notes/lib/pages/note.dart

@ -141,42 +141,44 @@ class _NotesNotePageState extends State<NotesNotePage> {
), ),
], ],
), ),
body: GestureDetector( body: SafeArea(
onTap: () { child: GestureDetector(
setState(() { onTap: () {
_showEditor = true; setState(() {
}); _showEditor = true;
}, });
child: Container( },
padding: EdgeInsets.symmetric( child: Container(
vertical: 10, padding: EdgeInsets.symmetric(
horizontal: _showEditor ? 20 : 10, vertical: 10,
), horizontal: _showEditor ? 20 : 10,
color: Colors.transparent, ),
constraints: const BoxConstraints.expand(), color: Colors.transparent,
child: _showEditor constraints: const BoxConstraints.expand(),
? TextField( child: _showEditor
controller: _contentController, ? TextField(
focusNode: _contentFocusNode, controller: _contentController,
keyboardType: TextInputType.multiline, focusNode: _contentFocusNode,
maxLines: null, keyboardType: TextInputType.multiline,
decoration: const InputDecoration( maxLines: null,
border: InputBorder.none, decoration: const InputDecoration(
), border: InputBorder.none,
) ),
: SingleChildScrollView( )
child: MarkdownBody( : SingleChildScrollView(
data: _contentController.text, child: MarkdownBody(
onTapLink: (final text, final href, final title) async { data: _contentController.text,
if (href != null) { onTapLink: (final text, final href, final title) async {
await launchUrlString( if (href != null) {
href, await launchUrlString(
mode: LaunchMode.externalApplication, href,
); mode: LaunchMode.externalApplication,
} );
}, }
},
),
), ),
), ),
), ),
), ),
), ),

Loading…
Cancel
Save