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(
resizeToAvoidBottomInset: false,
appBar: appBar,
body: Center(
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: body,
body: SafeArea(
child: Center(
child: ConstrainedBox(
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 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(),
)
: null,
body: Center(
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: SingleChildScrollView(
padding: const EdgeInsets.all(10),
primary: true,
child: Column(
children: [
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,
body: SafeArea(
child: Center(
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: SingleChildScrollView(
padding: const EdgeInsets.all(10),
primary: true,
child: Column(
children: [
ExcludeSemantics(
child: branding.logo,
),
Semantics(
label: NeonLocalizations.of(context).nextcloud,
child: const NextcloudLogo(),
Text(
branding.name,
style: Theme.of(context).textTheme.titleLarge,
),
],
const SizedBox(
height: 50,
),
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);
},
),
if (branding.showLoginWithNextcloud) ...[
const SizedBox(
height: 10,
),
keyboardType: TextInputType.url,
validator: (final input) => validateHttpUrl(context, input),
onFieldSubmitted: login,
autofillHints: const [AutofillHints.url],
),
),
if (NeonPlatform.instance.canUseCamera) ...[
Text(NeonLocalizations.of(context).loginWorksWith),
const SizedBox(
height: 10,
),
Semantics(
label: NeonLocalizations.of(context).nextcloud,
child: const NextcloudLogo(),
),
],
const SizedBox(
height: 50,
),
IconButton(
tooltip: NeonLocalizations.of(context).loginUsingQRcode,
icon: const Icon(
Icons.qr_code_scanner_rounded,
size: 60,
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,
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
Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(),
body: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: ResultBuilder.behaviorSubject(
subject: bloc.state,
builder: (final context, final state) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (state.hasError) ...[
Builder(
builder: (final context) {
final details = NeonError.getDetails(state.error);
return NeonValidationTile(
title: details.isUnauthorized
? NeonLocalizations.of(context).errorCredentialsForAccountNoLongerMatch
: details.getText(context),
state: ValidationState.failure,
);
},
),
],
_buildAccountTile(state),
Align(
alignment: Alignment.bottomRight,
child: ElevatedButton(
onPressed: state.hasData
? () {
NeonProvider.of<AccountsBloc>(context)
..updateAccount(state.requireData)
..setActiveAccount(state.requireData);
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: ResultBuilder.behaviorSubject(
subject: bloc.state,
builder: (final context, final state) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (state.hasError) ...[
Builder(
builder: (final context) {
final details = NeonError.getDetails(state.error);
return NeonValidationTile(
title: details.isUnauthorized
? NeonLocalizations.of(context).errorCredentialsForAccountNoLongerMatch
: details.getText(context),
state: ValidationState.failure,
);
},
),
],
_buildAccountTile(state),
Align(
alignment: Alignment.bottomRight,
child: ElevatedButton(
onPressed: state.hasData
? () {
NeonProvider.of<AccountsBloc>(context)
..updateAccount(state.requireData)
..setActiveAccount(state.requireData);
const HomeRoute().go(context);
}
: () {
if (state.hasError && NeonError.getDetails(state.error).isUnauthorized) {
Navigator.pop(context);
return;
const HomeRoute().go(context);
}
: () {
if (state.hasError && NeonError.getDetails(state.error).isUnauthorized) {
Navigator.pop(context);
return;
}
unawaited(bloc.refresh());
},
child: Text(
state.hasData
? NeonLocalizations.of(context).actionContinue
: NeonLocalizations.of(context).actionRetry,
unawaited(bloc.refresh());
},
child: Text(
state.hasData
? NeonLocalizations.of(context).actionContinue
: 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
Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(),
body: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: ResultBuilder.behaviorSubject(
subject: bloc.state,
builder: (final context, final state) {
final success = state.hasData && state.requireData.isSupported && !state.requireData.maintenance;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (state.hasError) ...[
NeonValidationTile(
title: NeonError.getDetails(state.error).getText(context),
state: ValidationState.failure,
),
],
_buildServerVersionTile(state),
_buildMaintenanceModeTile(state),
Align(
alignment: Alignment.bottomRight,
child: ElevatedButton(
onPressed: success ? _onContinue : bloc.refresh,
child: Text(
success
? NeonLocalizations.of(context).actionContinue
: NeonLocalizations.of(context).actionRetry,
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: ResultBuilder.behaviorSubject(
subject: bloc.state,
builder: (final context, final state) {
final success = state.hasData && state.requireData.isSupported && !state.requireData.maintenance;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (state.hasError) ...[
NeonValidationTile(
title: NeonError.getDetails(state.error).getText(context),
state: ValidationState.failure,
),
],
_buildServerVersionTile(state),
_buildMaintenanceModeTile(state),
Align(
alignment: Alignment.bottomRight,
child: ElevatedButton(
onPressed: success ? _onContinue : bloc.refresh,
child: Text(
success
? 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
Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(),
body: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: ResultBuilder.behaviorSubject(
subject: bloc.init,
builder: (final context, final init) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
NeonLinearProgressIndicator(
visible: init.isLoading,
),
NeonError(
init.error,
onRetry: bloc.refresh,
),
if (init.hasData) ...[
Text(NeonLocalizations.of(context).loginSwitchToBrowserWindow),
const SizedBox(
height: 10,
body: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: ResultBuilder.behaviorSubject(
subject: bloc.init,
builder: (final context, final init) => Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
NeonLinearProgressIndicator(
visible: init.isLoading,
),
ElevatedButton(
onPressed: bloc.refresh,
child: Text(NeonLocalizations.of(context).loginOpenAgain),
NeonError(
init.error,
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
Widget build(final BuildContext context) => Scaffold(
appBar: AppBar(),
body: ReaderWidget(
codeFormat: Format.qrCode,
showGallery: false,
showToggleCamera: false,
showScannerOverlay: false,
tryHarder: true,
cropPercent: 0,
scanDelaySuccess: const Duration(seconds: 3),
onScan: (final code) async {
String? url;
try {
url = code.text;
if (url == null) {
throw const InvalidQRcodeException();
}
final match = LoginQRcode.tryParse(url);
if (match == null) {
throw const InvalidQRcodeException();
}
body: SafeArea(
child: ReaderWidget(
codeFormat: Format.qrCode,
showGallery: false,
showToggleCamera: false,
showScannerOverlay: false,
tryHarder: true,
cropPercent: 0,
scanDelaySuccess: const Duration(seconds: 3),
onScan: (final code) async {
String? url;
try {
url = code.text;
if (url == null) {
throw const InvalidQRcodeException();
}
final match = LoginQRcode.tryParse(url);
if (match == null) {
throw const InvalidQRcodeException();
}
LoginCheckServerStatusRoute.withCredentials(
serverUrl: match.serverURL,
loginName: match.username,
password: match.password,
).pushReplacement(context);
} catch (e, s) {
if (_lastErrorURL != url) {
debugPrint(e.toString());
debugPrint(s.toString());
LoginCheckServerStatusRoute.withCredentials(
serverUrl: match.serverURL,
loginName: match.username,
password: match.password,
).pushReplacement(context);
} catch (e, s) {
if (_lastErrorURL != url) {
debugPrint(e.toString());
debugPrint(s.toString());
_lastErrorURL = url;
NeonError.showSnackbar(context, e);
_lastErrorURL = url;
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(
resizeToAvoidBottomInset: false,
appBar: appBar,
body: Center(
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: body,
body: SafeArea(
child: Center(
child: ConstrainedBox(
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(
child: Text(NeonLocalizations.of(context).errorRouteNotFound(widget.uri.toString())),
body: SafeArea(
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(
resizeToAvoidBottomInset: false,
appBar: appBar,
body: Center(
child: ConstrainedBox(
constraints: NeonDialogTheme.of(context).constraints,
child: body,
body: SafeArea(
child: Center(
child: ConstrainedBox(
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(

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

@ -16,59 +16,61 @@ class FilesDetailsPage extends StatelessWidget {
appBar: AppBar(
title: Text(details.name),
),
body: ListView(
primary: true,
children: [
ColoredBox(
color: Theme.of(context).colorScheme.primary,
child: FilePreview(
bloc: bloc,
details: details,
color: Theme.of(context).colorScheme.onPrimary,
size: Size(
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height / 4,
body: SafeArea(
child: ListView(
primary: true,
children: [
ColoredBox(
color: Theme.of(context).colorScheme.primary,
child: FilePreview(
bloc: bloc,
details: details,
color: Theme.of(context).colorScheme.onPrimary,
size: Size(
MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height / 4,
),
),
),
),
DataTable(
headingRowHeight: 0,
columns: const [
DataColumn(label: SizedBox()),
DataColumn(label: SizedBox()),
],
rows: [
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) ...{
DataTable(
headingRowHeight: 0,
columns: const [
DataColumn(label: SizedBox()),
DataColumn(label: SizedBox()),
],
rows: [
for (final entry in {
details.isDirectory
? FilesLocalizations.of(context).detailsFolderSize
: FilesLocalizations.of(context).detailsFileSize: filesize(details.size, 1),
},
if (details.lastModified != null) ...{
FilesLocalizations.of(context).detailsLastModified:
details.lastModified!.toLocal().toIso8601String(),
},
if (details.isFavorite != null) ...{
FilesLocalizations.of(context).detailsIsFavorite: details.isFavorite!
? FilesLocalizations.of(context).actionYes
: FilesLocalizations.of(context).actionNo,
},
}.entries) ...[
DataRow(
cells: [
DataCell(Text(entry.key)),
DataCell(Text(entry.value)),
],
),
? 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
? FilesLocalizations.of(context).detailsFolderSize
: FilesLocalizations.of(context).detailsFileSize: filesize(details.size, 1),
},
if (details.lastModified != null) ...{
FilesLocalizations.of(context).detailsLastModified:
details.lastModified!.toLocal().toIso8601String(),
},
if (details.isFavorite != null) ...{
FilesLocalizations.of(context).detailsIsFavorite: details.isFavorite!
? FilesLocalizations.of(context).actionYes
: 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
? WebViewWidget(
controller: _webviewController!,
)
: SingleChildScrollView(
padding: const EdgeInsets.all(10),
child: Html(
data: widget.bodyData,
onLinkTap: (
final url,
final attributes,
final element,
) async {
if (url != null) {
await launchUrlString(
url,
mode: LaunchMode.externalApplication,
);
}
},
body: SafeArea(
child: widget.useWebView
? WebViewWidget(
controller: _webviewController!,
)
: SingleChildScrollView(
padding: const EdgeInsets.all(10),
child: Html(
data: widget.bodyData,
onLinkTap: (
final url,
final attributes,
final element,
) async {
if (url != null) {
await launchUrlString(
url,
mode: LaunchMode.externalApplication,
);
}
},
),
),
),
),
),
);
}

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

@ -16,15 +16,17 @@ class NewsFeedPage extends StatelessWidget {
appBar: AppBar(
title: Text(feed.title),
),
body: NewsArticlesView(
bloc: NewsArticlesBloc(
bloc,
bloc.options,
bloc.account,
id: feed.id,
listType: ListType.feed,
body: SafeArea(
child: NewsArticlesView(
bloc: NewsArticlesBloc(
bloc,
bloc.options,
bloc.account,
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(
title: Text(folder.name),
),
body: NewsFolderView(
bloc: bloc,
folder: folder,
body: SafeArea(
child: NewsFolderView(
bloc: bloc,
folder: folder,
),
),
floatingActionButton: NewsFeedFloatingActionButton(
bloc: bloc,

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

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

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

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

Loading…
Cancel
Save