mirror of
https://github.com/immich-app/immich.git
synced 2025-11-21 15:30:41 +09:00
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Docker / pre-job (push) Waiting to run
Docker / Re-Tag ML () (push) Blocked by required conditions
Docker / Re-Tag ML (-armnn) (push) Blocked by required conditions
Docker / Re-Tag ML (-cuda) (push) Blocked by required conditions
Docker / Re-Tag ML (-openvino) (push) Blocked by required conditions
Docker / Re-Tag ML (-rknn) (push) Blocked by required conditions
Docker / Re-Tag ML (-rocm) (push) Blocked by required conditions
Docker / Re-Tag Server () (push) Blocked by required conditions
Docker / Build and Push ML (armnn, linux/arm64, -armnn) (push) Blocked by required conditions
Docker / Build and Push ML (cpu, ) (push) Blocked by required conditions
Docker / Build and Push ML (cuda, linux/amd64, -cuda) (push) Blocked by required conditions
Docker / Build and Push ML (openvino, linux/amd64, -openvino) (push) Blocked by required conditions
Docker / Build and Push ML (rknn, linux/arm64, -rknn) (push) Blocked by required conditions
Docker / Build and Push ML (rocm, linux/amd64, {"linux/amd64": "mich"}, -rocm) (push) Blocked by required conditions
Docker / Build and Push Server (push) Blocked by required conditions
Docker / Docker Build & Push Server Success (push) Blocked by required conditions
Docker / Docker Build & Push ML Success (push) Blocked by required conditions
Docs build / pre-job (push) Waiting to run
Docs build / Docs Build (push) Blocked by required conditions
Static Code Analysis / pre-job (push) Waiting to run
Static Code Analysis / Run Dart Code Analysis (push) Blocked by required conditions
Static Code Analysis / zizmor (push) Waiting to run
Test / pre-job (push) Waiting to run
Test / Test & Lint Server (push) Blocked by required conditions
Test / Unit Test CLI (push) Blocked by required conditions
Test / Unit Test CLI (Windows) (push) Blocked by required conditions
Test / Lint Web (push) Blocked by required conditions
Test / Test Web (push) Blocked by required conditions
Test / Test i18n (push) Blocked by required conditions
Test / End-to-End Lint (push) Blocked by required conditions
Test / Medium Tests (Server) (push) Blocked by required conditions
Test / End-to-End Tests (Server & CLI) (ubuntu-24.04-arm) (push) Blocked by required conditions
Test / End-to-End Tests (Server & CLI) (ubuntu-latest) (push) Blocked by required conditions
Test / End-to-End Tests (Web) (ubuntu-24.04-arm) (push) Blocked by required conditions
Test / End-to-End Tests (Web) (ubuntu-latest) (push) Blocked by required conditions
Test / End-to-End Tests Success (push) Blocked by required conditions
Test / Unit Test Mobile (push) Blocked by required conditions
Test / Unit Test ML (push) Blocked by required conditions
Test / .github Files Formatting (push) Blocked by required conditions
Test / ShellCheck (push) Waiting to run
Test / OpenAPI Clients (push) Waiting to run
Test / SQL Schema Checks (push) Waiting to run
154 lines
4.7 KiB
Dart
154 lines
4.7 KiB
Dart
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';
|
|
|
|
class EndpointInput extends StatefulHookConsumerWidget {
|
|
const EndpointInput({
|
|
super.key,
|
|
required this.initialValue,
|
|
required this.index,
|
|
required this.onValidated,
|
|
required this.onDismissed,
|
|
this.enabled = true,
|
|
});
|
|
|
|
final AuxilaryEndpoint initialValue;
|
|
final int index;
|
|
final Function(String url, int index, AuxCheckStatus status) onValidated;
|
|
final Function(int index) onDismissed;
|
|
final bool enabled;
|
|
|
|
@override
|
|
EndpointInputState createState() => EndpointInputState();
|
|
}
|
|
|
|
class EndpointInputState extends ConsumerState<EndpointInput> {
|
|
late final TextEditingController controller;
|
|
late final FocusNode focusNode;
|
|
late AuxCheckStatus auxCheckStatus;
|
|
bool isInputValid = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
controller = TextEditingController(text: widget.initialValue.url);
|
|
focusNode = FocusNode()..addListener(_onOutFocus);
|
|
|
|
setState(() {
|
|
auxCheckStatus = widget.initialValue.status;
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
focusNode.removeListener(_onOutFocus);
|
|
focusNode.dispose();
|
|
controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _onOutFocus() {
|
|
if (!focusNode.hasFocus && isInputValid) {
|
|
validateAuxilaryServerUrl();
|
|
}
|
|
}
|
|
|
|
Future<void> validateAuxilaryServerUrl() async {
|
|
final url = controller.text;
|
|
setState(() => auxCheckStatus = AuxCheckStatus.loading);
|
|
|
|
final isValid = await ref.read(authProvider.notifier).validateAuxilaryServerUrl(url);
|
|
|
|
setState(() {
|
|
if (mounted) {
|
|
auxCheckStatus = isValid ? AuxCheckStatus.valid : AuxCheckStatus.error;
|
|
}
|
|
});
|
|
|
|
widget.onValidated(url, widget.index, auxCheckStatus);
|
|
}
|
|
|
|
String? validateUrl(String? url) {
|
|
try {
|
|
if (url == null || url.isEmpty || !Uri.parse(url).isAbsolute) {
|
|
isInputValid = false;
|
|
return 'validate_endpoint_error'.tr();
|
|
}
|
|
} catch (_) {
|
|
isInputValid = false;
|
|
return 'validate_endpoint_error'.tr();
|
|
}
|
|
|
|
isInputValid = true;
|
|
return null;
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Dismissible(
|
|
key: ValueKey(widget.index.toString()),
|
|
direction: DismissDirection.endToStart,
|
|
onDismissed: (_) => widget.onDismissed(widget.index),
|
|
background: Container(
|
|
color: Colors.red,
|
|
alignment: Alignment.centerRight,
|
|
padding: const EdgeInsets.only(right: 16),
|
|
child: const Icon(
|
|
Icons.delete,
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
child: ListTile(
|
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
|
trailing: ReorderableDragStartListener(
|
|
enabled: widget.enabled,
|
|
index: widget.index,
|
|
child: const Icon(Icons.drag_handle_rounded),
|
|
),
|
|
leading: NetworkStatusIcon(
|
|
key: ValueKey('status_$auxCheckStatus'),
|
|
status: auxCheckStatus,
|
|
enabled: widget.enabled,
|
|
),
|
|
subtitle: TextFormField(
|
|
enabled: widget.enabled,
|
|
onTapOutside: (_) => focusNode.unfocus(),
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
validator: validateUrl,
|
|
keyboardType: TextInputType.url,
|
|
style: const TextStyle(
|
|
fontFamily: 'Inconsolata',
|
|
fontWeight: FontWeight.w600,
|
|
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)),
|
|
),
|
|
),
|
|
controller: controller,
|
|
focusNode: focusNode,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|