mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 06:07:54 -06:00
Initial commit
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:flutter_paperless_mobile/features/login/model/client_certificate.dart';
|
||||
import 'package:flutter_paperless_mobile/features/login/view/widgets/password_text_field.dart';
|
||||
import 'package:flutter_paperless_mobile/generated/l10n.dart';
|
||||
|
||||
class ClientCertificateFormField extends StatefulWidget {
|
||||
static const fkClientCertificate = 'clientCertificate';
|
||||
const ClientCertificateFormField({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ClientCertificateFormField> createState() => _ClientCertificateFormFieldState();
|
||||
}
|
||||
|
||||
class _ClientCertificateFormFieldState extends State<ClientCertificateFormField> {
|
||||
File? _selectedFile;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FormBuilderField<ClientCertificate?>(
|
||||
initialValue: null,
|
||||
validator: (value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
assert(_selectedFile != null);
|
||||
if (_selectedFile?.path.split(".").last != 'pfx') {
|
||||
return S.of(context).loginPageClientCertificateSettingInvalidFileFormatValidationText;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
builder: (field) {
|
||||
return ExpansionTile(
|
||||
title: Text(S.of(context).loginPageClientCertificateSettingLabel),
|
||||
subtitle: Text(S.of(context).loginPageClientCertificateSettingDescriptionText),
|
||||
children: [
|
||||
InputDecorator(
|
||||
decoration: InputDecoration(
|
||||
errorText: field.errorText,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
leading: ElevatedButton(
|
||||
onPressed: () => _onSelectFile(field),
|
||||
child: Text(S.of(context).genericActionSelectText),
|
||||
),
|
||||
title: _buildSelectedFileText(field),
|
||||
trailing: AbsorbPointer(
|
||||
absorbing: field.value == null,
|
||||
child: _selectedFile != null
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => setState(() {
|
||||
_selectedFile = null;
|
||||
field.didChange(null);
|
||||
}),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
if (_selectedFile != null) ...[
|
||||
ObscuredInputTextFormField(
|
||||
initialValue: field.value?.passphrase,
|
||||
onChanged: (value) => field.didChange(
|
||||
field.value?.copyWith(passphrase: value),
|
||||
),
|
||||
label: S.of(context).loginPageClientCertificatePassphraseLabel,
|
||||
).padded(),
|
||||
] else
|
||||
...[]
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
name: ClientCertificateFormField.fkClientCertificate,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onSelectFile(FormFieldState<ClientCertificate?> field) async {
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles();
|
||||
if (result != null && result.files.single.path != null) {
|
||||
File file = File(result.files.single.path!);
|
||||
setState(() {
|
||||
_selectedFile = file;
|
||||
});
|
||||
final changedValue = field.value?.copyWith(bytes: file.readAsBytesSync()) ??
|
||||
ClientCertificate(bytes: file.readAsBytesSync());
|
||||
field.didChange(changedValue);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildSelectedFileText(FormFieldState<ClientCertificate?> field) {
|
||||
if (field.value == null) {
|
||||
assert(_selectedFile == null);
|
||||
return Text(
|
||||
S.of(context).loginPageClientCertificateSettingSelectFileText,
|
||||
style: TextStyle(color: Theme.of(context).hintColor),
|
||||
);
|
||||
} else {
|
||||
assert(_selectedFile != null);
|
||||
return Text(
|
||||
_selectedFile!.path.split("/").last,
|
||||
style: const TextStyle(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
lib/features/login/view/widgets/password_text_field.dart
Normal file
53
lib/features/login/view/widgets/password_text_field.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ObscuredInputTextFormField extends StatefulWidget {
|
||||
final String? initialValue;
|
||||
final String label;
|
||||
final void Function(String?) onChanged;
|
||||
final FormFieldValidator<String>? validator;
|
||||
|
||||
const ObscuredInputTextFormField({
|
||||
super.key,
|
||||
required this.onChanged,
|
||||
required this.label,
|
||||
this.validator,
|
||||
this.initialValue,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ObscuredInputTextFormField> createState() => _ObscuredInputTextFormFieldState();
|
||||
}
|
||||
|
||||
class _ObscuredInputTextFormFieldState extends State<ObscuredInputTextFormField> {
|
||||
bool _showPassword = false;
|
||||
final FocusNode _passwordFocusNode = FocusNode();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_passwordFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextFormField(
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
validator: widget.validator,
|
||||
initialValue: widget.initialValue,
|
||||
focusNode: _passwordFocusNode,
|
||||
obscureText: !_showPassword,
|
||||
autocorrect: false,
|
||||
onChanged: widget.onChanged,
|
||||
autofillHints: const [AutofillHints.password],
|
||||
decoration: InputDecoration(
|
||||
label: Text(widget.label),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(_showPassword ? Icons.visibility_off : Icons.visibility),
|
||||
onPressed: () => setState(() {
|
||||
_showPassword = !_showPassword;
|
||||
}),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_paperless_mobile/core/service/connectivity_status.service.dart';
|
||||
import 'package:flutter_paperless_mobile/di_initializer.dart';
|
||||
import 'package:flutter_paperless_mobile/generated/l10n.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
|
||||
class ServerAddressFormField extends StatefulWidget {
|
||||
static const String fkServerAddress = "serverAddress";
|
||||
const ServerAddressFormField({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ServerAddressFormField> createState() => _ServerAddressFormFieldState();
|
||||
}
|
||||
|
||||
class _ServerAddressFormFieldState extends State<ServerAddressFormField> {
|
||||
ReachabilityStatus _reachabilityStatus = ReachabilityStatus.undefined;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FormBuilderTextField(
|
||||
name: ServerAddressFormField.fkServerAddress,
|
||||
validator: FormBuilderValidators.required(
|
||||
errorText: S.of(context).loginPageServerUrlValidatorMessageText,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: _buildIsReachableIcon(),
|
||||
hintText: "http://192.168.1.50:8000",
|
||||
labelText: S.of(context).loginPageServerUrlFieldLabel,
|
||||
),
|
||||
onSubmitted: _updateIsAddressReachableStatus,
|
||||
);
|
||||
}
|
||||
|
||||
Widget? _buildIsReachableIcon() {
|
||||
switch (_reachabilityStatus) {
|
||||
case ReachabilityStatus.reachable:
|
||||
return const Icon(
|
||||
Icons.done,
|
||||
color: Colors.green,
|
||||
);
|
||||
case ReachabilityStatus.notReachable:
|
||||
return Icon(
|
||||
Icons.close,
|
||||
color: Theme.of(context).colorScheme.error,
|
||||
);
|
||||
case ReachabilityStatus.testing:
|
||||
return const RefreshProgressIndicator();
|
||||
case ReachabilityStatus.undefined:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void _updateIsAddressReachableStatus(String? address) async {
|
||||
if (address == null || address.isEmpty) {
|
||||
setState(() {
|
||||
_reachabilityStatus = ReachabilityStatus.undefined;
|
||||
});
|
||||
return;
|
||||
}
|
||||
//https://stackoverflow.com/questions/49648022/check-whether-there-is-an-internet-connection-available-on-flutter-app
|
||||
setState(() => _reachabilityStatus = ReachabilityStatus.testing);
|
||||
final isReachable = await getIt<ConnectivityStatusService>().isServerReachable(address);
|
||||
if (isReachable) {
|
||||
setState(() => _reachabilityStatus = ReachabilityStatus.reachable);
|
||||
} else {
|
||||
setState(() => _reachabilityStatus = ReachabilityStatus.notReachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ReachabilityStatus { reachable, notReachable, testing, undefined }
|
||||
@@ -0,0 +1,96 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_form_builder/flutter_form_builder.dart';
|
||||
import 'package:flutter_paperless_mobile/extensions/flutter_extensions.dart';
|
||||
import 'package:flutter_paperless_mobile/features/login/model/user_credentials.model.dart';
|
||||
import 'package:flutter_paperless_mobile/features/login/view/widgets/password_text_field.dart';
|
||||
import 'package:flutter_paperless_mobile/generated/l10n.dart';
|
||||
import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
|
||||
class UserCredentialsFormField extends StatefulWidget {
|
||||
static const fkCredentials = 'credentials';
|
||||
const UserCredentialsFormField({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UserCredentialsFormField> createState() =>
|
||||
_UserCredentialsFormFieldState();
|
||||
}
|
||||
|
||||
class _UserCredentialsFormFieldState extends State<UserCredentialsFormField> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FormBuilderField<UserCredentials?>(
|
||||
name: UserCredentialsFormField.fkCredentials,
|
||||
builder: (field) => AutofillGroup(
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
textCapitalization: TextCapitalization.words,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
// USERNAME
|
||||
autocorrect: false,
|
||||
onChanged: (username) => field.didChange(
|
||||
field.value?.copyWith(username: username) ??
|
||||
UserCredentials(username: username),
|
||||
),
|
||||
validator: FormBuilderValidators.required(
|
||||
errorText: S.of(context).loginPageUsernameValidatorMessageText,
|
||||
),
|
||||
autofillHints: const [AutofillHints.username],
|
||||
decoration: InputDecoration(
|
||||
label: Text(S.of(context).loginPageUsernameLabel),
|
||||
),
|
||||
),
|
||||
ObscuredInputTextFormField(
|
||||
label: S.of(context).loginPagePasswordFieldLabel,
|
||||
onChanged: (password) => field.didChange(
|
||||
field.value?.copyWith(password: password) ??
|
||||
UserCredentials(password: password),
|
||||
),
|
||||
validator: FormBuilderValidators.required(
|
||||
errorText: S.of(context).loginPagePasswordValidatorMessageText,
|
||||
),
|
||||
),
|
||||
].map((child) => child.padded()).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AutofillGroup(
|
||||
child: Column(
|
||||
children: [
|
||||
FormBuilderTextField(
|
||||
name: fkUsername,
|
||||
focusNode: _focusNodes[fkUsername],
|
||||
onSubmitted: (_) {
|
||||
FocusScope.of(context).requestFocus(_focusNodes[fkPassword]);
|
||||
},
|
||||
validator: FormBuilderValidators.required(
|
||||
errorText: S.of(context).loginPageUsernameValidatorMessageText,
|
||||
),
|
||||
autofillHints: const [AutofillHints.username],
|
||||
decoration: InputDecoration(
|
||||
labelText: S.of(context).loginPageUsernameLabel,
|
||||
),
|
||||
).padded(),
|
||||
FormBuilderTextField(
|
||||
name: fkPassword,
|
||||
focusNode: _focusNodes[fkPassword],
|
||||
onSubmitted: (_) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
autofillHints: const [AutofillHints.password],
|
||||
validator: FormBuilderValidators.required(
|
||||
errorText: S.of(context).loginPagePasswordValidatorMessageText,
|
||||
),
|
||||
obscureText: true,
|
||||
decoration: InputDecoration(
|
||||
labelText: S.of(context).loginPagePasswordFieldLabel,
|
||||
),
|
||||
).padded(),
|
||||
],
|
||||
),
|
||||
);
|
||||
*/
|
||||
Reference in New Issue
Block a user