mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-08 10:07:51 -06:00
Added server address validation, success message, localization
This commit is contained in:
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:dio/adapter.dart';
|
import 'package:dio/adapter.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:paperless_mobile/core/interceptor/retry_on_connection_change_interceptor.dart';
|
||||||
import 'package:paperless_mobile/extensions/security_context_extension.dart';
|
import 'package:paperless_mobile/extensions/security_context_extension.dart';
|
||||||
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
|
import 'package:paperless_mobile/features/login/model/client_certificate.dart';
|
||||||
|
|
||||||
@@ -23,6 +24,7 @@ class AuthenticationAwareDioManager {
|
|||||||
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
|
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
|
||||||
(client) => client..badCertificateCallback = (cert, host, port) => true;
|
(client) => client..badCertificateCallback = (cert, host, port) => true;
|
||||||
dio.interceptors.addAll(interceptors);
|
dio.interceptors.addAll(interceptors);
|
||||||
|
dio.interceptors.add(RetryOnConnectionChangeInterceptor(dio: dio));
|
||||||
return dio;
|
return dio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ class _ExtendedDateRangeDialogState extends State<ExtendedDateRangeDialog> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text("Select date range"),
|
insetPadding: const EdgeInsets.all(24.0),
|
||||||
|
title: Text(S.of(context).extendedDateRangeDialogTitle),
|
||||||
content: FormBuilder(
|
content: FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -55,7 +56,7 @@ class _ExtendedDateRangeDialogState extends State<ExtendedDateRangeDialog> {
|
|||||||
children: [
|
children: [
|
||||||
_buildDateRangeQueryTypeSelection(),
|
_buildDateRangeQueryTypeSelection(),
|
||||||
Text(
|
Text(
|
||||||
"Hint: You can either specify absolute values by selecting concrete dates, or you can specify a time range relative to the current date.",
|
S.of(context).extendedDateRangeDialogHintText,
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
).paddedOnly(top: 8, bottom: 16),
|
).paddedOnly(top: 8, bottom: 16),
|
||||||
Builder(
|
Builder(
|
||||||
@@ -109,12 +110,12 @@ class _ExtendedDateRangeDialogState extends State<ExtendedDateRangeDialog> {
|
|||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: DateRangeType.absolute,
|
value: DateRangeType.absolute,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
label: Text("Absolute"),
|
label: Text(S.of(context).extendedDateRangeDialogAbsoluteLabel),
|
||||||
),
|
),
|
||||||
ButtonSegment(
|
ButtonSegment(
|
||||||
value: DateRangeType.relative,
|
value: DateRangeType.relative,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
label: Text("Relative"),
|
label: Text(S.of(context).extendedDateRangeDialogRelativeLabel),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
selected: {_selectedDateRangeType},
|
selected: {_selectedDateRangeType},
|
||||||
|
|||||||
@@ -47,12 +47,14 @@ class _FormBuilderRelativeDateRangePickerState
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text("Last"),
|
Text(S.of(context).extendedDateRangeDialogRelativeLastLabel),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 70,
|
width: 80,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Offset",
|
labelText: S
|
||||||
|
.of(context)
|
||||||
|
.extendedDateRangeDialogRelativeAmountLabel,
|
||||||
),
|
),
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
FilteringTextInputFormatter.digitsOnly,
|
FilteringTextInputFormatter.digitsOnly,
|
||||||
@@ -91,7 +93,9 @@ class _FormBuilderRelativeDateRangePickerState
|
|||||||
onChanged: (value) =>
|
onChanged: (value) =>
|
||||||
field.didChange(field.value!.copyWith(unit: value)),
|
field.didChange(field.value!.copyWith(unit: value)),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Amount",
|
labelText: S
|
||||||
|
.of(context)
|
||||||
|
.extendedDateRangeDialogRelativeTimeUnitLabel,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
-4), //TODO: Wait for stable version of m3, then use AlignmentDirectional.topEnd
|
-4), //TODO: Wait for stable version of m3, then use AlignmentDirectional.topEnd
|
||||||
isLabelVisible: appliedFiltersCount > 0,
|
isLabelVisible: appliedFiltersCount > 0,
|
||||||
count: state.filter.appliedFiltersCount,
|
count: state.filter.appliedFiltersCount,
|
||||||
backgroundColor: Theme.of(context).colorScheme.errorContainer,
|
backgroundColor: Colors.red,
|
||||||
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
textColor: Colors.white,
|
||||||
child: FloatingActionButton(
|
child: FloatingActionButton(
|
||||||
child: const Icon(Icons.filter_alt_outlined),
|
child: const Icon(Icons.filter_alt_outlined),
|
||||||
onPressed: _openDocumentFilter,
|
onPressed: _openDocumentFilter,
|
||||||
@@ -115,7 +115,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
|
|||||||
expand: false,
|
expand: false,
|
||||||
snap: true,
|
snap: true,
|
||||||
initialChildSize: .9,
|
initialChildSize: .9,
|
||||||
maxChildSize: .9,
|
snapSizes: const [.9, 1],
|
||||||
builder: (context, controller) => LabelsBlocProvider(
|
builder: (context, controller) => LabelsBlocProvider(
|
||||||
child: DocumentFilterPanel(
|
child: DocumentFilterPanel(
|
||||||
initialFilter: context.read<DocumentsCubit>().state.filter,
|
initialFilter: context.read<DocumentsCubit>().state.filter,
|
||||||
|
|||||||
@@ -70,65 +70,74 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
|
|||||||
resizeToAvoidBottomInset: true,
|
resizeToAvoidBottomInset: true,
|
||||||
body: FormBuilder(
|
body: FormBuilder(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
child: ListView(
|
child: _buildFormList(context),
|
||||||
controller: widget.scrollController,
|
|
||||||
children: [
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: Container(
|
|
||||||
width: 32,
|
|
||||||
height: 4,
|
|
||||||
margin: const EdgeInsets.only(top: 16),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
S.of(context).documentFilterTitle,
|
|
||||||
style: Theme.of(context).textTheme.headlineSmall,
|
|
||||||
).paddedOnly(
|
|
||||||
top: 16.0,
|
|
||||||
left: 16.0,
|
|
||||||
bottom: 24,
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(
|
|
||||||
S.of(context).documentFilterSearchLabel,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
),
|
|
||||||
).paddedOnly(left: 8.0),
|
|
||||||
_buildQueryFormField().padded(),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: Text(
|
|
||||||
S.of(context).documentFilterAdvancedLabel,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
),
|
|
||||||
).padded(),
|
|
||||||
FormBuilderExtendedDateRangePicker(
|
|
||||||
name: DocumentModel.createdKey,
|
|
||||||
initialValue: widget.initialFilter.created,
|
|
||||||
labelText: S.of(context).documentCreatedPropertyLabel,
|
|
||||||
).padded(),
|
|
||||||
FormBuilderExtendedDateRangePicker(
|
|
||||||
name: DocumentModel.addedKey,
|
|
||||||
initialValue: widget.initialFilter.added,
|
|
||||||
labelText: S.of(context).documentAddedPropertyLabel,
|
|
||||||
).padded(),
|
|
||||||
_buildCorrespondentFormField().padded(),
|
|
||||||
_buildDocumentTypeFormField().padded(),
|
|
||||||
_buildStoragePathFormField().padded(),
|
|
||||||
_buildTagsFormField().padded(),
|
|
||||||
],
|
|
||||||
).paddedOnly(bottom: 16),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ListView _buildFormList(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
controller: widget.scrollController,
|
||||||
|
children: [
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: _buildDragHandle(context),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
S.of(context).documentFilterTitle,
|
||||||
|
style: Theme.of(context).textTheme.headlineSmall,
|
||||||
|
).paddedOnly(
|
||||||
|
top: 16.0,
|
||||||
|
left: 16.0,
|
||||||
|
bottom: 24,
|
||||||
|
),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
S.of(context).documentFilterSearchLabel,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
).paddedOnly(left: 8.0),
|
||||||
|
_buildQueryFormField().padded(),
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: Text(
|
||||||
|
S.of(context).documentFilterAdvancedLabel,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
).padded(),
|
||||||
|
FormBuilderExtendedDateRangePicker(
|
||||||
|
name: DocumentModel.createdKey,
|
||||||
|
initialValue: widget.initialFilter.created,
|
||||||
|
labelText: S.of(context).documentCreatedPropertyLabel,
|
||||||
|
).padded(),
|
||||||
|
FormBuilderExtendedDateRangePicker(
|
||||||
|
name: DocumentModel.addedKey,
|
||||||
|
initialValue: widget.initialFilter.added,
|
||||||
|
labelText: S.of(context).documentAddedPropertyLabel,
|
||||||
|
).padded(),
|
||||||
|
_buildCorrespondentFormField().padded(),
|
||||||
|
_buildDocumentTypeFormField().padded(),
|
||||||
|
_buildStoragePathFormField().padded(),
|
||||||
|
_buildTagsFormField().padded(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Container _buildDragHandle(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
// According to m3 spec
|
||||||
|
width: 32,
|
||||||
|
height: 4,
|
||||||
|
margin: const EdgeInsets.only(top: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant.withOpacity(0.4),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
BlocBuilder<LabelCubit<Tag>, LabelState<Tag>> _buildTagsFormField() {
|
BlocBuilder<LabelCubit<Tag>, LabelState<Tag>> _buildTagsFormField() {
|
||||||
return BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
return BlocBuilder<LabelCubit<Tag>, LabelState<Tag>>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
|||||||
76
lib/features/home/view/widget/verify_identity_page.dart
Normal file
76
lib/features/home/view/widget/verify_identity_page.dart
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hydrated_bloc/hydrated_bloc.dart';
|
||||||
|
import 'package:paperless_api/paperless_api.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/label_repository.dart';
|
||||||
|
import 'package:paperless_mobile/core/repository/saved_view_repository.dart';
|
||||||
|
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
|
||||||
|
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/features/settings/bloc/application_settings_cubit.dart';
|
||||||
|
import 'package:paperless_mobile/generated/l10n.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class VerifyIdentityPage extends StatelessWidget {
|
||||||
|
const VerifyIdentityPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
child: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.background,
|
||||||
|
title: Text(S.of(context).verifyIdentityPageTitle),
|
||||||
|
),
|
||||||
|
body: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(S.of(context).verifyIdentityPageDescriptionText)
|
||||||
|
.paddedSymmetrically(horizontal: 16),
|
||||||
|
const Icon(
|
||||||
|
Icons.fingerprint,
|
||||||
|
size: 96,
|
||||||
|
),
|
||||||
|
Wrap(
|
||||||
|
alignment: WrapAlignment.spaceBetween,
|
||||||
|
runAlignment: WrapAlignment.spaceBetween,
|
||||||
|
runSpacing: 8,
|
||||||
|
spacing: 8,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => _logout(context),
|
||||||
|
child: Text(
|
||||||
|
S.of(context).verifyIdentityPageLogoutButtonLabel,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () => context
|
||||||
|
.read<AuthenticationCubit>()
|
||||||
|
.restoreSessionState(context
|
||||||
|
.read<ApplicationSettingsCubit>()
|
||||||
|
.state
|
||||||
|
.isLocalAuthenticationEnabled),
|
||||||
|
child: Text(S
|
||||||
|
.of(context)
|
||||||
|
.verifyIdentityPageVerifyIdentityButtonLabel),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).padded(16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _logout(BuildContext context) {
|
||||||
|
context.read<AuthenticationCubit>().logout();
|
||||||
|
context.read<LabelRepository<Tag>>().clear();
|
||||||
|
context.read<LabelRepository<Correspondent>>().clear();
|
||||||
|
context.read<LabelRepository<DocumentType>>().clear();
|
||||||
|
context.read<LabelRepository<StoragePath>>().clear();
|
||||||
|
context.read<SavedViewRepository>().clear();
|
||||||
|
HydratedBloc.storage.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,26 +17,49 @@ class ServerAddressFormField extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ServerAddressFormFieldState extends State<ServerAddressFormField> {
|
class _ServerAddressFormFieldState extends State<ServerAddressFormField> {
|
||||||
|
static const _ipv4Regex = r"((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4}";
|
||||||
|
static const _ipv6Regex =
|
||||||
|
r"(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))";
|
||||||
|
static final _urlRegex = RegExp(
|
||||||
|
r"^(https?:\/\/)(([\da-z\.-]+)\.([a-z\.]{2,6})|(((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4})|((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))))(:\d{1,5})?([\/\w \.-]*)*\/?$");
|
||||||
|
final TextEditingController _textEditingController = TextEditingController();
|
||||||
ReachabilityStatus _reachabilityStatus = ReachabilityStatus.undefined;
|
ReachabilityStatus _reachabilityStatus = ReachabilityStatus.undefined;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FormBuilderTextField(
|
return FormBuilderTextField(
|
||||||
key: const ValueKey('login-server-address'),
|
key: const ValueKey('login-server-address'),
|
||||||
|
controller: _textEditingController,
|
||||||
name: ServerAddressFormField.fkServerAddress,
|
name: ServerAddressFormField.fkServerAddress,
|
||||||
validator: FormBuilderValidators.required(
|
validator: FormBuilderValidators.compose(
|
||||||
errorText: S.of(context).loginPageServerUrlValidatorMessageText,
|
[
|
||||||
|
FormBuilderValidators.required(
|
||||||
|
errorText:
|
||||||
|
S.of(context).loginPageServerUrlValidatorMessageRequiredText,
|
||||||
|
),
|
||||||
|
FormBuilderValidators.match(
|
||||||
|
_urlRegex.pattern,
|
||||||
|
errorText: S
|
||||||
|
.of(context)
|
||||||
|
.loginPageServerUrlValidatorMessageInvalidAddressText,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.deny(r".*/$"),
|
|
||||||
FilteringTextInputFormatter.deny(r"\s"),
|
|
||||||
],
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
suffixIcon: _buildIsReachableIcon(),
|
suffixIcon: _buildIsReachableIcon(),
|
||||||
hintText: "http://192.168.1.50:8000",
|
hintText: "http://192.168.1.50:8000",
|
||||||
labelText: S.of(context).loginPageServerUrlFieldLabel,
|
labelText: S.of(context).loginPageServerUrlFieldLabel,
|
||||||
),
|
),
|
||||||
onChanged: _updateIsAddressReachableStatus,
|
onChanged: _updateIsAddressReachableStatus,
|
||||||
|
onSubmitted: (value) {
|
||||||
|
if (value == null) return;
|
||||||
|
// Remove trailing slash if it is a valid address.
|
||||||
|
final address = value.trim();
|
||||||
|
_textEditingController.text = address;
|
||||||
|
if (_urlRegex.hasMatch(address) && address.endsWith("/")) {
|
||||||
|
_textEditingController.text = address.replaceAll(RegExp(r'\/$'), '');
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +83,7 @@ class _ServerAddressFormFieldState extends State<ServerAddressFormField> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _updateIsAddressReachableStatus(String? address) async {
|
void _updateIsAddressReachableStatus(String? address) async {
|
||||||
if (address == null || address.isEmpty) {
|
if (address == null || !_urlRegex.hasMatch(address)) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_reachabilityStatus = ReachabilityStatus.undefined;
|
_reachabilityStatus = ReachabilityStatus.undefined;
|
||||||
});
|
});
|
||||||
@@ -70,12 +93,12 @@ class _ServerAddressFormFieldState extends State<ServerAddressFormField> {
|
|||||||
setState(() => _reachabilityStatus = ReachabilityStatus.testing);
|
setState(() => _reachabilityStatus = ReachabilityStatus.testing);
|
||||||
final isReachable = await context
|
final isReachable = await context
|
||||||
.read<ConnectivityStatusService>()
|
.read<ConnectivityStatusService>()
|
||||||
.isServerReachable(address);
|
.isServerReachable(address.trim());
|
||||||
if (isReachable) {
|
setState(
|
||||||
setState(() => _reachabilityStatus = ReachabilityStatus.reachable);
|
() => _reachabilityStatus = isReachable
|
||||||
} else {
|
? ReachabilityStatus.reachable
|
||||||
setState(() => _reachabilityStatus = ReachabilityStatus.notReachable);
|
: ReachabilityStatus.notReachable,
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -260,11 +260,25 @@
|
|||||||
"@errorMessageUnsupportedFileFormat": {},
|
"@errorMessageUnsupportedFileFormat": {},
|
||||||
"errorReportLabel": "NAHLÁSIT",
|
"errorReportLabel": "NAHLÁSIT",
|
||||||
"@errorReportLabel": {},
|
"@errorReportLabel": {},
|
||||||
"extendedDateRangePickerAfterLabel": "",
|
"extendedDateRangeDialogAbsoluteLabel": "Absolute",
|
||||||
|
"@extendedDateRangeDialogAbsoluteLabel": {},
|
||||||
|
"extendedDateRangeDialogHintText": "Hint: Apart from concrete dates, you can also specify a time range relative to the current date.",
|
||||||
|
"@extendedDateRangeDialogHintText": {},
|
||||||
|
"extendedDateRangeDialogRelativeAmountLabel": "Amount",
|
||||||
|
"@extendedDateRangeDialogRelativeAmountLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeLabel": "Relative",
|
||||||
|
"@extendedDateRangeDialogRelativeLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeLastLabel": "Last",
|
||||||
|
"@extendedDateRangeDialogRelativeLastLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeTimeUnitLabel": "Time unit",
|
||||||
|
"@extendedDateRangeDialogRelativeTimeUnitLabel": {},
|
||||||
|
"extendedDateRangeDialogTitle": "Select date range",
|
||||||
|
"@extendedDateRangeDialogTitle": {},
|
||||||
|
"extendedDateRangePickerAfterLabel": "After",
|
||||||
"@extendedDateRangePickerAfterLabel": {},
|
"@extendedDateRangePickerAfterLabel": {},
|
||||||
"extendedDateRangePickerBeforeLabel": "",
|
"extendedDateRangePickerBeforeLabel": "Before",
|
||||||
"@extendedDateRangePickerBeforeLabel": {},
|
"@extendedDateRangePickerBeforeLabel": {},
|
||||||
"extendedDateRangePickerDayText": "{count, plural, other{}}",
|
"extendedDateRangePickerDayText": "{count, plural, zero{} one{day} other{days}}",
|
||||||
"@extendedDateRangePickerDayText": {
|
"@extendedDateRangePickerDayText": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"count": {}
|
"count": {}
|
||||||
@@ -284,9 +298,9 @@
|
|||||||
"count": {}
|
"count": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extendedDateRangePickerLastText": "",
|
"extendedDateRangePickerLastText": "Last",
|
||||||
"@extendedDateRangePickerLastText": {},
|
"@extendedDateRangePickerLastText": {},
|
||||||
"extendedDateRangePickerLastWeeksLabel": "{count, plural, other{}}",
|
"extendedDateRangePickerLastWeeksLabel": "{count, plural, zero{} one{Last week} other{Last {count} weeks}}",
|
||||||
"@extendedDateRangePickerLastWeeksLabel": {
|
"@extendedDateRangePickerLastWeeksLabel": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"count": {}
|
"count": {}
|
||||||
@@ -298,7 +312,7 @@
|
|||||||
"count": {}
|
"count": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extendedDateRangePickerMonthText": "{count, plural, other{}}",
|
"extendedDateRangePickerMonthText": "{count, plural, zero{} one{month} other{months}}",
|
||||||
"@extendedDateRangePickerMonthText": {
|
"@extendedDateRangePickerMonthText": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"count": {}
|
"count": {}
|
||||||
@@ -306,13 +320,13 @@
|
|||||||
},
|
},
|
||||||
"extendedDateRangePickerToLabel": "Do",
|
"extendedDateRangePickerToLabel": "Do",
|
||||||
"@extendedDateRangePickerToLabel": {},
|
"@extendedDateRangePickerToLabel": {},
|
||||||
"extendedDateRangePickerWeekText": "{count, plural, other{}}",
|
"extendedDateRangePickerWeekText": "{count, plural, zero{} one{week} other{weeks}}",
|
||||||
"@extendedDateRangePickerWeekText": {
|
"@extendedDateRangePickerWeekText": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"count": {}
|
"count": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extendedDateRangePickerYearText": "{count, plural, other{}}",
|
"extendedDateRangePickerYearText": "{count, plural, zero{} one{year} other{years}}",
|
||||||
"@extendedDateRangePickerYearText": {
|
"@extendedDateRangePickerYearText": {
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"count": {}
|
"count": {}
|
||||||
@@ -424,8 +438,10 @@
|
|||||||
"@loginPagePasswordValidatorMessageText": {},
|
"@loginPagePasswordValidatorMessageText": {},
|
||||||
"loginPageServerUrlFieldLabel": "'Adresa serveru",
|
"loginPageServerUrlFieldLabel": "'Adresa serveru",
|
||||||
"@loginPageServerUrlFieldLabel": {},
|
"@loginPageServerUrlFieldLabel": {},
|
||||||
"loginPageServerUrlValidatorMessageText": "Adresa serveru nesmí být prázdná.",
|
"loginPageServerUrlValidatorMessageInvalidAddressText": "Invalid address.",
|
||||||
"@loginPageServerUrlValidatorMessageText": {},
|
"@loginPageServerUrlValidatorMessageInvalidAddressText": {},
|
||||||
|
"loginPageServerUrlValidatorMessageRequiredText": "Adresa serveru nesmí být prázdná.",
|
||||||
|
"@loginPageServerUrlValidatorMessageRequiredText": {},
|
||||||
"loginPageTitle": "Propojit s Paperless",
|
"loginPageTitle": "Propojit s Paperless",
|
||||||
"@loginPageTitle": {},
|
"@loginPageTitle": {},
|
||||||
"loginPageUsernameLabel": "Jméno uživatele",
|
"loginPageUsernameLabel": "Jméno uživatele",
|
||||||
@@ -499,5 +515,13 @@
|
|||||||
"tagInboxTagPropertyLabel": "Tag inboxu",
|
"tagInboxTagPropertyLabel": "Tag inboxu",
|
||||||
"@tagInboxTagPropertyLabel": {},
|
"@tagInboxTagPropertyLabel": {},
|
||||||
"uploadPageAutomaticallInferredFieldsHintText": "Pokud specifikuješ hodnoty pro tato pole, paperless instance nebude automaticky přiřazovat naučené hodnoty. Pokud mají být tato pole automaticky vyplňována, nevyplňujte zde nic.",
|
"uploadPageAutomaticallInferredFieldsHintText": "Pokud specifikuješ hodnoty pro tato pole, paperless instance nebude automaticky přiřazovat naučené hodnoty. Pokud mají být tato pole automaticky vyplňována, nevyplňujte zde nic.",
|
||||||
"@uploadPageAutomaticallInferredFieldsHintText": {}
|
"@uploadPageAutomaticallInferredFieldsHintText": {},
|
||||||
|
"verifyIdentityPageDescriptionText": "Use the configured biometric factor to authenticate and unlock your documents.",
|
||||||
|
"@verifyIdentityPageDescriptionText": {},
|
||||||
|
"verifyIdentityPageLogoutButtonLabel": "Disconnect",
|
||||||
|
"@verifyIdentityPageLogoutButtonLabel": {},
|
||||||
|
"verifyIdentityPageTitle": "Verify your identity",
|
||||||
|
"@verifyIdentityPageTitle": {},
|
||||||
|
"verifyIdentityPageVerifyIdentityButtonLabel": "Verify Identity",
|
||||||
|
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
|
||||||
}
|
}
|
||||||
@@ -260,6 +260,20 @@
|
|||||||
"@errorMessageUnsupportedFileFormat": {},
|
"@errorMessageUnsupportedFileFormat": {},
|
||||||
"errorReportLabel": "MELDEN",
|
"errorReportLabel": "MELDEN",
|
||||||
"@errorReportLabel": {},
|
"@errorReportLabel": {},
|
||||||
|
"extendedDateRangeDialogAbsoluteLabel": "Absolut",
|
||||||
|
"@extendedDateRangeDialogAbsoluteLabel": {},
|
||||||
|
"extendedDateRangeDialogHintText": "Hinweis: Neben konkreten Daten kannst du den Zeitraum auch über eine relative Zeitspanne einschränken.",
|
||||||
|
"@extendedDateRangeDialogHintText": {},
|
||||||
|
"extendedDateRangeDialogRelativeAmountLabel": "Anzahl",
|
||||||
|
"@extendedDateRangeDialogRelativeAmountLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeLabel": "Relativ",
|
||||||
|
"@extendedDateRangeDialogRelativeLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeLastLabel": "Letzte",
|
||||||
|
"@extendedDateRangeDialogRelativeLastLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeTimeUnitLabel": "Zeiteinheit",
|
||||||
|
"@extendedDateRangeDialogRelativeTimeUnitLabel": {},
|
||||||
|
"extendedDateRangeDialogTitle": "Wähle Zeitraum",
|
||||||
|
"@extendedDateRangeDialogTitle": {},
|
||||||
"extendedDateRangePickerAfterLabel": "Nach",
|
"extendedDateRangePickerAfterLabel": "Nach",
|
||||||
"@extendedDateRangePickerAfterLabel": {},
|
"@extendedDateRangePickerAfterLabel": {},
|
||||||
"extendedDateRangePickerBeforeLabel": "Vor",
|
"extendedDateRangePickerBeforeLabel": "Vor",
|
||||||
@@ -346,7 +360,7 @@
|
|||||||
"@inboxPageMarkAllAsSeenConfirmationDialogTitleText": {},
|
"@inboxPageMarkAllAsSeenConfirmationDialogTitleText": {},
|
||||||
"inboxPageMarkAllAsSeenLabel": "Alle gesehen",
|
"inboxPageMarkAllAsSeenLabel": "Alle gesehen",
|
||||||
"@inboxPageMarkAllAsSeenLabel": {},
|
"@inboxPageMarkAllAsSeenLabel": {},
|
||||||
"inboxPageMarkAsSeenText": "Als gelesen markieren",
|
"inboxPageMarkAsSeenText": "Als gesehen markieren",
|
||||||
"@inboxPageMarkAsSeenText": {},
|
"@inboxPageMarkAsSeenText": {},
|
||||||
"inboxPageNoNewDocumentsRefreshLabel": "Neu laden",
|
"inboxPageNoNewDocumentsRefreshLabel": "Neu laden",
|
||||||
"@inboxPageNoNewDocumentsRefreshLabel": {},
|
"@inboxPageNoNewDocumentsRefreshLabel": {},
|
||||||
@@ -424,8 +438,10 @@
|
|||||||
"@loginPagePasswordValidatorMessageText": {},
|
"@loginPagePasswordValidatorMessageText": {},
|
||||||
"loginPageServerUrlFieldLabel": "Server-Adresse",
|
"loginPageServerUrlFieldLabel": "Server-Adresse",
|
||||||
"@loginPageServerUrlFieldLabel": {},
|
"@loginPageServerUrlFieldLabel": {},
|
||||||
"loginPageServerUrlValidatorMessageText": "Server-Addresse darf nicht leer sein.",
|
"loginPageServerUrlValidatorMessageInvalidAddressText": "Ungültige Adresse.",
|
||||||
"@loginPageServerUrlValidatorMessageText": {},
|
"@loginPageServerUrlValidatorMessageInvalidAddressText": {},
|
||||||
|
"loginPageServerUrlValidatorMessageRequiredText": "Server-Addresse darf nicht leer sein.",
|
||||||
|
"@loginPageServerUrlValidatorMessageRequiredText": {},
|
||||||
"loginPageTitle": "Mit Paperless verbinden",
|
"loginPageTitle": "Mit Paperless verbinden",
|
||||||
"@loginPageTitle": {},
|
"@loginPageTitle": {},
|
||||||
"loginPageUsernameLabel": "Nutzername",
|
"loginPageUsernameLabel": "Nutzername",
|
||||||
@@ -499,5 +515,13 @@
|
|||||||
"tagInboxTagPropertyLabel": "Posteingangs-Tag",
|
"tagInboxTagPropertyLabel": "Posteingangs-Tag",
|
||||||
"@tagInboxTagPropertyLabel": {},
|
"@tagInboxTagPropertyLabel": {},
|
||||||
"uploadPageAutomaticallInferredFieldsHintText": "Wenn Werte für diese Felder angegeben werden, wird Paperless nicht automatisch einen Wert zuweisen. Wenn diese Felder automatisch von Paperless erkannt werden sollen, sollten die Felder leer bleiben.",
|
"uploadPageAutomaticallInferredFieldsHintText": "Wenn Werte für diese Felder angegeben werden, wird Paperless nicht automatisch einen Wert zuweisen. Wenn diese Felder automatisch von Paperless erkannt werden sollen, sollten die Felder leer bleiben.",
|
||||||
"@uploadPageAutomaticallInferredFieldsHintText": {}
|
"@uploadPageAutomaticallInferredFieldsHintText": {},
|
||||||
|
"verifyIdentityPageDescriptionText": "Benutze den konfigurierten Biometrischen Faktor um dich zu identifizieren und auf deine Dokumente zuzugreifen.",
|
||||||
|
"@verifyIdentityPageDescriptionText": {},
|
||||||
|
"verifyIdentityPageLogoutButtonLabel": "Verbindung trennen",
|
||||||
|
"@verifyIdentityPageLogoutButtonLabel": {},
|
||||||
|
"verifyIdentityPageTitle": "Verifiziere deine Identität",
|
||||||
|
"@verifyIdentityPageTitle": {},
|
||||||
|
"verifyIdentityPageVerifyIdentityButtonLabel": "Identität verifizieren",
|
||||||
|
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
|
||||||
}
|
}
|
||||||
@@ -260,6 +260,20 @@
|
|||||||
"@errorMessageUnsupportedFileFormat": {},
|
"@errorMessageUnsupportedFileFormat": {},
|
||||||
"errorReportLabel": "REPORT",
|
"errorReportLabel": "REPORT",
|
||||||
"@errorReportLabel": {},
|
"@errorReportLabel": {},
|
||||||
|
"extendedDateRangeDialogAbsoluteLabel": "Absolute",
|
||||||
|
"@extendedDateRangeDialogAbsoluteLabel": {},
|
||||||
|
"extendedDateRangeDialogHintText": "Hint: Apart from concrete dates, you can also specify a time range relative to the current date.",
|
||||||
|
"@extendedDateRangeDialogHintText": {},
|
||||||
|
"extendedDateRangeDialogRelativeAmountLabel": "Amount",
|
||||||
|
"@extendedDateRangeDialogRelativeAmountLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeLabel": "Relative",
|
||||||
|
"@extendedDateRangeDialogRelativeLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeLastLabel": "Last",
|
||||||
|
"@extendedDateRangeDialogRelativeLastLabel": {},
|
||||||
|
"extendedDateRangeDialogRelativeTimeUnitLabel": "Time unit",
|
||||||
|
"@extendedDateRangeDialogRelativeTimeUnitLabel": {},
|
||||||
|
"extendedDateRangeDialogTitle": "Select date range",
|
||||||
|
"@extendedDateRangeDialogTitle": {},
|
||||||
"extendedDateRangePickerAfterLabel": "After",
|
"extendedDateRangePickerAfterLabel": "After",
|
||||||
"@extendedDateRangePickerAfterLabel": {},
|
"@extendedDateRangePickerAfterLabel": {},
|
||||||
"extendedDateRangePickerBeforeLabel": "Before",
|
"extendedDateRangePickerBeforeLabel": "Before",
|
||||||
@@ -424,8 +438,10 @@
|
|||||||
"@loginPagePasswordValidatorMessageText": {},
|
"@loginPagePasswordValidatorMessageText": {},
|
||||||
"loginPageServerUrlFieldLabel": "Server Address",
|
"loginPageServerUrlFieldLabel": "Server Address",
|
||||||
"@loginPageServerUrlFieldLabel": {},
|
"@loginPageServerUrlFieldLabel": {},
|
||||||
"loginPageServerUrlValidatorMessageText": "Server address must not be empty.",
|
"loginPageServerUrlValidatorMessageInvalidAddressText": "Invalid address.",
|
||||||
"@loginPageServerUrlValidatorMessageText": {},
|
"@loginPageServerUrlValidatorMessageInvalidAddressText": {},
|
||||||
|
"loginPageServerUrlValidatorMessageRequiredText": "Server address must not be empty.",
|
||||||
|
"@loginPageServerUrlValidatorMessageRequiredText": {},
|
||||||
"loginPageTitle": "Connect to Paperless",
|
"loginPageTitle": "Connect to Paperless",
|
||||||
"@loginPageTitle": {},
|
"@loginPageTitle": {},
|
||||||
"loginPageUsernameLabel": "Username",
|
"loginPageUsernameLabel": "Username",
|
||||||
@@ -499,5 +515,13 @@
|
|||||||
"tagInboxTagPropertyLabel": "Inbox-Tag",
|
"tagInboxTagPropertyLabel": "Inbox-Tag",
|
||||||
"@tagInboxTagPropertyLabel": {},
|
"@tagInboxTagPropertyLabel": {},
|
||||||
"uploadPageAutomaticallInferredFieldsHintText": "If you specify values for these fields, your paperless instance will not automatically derive a value. If you want these values to be automatically populated by your server, leave the fields blank.",
|
"uploadPageAutomaticallInferredFieldsHintText": "If you specify values for these fields, your paperless instance will not automatically derive a value. If you want these values to be automatically populated by your server, leave the fields blank.",
|
||||||
"@uploadPageAutomaticallInferredFieldsHintText": {}
|
"@uploadPageAutomaticallInferredFieldsHintText": {},
|
||||||
|
"verifyIdentityPageDescriptionText": "Use the configured biometric factor to authenticate and unlock your documents.",
|
||||||
|
"@verifyIdentityPageDescriptionText": {},
|
||||||
|
"verifyIdentityPageLogoutButtonLabel": "Disconnect",
|
||||||
|
"@verifyIdentityPageLogoutButtonLabel": {},
|
||||||
|
"verifyIdentityPageTitle": "Verify your identity",
|
||||||
|
"@verifyIdentityPageTitle": {},
|
||||||
|
"verifyIdentityPageVerifyIdentityButtonLabel": "Verify Identity",
|
||||||
|
"@verifyIdentityPageVerifyIdentityButtonLabel": {}
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,7 @@ import 'package:paperless_mobile/features/app_intro/application_intro_slideshow.
|
|||||||
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
import 'package:paperless_mobile/features/document_upload/cubit/document_upload_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
import 'package:paperless_mobile/features/document_upload/view/document_upload_preparation_page.dart';
|
||||||
import 'package:paperless_mobile/features/home/view/home_page.dart';
|
import 'package:paperless_mobile/features/home/view/home_page.dart';
|
||||||
|
import 'package:paperless_mobile/features/home/view/widget/verify_identity_page.dart';
|
||||||
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
import 'package:paperless_mobile/features/login/bloc/authentication_cubit.dart';
|
||||||
import 'package:paperless_mobile/features/login/bloc/authentication_state.dart';
|
import 'package:paperless_mobile/features/login/bloc/authentication_state.dart';
|
||||||
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
import 'package:paperless_mobile/features/login/services/authentication_service.dart';
|
||||||
@@ -349,6 +350,9 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (success) {
|
if (success) {
|
||||||
|
Fluttertoast.showToast(
|
||||||
|
msg: S.of(context).documentUploadSuccessText,
|
||||||
|
);
|
||||||
SystemNavigator.pop();
|
SystemNavigator.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,7 +401,7 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
|||||||
} else {
|
} else {
|
||||||
if (authentication.wasLoginStored &&
|
if (authentication.wasLoginStored &&
|
||||||
!(authentication.wasLocalAuthenticationSuccessful ?? false)) {
|
!(authentication.wasLocalAuthenticationSuccessful ?? false)) {
|
||||||
return const BiometricAuthenticationPage();
|
return const VerifyIdentityPage();
|
||||||
}
|
}
|
||||||
return const LoginPage();
|
return const LoginPage();
|
||||||
}
|
}
|
||||||
@@ -406,50 +410,3 @@ class _AuthenticationWrapperState extends State<AuthenticationWrapper> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BiometricAuthenticationPage extends StatelessWidget {
|
|
||||||
const BiometricAuthenticationPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Material(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"The app is locked!",
|
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"You can now either try to authenticate again or disconnect from the current server.",
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
|
||||||
).padded(),
|
|
||||||
const SizedBox(height: 48),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: [
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
context.read<AuthenticationCubit>().logout();
|
|
||||||
context.read();
|
|
||||||
HydratedBloc.storage.clear();
|
|
||||||
},
|
|
||||||
child: const Text("Log out"),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () => context
|
|
||||||
.read<AuthenticationCubit>()
|
|
||||||
.restoreSessionState(context
|
|
||||||
.read<ApplicationSettingsCubit>()
|
|
||||||
.state
|
|
||||||
.isLocalAuthenticationEnabled),
|
|
||||||
child: const Text("Authenticate"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -35,8 +35,24 @@ void showSnackBar(
|
|||||||
..hideCurrentSnackBar()
|
..hideCurrentSnackBar()
|
||||||
..showSnackBar(
|
..showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: RichText(
|
||||||
message + (details != null ? ' ($details)' : ''),
|
maxLines: 5,
|
||||||
|
text: TextSpan(
|
||||||
|
text: message,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||||
|
),
|
||||||
|
children: <TextSpan>[
|
||||||
|
if (details != null)
|
||||||
|
TextSpan(
|
||||||
|
text: "\n$details",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
fontSize: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
action: action != null
|
action: action != null
|
||||||
? SnackBarAction(
|
? SnackBarAction(
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:paperless_api/src/models/paperless_server_exception.dart';
|
import 'package:paperless_api/src/models/paperless_server_exception.dart';
|
||||||
import 'package:paperless_api/src/modules/authentication_api/authentication_api.dart';
|
import 'package:paperless_api/src/modules/authentication_api/authentication_api.dart';
|
||||||
@@ -12,7 +14,6 @@ class PaperlessAuthenticationApiImpl implements PaperlessAuthenticationApi {
|
|||||||
required String username,
|
required String username,
|
||||||
required String password,
|
required String password,
|
||||||
}) async {
|
}) async {
|
||||||
print(client.hashCode);
|
|
||||||
late Response response;
|
late Response response;
|
||||||
try {
|
try {
|
||||||
response = await client.post(
|
response = await client.post(
|
||||||
@@ -29,7 +30,11 @@ class PaperlessAuthenticationApiImpl implements PaperlessAuthenticationApi {
|
|||||||
httpStatusCode: error.response?.statusCode,
|
httpStatusCode: error.response?.statusCode,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw error.error;
|
log(error.message);
|
||||||
|
throw PaperlessServerException(
|
||||||
|
ErrorCode.authenticationFailed,
|
||||||
|
details: error.message,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
|
|||||||
data: path.toJson(),
|
data: path.toJson(),
|
||||||
);
|
);
|
||||||
if (response.statusCode == HttpStatus.created) {
|
if (response.statusCode == HttpStatus.created) {
|
||||||
return StoragePath.fromJson(jsonDecode(response.data));
|
return StoragePath.fromJson(response.data);
|
||||||
}
|
}
|
||||||
throw PaperlessServerException(ErrorCode.storagePathCreateFailed,
|
throw PaperlessServerException(ErrorCode.storagePathCreateFailed,
|
||||||
httpStatusCode: response.statusCode);
|
httpStatusCode: response.statusCode);
|
||||||
@@ -282,7 +282,7 @@ class PaperlessLabelApiImpl implements PaperlessLabelsApi {
|
|||||||
data: path.toJson(),
|
data: path.toJson(),
|
||||||
);
|
);
|
||||||
if (response.statusCode == HttpStatus.ok) {
|
if (response.statusCode == HttpStatus.ok) {
|
||||||
return StoragePath.fromJson(jsonDecode(response.data));
|
return StoragePath.fromJson(response.data);
|
||||||
}
|
}
|
||||||
throw const PaperlessServerException(ErrorCode.unknown);
|
throw const PaperlessServerException(ErrorCode.unknown);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ dependencies:
|
|||||||
json_annotation: ^4.7.0
|
json_annotation: ^4.7.0
|
||||||
pretty_dio_logger: ^1.2.0-beta-1
|
pretty_dio_logger: ^1.2.0-beta-1
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
Reference in New Issue
Block a user