diff --git a/frontend/lib/navigation.dart b/frontend/lib/navigation.dart index 5272f19..311d0c8 100644 --- a/frontend/lib/navigation.dart +++ b/frontend/lib/navigation.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:frontend/settings/settings.dart'; import 'package:go_router/go_router.dart'; import 'notifications/notification_list.dart'; +import 'utils/pref_utils.dart'; // Routes definitions final GoRouter router = GoRouter( + // If there is no API base URL send the user to settings initialLocation: '/notifications', routes: [ ShellRoute( @@ -15,16 +18,18 @@ final GoRouter router = GoRouter( path: '/notifications', name: 'notifications', builder: (context, state) => NotificationList(), + redirect: (context, state) => _redirectToSettings(), ), GoRoute( path: '/devices', name: 'devices', builder: (context, state) => const Placeholder(), + redirect: (context, state) => _redirectToSettings(), ), GoRoute( path: '/settings', name: 'settings', - builder: (context, state) => const Placeholder(), + builder: (context, state) => Settings(), ), GoRoute( path: '/about', @@ -103,6 +108,12 @@ class MainShell extends StatelessWidget { } } +String? _redirectToSettings() { + return (PrefUtil.getValue("base_url", "") as String == "") + ? '/settings' + : null; +} + int _calculateSelectedIndex(BuildContext context) { final location = GoRouterState.of(context).uri.path; if (location.startsWith('/notifications')) return 0; diff --git a/frontend/lib/settings/settings.dart b/frontend/lib/settings/settings.dart new file mode 100644 index 0000000..2172bd6 --- /dev/null +++ b/frontend/lib/settings/settings.dart @@ -0,0 +1,104 @@ +import 'package:encrypter/encrypter/xor.dart'; +import 'package:flutter/material.dart'; +import '../utils/oott_api.dart'; +import '../utils/pref_utils.dart'; + +class Settings extends StatefulWidget { + @override + _SettingsState createState() => _SettingsState(); +} + +class _SettingsState extends State { + bool _isLoading = true; + final _baseUrlController = TextEditingController(); + final _apiKeyController = TextEditingController(); + bool _apiKeyVisible = false; + final _formKey = GlobalKey(); + + @override + void initState() { + super.initState(); + _getData(); + } + + @override + void dispose() { + _baseUrlController.dispose(); + _apiKeyController.dispose(); + super.dispose(); + } + + void _getData() async { + _baseUrlController.text = PrefUtil.getValue("base_url", "") as String; + _apiKeyController.text = XOR().xorDecode( + PrefUtil.getValue("api_key", "") as String, + ); + _isLoading = false; + setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('Settings')), + body: _isLoading + ? const Center(child: CircularProgressIndicator()) + : Form( + key: _formKey, + child: Column( + children: [ + SizedBox(height: 16), + TextFormField( + controller: _baseUrlController, + decoration: const InputDecoration( + border: UnderlineInputBorder(), + labelText: 'Base URL of your OOTT server\'s API', + hintText: "For example http://192.168.0.1:3000/api", + ), + ), + SizedBox(height: 16), + TextFormField( + controller: _apiKeyController, + obscureText: !_apiKeyVisible, + decoration: InputDecoration( + border: UnderlineInputBorder(), + labelText: 'API Key', + suffixIcon: IconButton( + icon: Icon( + _apiKeyVisible + ? Icons.visibility + : Icons.visibility_off, + ), + onPressed: () { + setState(() { + _apiKeyVisible = !_apiKeyVisible; + }); + }, + ), + ), + ), + SizedBox(height: 16), + ElevatedButton.icon( + onPressed: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + content: Text( + _baseUrlController.text + + ' - ' + + _apiKeyController.text, + ), + ); + }, + ); + }, + label: Text('Test'), + icon: const Icon(Icons.check), + ), + ], + ), + ), + ); + } +} diff --git a/frontend/lib/utils/pref_utils.dart b/frontend/lib/utils/pref_utils.dart index 011faf7..97b6d24 100644 --- a/frontend/lib/utils/pref_utils.dart +++ b/frontend/lib/utils/pref_utils.dart @@ -28,11 +28,11 @@ class PrefUtil { static Object getValue(String key, Object defaultValue) { switch (defaultValue.runtimeType) { case String: - return preferences.getString(key) ?? ""; + return preferences.getString(key) ?? defaultValue; case bool: - return preferences.getBool(key) ?? false; + return preferences.getBool(key) ?? defaultValue; case int: - return preferences.getInt(key) ?? 0; + return preferences.getInt(key) ?? defaultValue; default: return defaultValue; }