init
This commit is contained in:
commit
d205277cdd
182 changed files with 22978 additions and 0 deletions
144
lib/screens/connect_sheet.dart
Normal file
144
lib/screens/connect_sheet.dart
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../state/providers.dart';
|
||||
import '../subsonic/credentials.dart';
|
||||
import '../theme/tokens.dart';
|
||||
|
||||
/// Open the connect-server sheet.
|
||||
Future<void> showConnectSheet(BuildContext context) {
|
||||
return showModalBottomSheet<void>(
|
||||
context: context,
|
||||
backgroundColor: RatuneColors.background,
|
||||
isScrollControlled: true,
|
||||
builder: (_) => const _ConnectSheet(),
|
||||
);
|
||||
}
|
||||
|
||||
class _ConnectSheet extends ConsumerStatefulWidget {
|
||||
const _ConnectSheet();
|
||||
|
||||
@override
|
||||
ConsumerState<_ConnectSheet> createState() => _ConnectSheetState();
|
||||
}
|
||||
|
||||
class _ConnectSheetState extends ConsumerState<_ConnectSheet> {
|
||||
final _url = TextEditingController();
|
||||
final _user = TextEditingController();
|
||||
final _pass = TextEditingController();
|
||||
bool _busy = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final creds = ref.read(connectionProvider).credentials;
|
||||
if (creds != null) {
|
||||
_url.text = creds.url;
|
||||
_user.text = creds.username;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_url.dispose();
|
||||
_user.dispose();
|
||||
_pass.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _connect() async {
|
||||
setState(() => _busy = true);
|
||||
final ok = await ref.read(connectionProvider.notifier).connect(
|
||||
SubsonicCredentials(
|
||||
url: _url.text.trim(),
|
||||
username: _user.text.trim(),
|
||||
password: _pass.text,
|
||||
),
|
||||
);
|
||||
if (!mounted) return;
|
||||
setState(() => _busy = false);
|
||||
if (ok) Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final conn = ref.watch(connectionProvider);
|
||||
final accent = Theme.of(context).colorScheme.primary;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: RatuneSpacing.xl,
|
||||
right: RatuneSpacing.xl,
|
||||
top: RatuneSpacing.xl,
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom + RatuneSpacing.xl,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text('Connect to server',
|
||||
style: TextStyle(color: accent, fontWeight: FontWeight.w700)),
|
||||
const SizedBox(height: RatuneSpacing.lg),
|
||||
_field(_url, 'Server URL', 'https://navidrome.example.com',
|
||||
keyboard: TextInputType.url),
|
||||
_field(_user, 'Username', 'you'),
|
||||
_field(_pass, 'Password', '••••••••', obscure: true),
|
||||
if (conn.status == ConnStatus.error && conn.error != null) ...[
|
||||
const SizedBox(height: RatuneSpacing.md),
|
||||
Text(conn.error!,
|
||||
style: const TextStyle(color: Color(0xFFE06C75))),
|
||||
],
|
||||
const SizedBox(height: RatuneSpacing.lg),
|
||||
FilledButton(
|
||||
onPressed: _busy ? null : _connect,
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: accent,
|
||||
foregroundColor: RatuneColors.background,
|
||||
shape: const RoundedRectangleBorder(),
|
||||
),
|
||||
child: _busy
|
||||
? const SizedBox(
|
||||
height: 16,
|
||||
width: 16,
|
||||
child: CircularProgressIndicator(strokeWidth: 2),
|
||||
)
|
||||
: const Text('Connect'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _field(
|
||||
TextEditingController c,
|
||||
String label,
|
||||
String hint, {
|
||||
bool obscure = false,
|
||||
TextInputType? keyboard,
|
||||
}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: RatuneSpacing.md),
|
||||
child: TextField(
|
||||
controller: c,
|
||||
obscureText: obscure,
|
||||
keyboardType: keyboard,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
style: const TextStyle(color: RatuneColors.foreground),
|
||||
decoration: InputDecoration(
|
||||
labelText: label,
|
||||
hintText: hint,
|
||||
labelStyle: const TextStyle(color: RatuneColors.dimmed),
|
||||
hintStyle: const TextStyle(color: RatuneColors.dimmed),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderRadius: BorderRadius.zero,
|
||||
borderSide: BorderSide(color: RatuneColors.border),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.zero,
|
||||
borderSide: BorderSide(color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue