feat(ui): add ImmichURLInput (#27105)
feat(ui): implement shared URL input configuration and update input fieldspull/28479/head
parent
7993619ed2
commit
3a3469a5f9
|
|
@ -400,15 +400,12 @@ class LoginForm extends HookConsumerWidget {
|
|||
submitText: 'next'.t(context: context),
|
||||
submitIcon: Icons.arrow_forward_rounded,
|
||||
onSubmit: getServerAuthSettings,
|
||||
child: ImmichTextInput(
|
||||
child: ImmichURLInput(
|
||||
controller: serverEndpointController,
|
||||
label: 'login_form_endpoint_url'.t(context: context),
|
||||
hintText: 'login_form_endpoint_hint'.t(context: context),
|
||||
validator: _validateUrl,
|
||||
keyboardAction: TextInputAction.next,
|
||||
keyboardType: TextInputType.url,
|
||||
autofillHints: const [AutofillHints.url],
|
||||
autoCorrect: false,
|
||||
keyboardAction: .next,
|
||||
onSubmit: (ctx, _) => ImmichForm.of(ctx).submit(),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||
import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart';
|
||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||
import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart';
|
||||
import 'package:immich_ui/immich_ui.dart';
|
||||
|
||||
class EndpointInput extends StatefulHookConsumerWidget {
|
||||
const EndpointInput({
|
||||
|
|
@ -111,28 +111,12 @@ class EndpointInputState extends ConsumerState<EndpointInput> {
|
|||
status: auxCheckStatus,
|
||||
enabled: widget.enabled,
|
||||
),
|
||||
subtitle: TextFormField(
|
||||
subtitle: ImmichURLInput(
|
||||
enabled: widget.enabled,
|
||||
onTapOutside: (_) => focusNode.unfocus(),
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
autovalidateMode: .onUserInteraction,
|
||||
validator: validateUrl,
|
||||
keyboardType: TextInputType.url,
|
||||
style: const TextStyle(fontFamily: 'GoogleSansCode', fontSize: 14),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'http(s)://immich.domain.com',
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
filled: true,
|
||||
fillColor: context.colorScheme.surfaceContainer,
|
||||
border: const OutlineInputBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.red[300]!),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
disabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: context.isDarkTheme ? Colors.grey[900]! : Colors.grey[300]!),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
),
|
||||
keyboardAction: .next,
|
||||
hintText: 'http(s)://immich.domain.com',
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,24 +8,29 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||
import 'package:immich_mobile/providers/network.provider.dart';
|
||||
import 'package:immich_ui/immich_ui.dart';
|
||||
|
||||
class LocalNetworkPreference extends HookConsumerWidget {
|
||||
const LocalNetworkPreference({super.key, required this.enabled});
|
||||
|
||||
final bool enabled;
|
||||
|
||||
Future<String?> _showEditDialog(BuildContext context, String title, String hintText, String initialValue) {
|
||||
Future<String?> _showEditDialog(
|
||||
BuildContext context,
|
||||
String title,
|
||||
String hintText,
|
||||
String initialValue, {
|
||||
bool isUrlField = false,
|
||||
}) {
|
||||
final controller = TextEditingController(text: initialValue);
|
||||
|
||||
return showDialog<String>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: Text(title),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(border: const OutlineInputBorder(), hintText: hintText),
|
||||
),
|
||||
content: isUrlField
|
||||
? ImmichURLInput(controller: controller, autofocus: true, keyboardAction: .done, hintText: hintText)
|
||||
: ImmichTextInput(controller: controller, autofocus: true, keyboardAction: .done, hintText: hintText),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
|
|
@ -81,6 +86,7 @@ class LocalNetworkPreference extends HookConsumerWidget {
|
|||
"server_endpoint".tr(),
|
||||
"http://local-ip:2283",
|
||||
localEndpointText.value,
|
||||
isUrlField: true,
|
||||
);
|
||||
|
||||
if (localEndpoint != null) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export 'src/components/icon_button.dart';
|
|||
export 'src/components/password_input.dart';
|
||||
export 'src/components/text_button.dart';
|
||||
export 'src/components/text_input.dart';
|
||||
export 'src/components/url_input.dart';
|
||||
export 'src/constants.dart';
|
||||
export 'src/theme.dart';
|
||||
export 'src/translation.dart';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class ImmichTextInput extends StatefulWidget {
|
||||
final String label;
|
||||
final String? label;
|
||||
final String? hintText;
|
||||
final TextEditingController? controller;
|
||||
final FocusNode? focusNode;
|
||||
|
|
@ -12,13 +13,19 @@ class ImmichTextInput extends StatefulWidget {
|
|||
final List<String>? autofillHints;
|
||||
final Widget? suffixIcon;
|
||||
final bool obscureText;
|
||||
final bool autoCorrect;
|
||||
final bool autocorrect;
|
||||
final SmartDashesType? smartDashesType;
|
||||
final SmartQuotesType? smartQuotesType;
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
final bool enabled;
|
||||
final bool autofocus;
|
||||
final AutovalidateMode? autovalidateMode;
|
||||
|
||||
const ImmichTextInput({
|
||||
super.key,
|
||||
this.controller,
|
||||
this.focusNode,
|
||||
required this.label,
|
||||
this.label,
|
||||
this.hintText,
|
||||
this.validator,
|
||||
this.onSubmit,
|
||||
|
|
@ -27,7 +34,13 @@ class ImmichTextInput extends StatefulWidget {
|
|||
this.autofillHints,
|
||||
this.suffixIcon,
|
||||
this.obscureText = false,
|
||||
this.autoCorrect = true,
|
||||
this.autocorrect = true,
|
||||
this.smartDashesType,
|
||||
this.smartQuotesType,
|
||||
this.inputFormatters,
|
||||
this.enabled = true,
|
||||
this.autofocus = false,
|
||||
this.autovalidateMode,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
@ -53,9 +66,14 @@ class _ImmichTextInputState extends State<ImmichTextInput> {
|
|||
}
|
||||
|
||||
String? _validateInput(String? value) {
|
||||
setState(() {
|
||||
_error = widget.validator?.call(value);
|
||||
});
|
||||
final error = widget.validator?.call(value);
|
||||
if (error != _error) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (mounted) {
|
||||
setState(() => _error = error);
|
||||
}
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -68,6 +86,9 @@ class _ImmichTextInputState extends State<ImmichTextInput> {
|
|||
return TextFormField(
|
||||
controller: widget.controller,
|
||||
focusNode: _focusNode,
|
||||
enabled: widget.enabled,
|
||||
autofocus: widget.autofocus,
|
||||
autovalidateMode: widget.autovalidateMode,
|
||||
decoration: InputDecoration(
|
||||
hintText: widget.hintText,
|
||||
labelText: widget.label,
|
||||
|
|
@ -79,13 +100,16 @@ class _ImmichTextInputState extends State<ImmichTextInput> {
|
|||
),
|
||||
obscureText: widget.obscureText,
|
||||
validator: _validateInput,
|
||||
keyboardType: widget.keyboardType,
|
||||
textInputAction: widget.keyboardAction,
|
||||
autocorrect: widget.autoCorrect,
|
||||
autofillHints: widget.autofillHints,
|
||||
onTap: () => setState(() => _error = null),
|
||||
onTapOutside: (_) => _focusNode.unfocus(),
|
||||
onFieldSubmitted: (value) => widget.onSubmit?.call(context, value),
|
||||
keyboardType: widget.keyboardType,
|
||||
autofillHints: widget.autofillHints,
|
||||
autocorrect: widget.autocorrect,
|
||||
smartDashesType: widget.smartDashesType,
|
||||
smartQuotesType: widget.smartQuotesType,
|
||||
inputFormatters: widget.inputFormatters,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:immich_ui/src/components/text_input.dart';
|
||||
|
||||
class ImmichURLInput extends ImmichTextInput {
|
||||
ImmichURLInput({
|
||||
super.key,
|
||||
super.controller,
|
||||
super.focusNode,
|
||||
super.label,
|
||||
super.hintText,
|
||||
super.validator,
|
||||
super.onSubmit,
|
||||
super.keyboardAction,
|
||||
super.suffixIcon,
|
||||
super.enabled,
|
||||
super.autofocus,
|
||||
super.autovalidateMode,
|
||||
}) : super(
|
||||
keyboardType: .url,
|
||||
autofillHints: const [AutofillHints.url],
|
||||
autocorrect: false,
|
||||
smartDashesType: .disabled,
|
||||
smartQuotesType: .disabled,
|
||||
inputFormatters: _formatters,
|
||||
);
|
||||
|
||||
static final List<TextInputFormatter> _formatters = List.unmodifiable([
|
||||
FilteringTextInputFormatter.deny(RegExp(r'\s')),
|
||||
]);
|
||||
}
|
||||
Loading…
Reference in New Issue