added cargo files
This commit is contained in:
311
PinePods-0.8.2/mobile/lib/ui/settings/pinepods_login.dart
Normal file
311
PinePods-0.8.2/mobile/lib/ui/settings/pinepods_login.dart
Normal file
@@ -0,0 +1,311 @@
|
||||
// lib/ui/settings/pinepods_login.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinepods_mobile/bloc/settings/settings_bloc.dart';
|
||||
import 'package:pinepods_mobile/l10n/L.dart';
|
||||
import 'package:pinepods_mobile/services/pinepods/pinepods_service.dart';
|
||||
import 'package:pinepods_mobile/services/pinepods/login_service.dart';
|
||||
import 'package:pinepods_mobile/ui/widgets/restart_widget.dart';
|
||||
import 'package:pinepods_mobile/ui/settings/settings_section_label.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
class PinepodsLoginWidget extends StatefulWidget {
|
||||
const PinepodsLoginWidget({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<PinepodsLoginWidget> createState() => _PinepodsLoginWidgetState();
|
||||
}
|
||||
|
||||
class _PinepodsLoginWidgetState extends State<PinepodsLoginWidget> {
|
||||
final _serverController = TextEditingController();
|
||||
final _usernameController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
final _mfaController = TextEditingController();
|
||||
|
||||
bool _isLoading = false;
|
||||
bool _showMfaField = false;
|
||||
String _errorMessage = '';
|
||||
bool _isLoggedIn = false;
|
||||
String? _connectedServer;
|
||||
String? _tempServerUrl;
|
||||
String? _tempUsername;
|
||||
int? _tempUserId;
|
||||
String? _tempMfaSessionToken;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Initialize UI based on saved settings
|
||||
_loadSavedSettings();
|
||||
}
|
||||
|
||||
void _loadSavedSettings() {
|
||||
var settingsBloc = Provider.of<SettingsBloc>(context, listen: false);
|
||||
var settings = settingsBloc.currentSettings;
|
||||
|
||||
// Check if we have PinePods settings
|
||||
setState(() {
|
||||
_isLoggedIn = false;
|
||||
_connectedServer = null;
|
||||
|
||||
// We'll add these properties to AppSettings in the next step
|
||||
if (settings.pinepodsServer != null &&
|
||||
settings.pinepodsServer!.isNotEmpty &&
|
||||
settings.pinepodsApiKey != null &&
|
||||
settings.pinepodsApiKey!.isNotEmpty) {
|
||||
_isLoggedIn = true;
|
||||
_connectedServer = settings.pinepodsServer;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _connectToPinepods() async {
|
||||
if (!_showMfaField && (_serverController.text.isEmpty ||
|
||||
_usernameController.text.isEmpty ||
|
||||
_passwordController.text.isEmpty)) {
|
||||
setState(() {
|
||||
_errorMessage = 'Please fill in all fields';
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (_showMfaField && _mfaController.text.isEmpty) {
|
||||
setState(() {
|
||||
_errorMessage = 'Please enter your MFA code';
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_errorMessage = '';
|
||||
});
|
||||
|
||||
try {
|
||||
if (_showMfaField && _tempMfaSessionToken != null) {
|
||||
// Complete MFA login flow
|
||||
final mfaCode = _mfaController.text.trim();
|
||||
final result = await PinepodsLoginService.completeMfaLogin(
|
||||
serverUrl: _tempServerUrl!,
|
||||
username: _tempUsername!,
|
||||
mfaSessionToken: _tempMfaSessionToken!,
|
||||
mfaCode: mfaCode,
|
||||
);
|
||||
|
||||
if (result.isSuccess) {
|
||||
// Save the connection details including user ID
|
||||
var settingsBloc = Provider.of<SettingsBloc>(context, listen: false);
|
||||
settingsBloc.setPinepodsServer(result.serverUrl!);
|
||||
settingsBloc.setPinepodsApiKey(result.apiKey!);
|
||||
settingsBloc.setPinepodsUserId(result.userId!);
|
||||
|
||||
setState(() {
|
||||
_isLoggedIn = true;
|
||||
_connectedServer = _tempServerUrl;
|
||||
_showMfaField = false;
|
||||
_tempServerUrl = null;
|
||||
_tempUsername = null;
|
||||
_tempUserId = null;
|
||||
_tempMfaSessionToken = null;
|
||||
_isLoading = false;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_errorMessage = result.errorMessage ?? 'MFA verification failed';
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Initial login flow
|
||||
final serverUrl = _serverController.text.trim();
|
||||
final username = _usernameController.text.trim();
|
||||
final password = _passwordController.text;
|
||||
|
||||
final result = await PinepodsLoginService.login(
|
||||
serverUrl,
|
||||
username,
|
||||
password,
|
||||
);
|
||||
|
||||
if (result.isSuccess) {
|
||||
// Save the connection details including user ID
|
||||
var settingsBloc = Provider.of<SettingsBloc>(context, listen: false);
|
||||
settingsBloc.setPinepodsServer(result.serverUrl!);
|
||||
settingsBloc.setPinepodsApiKey(result.apiKey!);
|
||||
settingsBloc.setPinepodsUserId(result.userId!);
|
||||
|
||||
setState(() {
|
||||
_isLoggedIn = true;
|
||||
_connectedServer = serverUrl;
|
||||
_isLoading = false;
|
||||
});
|
||||
} else if (result.requiresMfa) {
|
||||
// Store MFA session info and show MFA field
|
||||
setState(() {
|
||||
_tempServerUrl = result.serverUrl;
|
||||
_tempUsername = result.username;
|
||||
_tempUserId = result.userId;
|
||||
_tempMfaSessionToken = result.mfaSessionToken;
|
||||
_showMfaField = true;
|
||||
_isLoading = false;
|
||||
_errorMessage = 'Please enter your MFA code';
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_errorMessage = result.errorMessage ?? 'Login failed';
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_errorMessage = 'Error: ${e.toString()}';
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _resetMfa() {
|
||||
setState(() {
|
||||
_showMfaField = false;
|
||||
_tempServerUrl = null;
|
||||
_tempUsername = null;
|
||||
_tempUserId = null;
|
||||
_tempMfaSessionToken = null;
|
||||
_mfaController.clear();
|
||||
_errorMessage = '';
|
||||
});
|
||||
}
|
||||
|
||||
void _logOut() async {
|
||||
var settingsBloc = Provider.of<SettingsBloc>(context, listen: false);
|
||||
|
||||
// Clear all PinePods user data
|
||||
settingsBloc.setPinepodsServer(null);
|
||||
settingsBloc.setPinepodsApiKey(null);
|
||||
settingsBloc.setPinepodsUserId(null);
|
||||
settingsBloc.setPinepodsUsername(null);
|
||||
settingsBloc.setPinepodsEmail(null);
|
||||
|
||||
setState(() {
|
||||
_isLoggedIn = false;
|
||||
_connectedServer = null;
|
||||
});
|
||||
|
||||
// Wait for the settings to be processed and then restart the app
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
if (mounted) {
|
||||
// Restart the entire app to reset all state
|
||||
RestartWidget.restartApp(context);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Add a divider label for the PinePods section
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SettingsDividerLabel(label: 'PinePods Server'),
|
||||
const Divider(),
|
||||
if (_isLoggedIn) ...[
|
||||
// Show connected status
|
||||
ListTile(
|
||||
title: const Text('PinePods Connection'),
|
||||
subtitle: Text(_connectedServer ?? ''),
|
||||
trailing: TextButton(
|
||||
onPressed: _logOut,
|
||||
child: const Text('Log Out'),
|
||||
),
|
||||
),
|
||||
] else ...[
|
||||
// Show login form
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: _serverController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Server URL',
|
||||
hintText: 'https://your-pinepods-server.com',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _usernameController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Username',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _passwordController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Password',
|
||||
),
|
||||
obscureText: true,
|
||||
enabled: !_showMfaField,
|
||||
),
|
||||
// MFA Field (shown when MFA is required)
|
||||
if (_showMfaField) ...[
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _mfaController,
|
||||
decoration: InputDecoration(
|
||||
labelText: 'MFA Code',
|
||||
hintText: 'Enter 6-digit code',
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: _resetMfa,
|
||||
tooltip: 'Cancel MFA',
|
||||
),
|
||||
),
|
||||
keyboardType: TextInputType.number,
|
||||
maxLength: 6,
|
||||
),
|
||||
],
|
||||
if (_errorMessage.isNotEmpty) ...[
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_errorMessage,
|
||||
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: _isLoading ? null : _connectToPinepods,
|
||||
child: _isLoading
|
||||
? const SizedBox(
|
||||
height: 20,
|
||||
width: 20,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
)
|
||||
: Text(_showMfaField ? 'Verify MFA Code' : 'Connect'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_serverController.dispose();
|
||||
_usernameController.dispose();
|
||||
_passwordController.dispose();
|
||||
_mfaController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user