Files
voidea/flutter/lib/features/integrations/disk_integration_screen.dart

201 lines
6.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../providers/disk_provider.dart';
import '../../core/theme/app_theme.dart';
class DiskIntegrationScreen extends ConsumerStatefulWidget {
const DiskIntegrationScreen({super.key});
@override
ConsumerState<DiskIntegrationScreen> createState() =>
_DiskIntegrationScreenState();
}
class _DiskIntegrationScreenState extends ConsumerState<DiskIntegrationScreen> {
@override
void initState() {
super.initState();
Future.microtask(() => ref.read(diskProvider.notifier).load());
}
@override
Widget build(BuildContext context) {
final state = ref.watch(diskProvider);
final notifier = ref.read(diskProvider.notifier);
return Scaffold(
appBar: AppBar(title: const Text('Облачные диски')),
body: state.isLoading
? const Center(child: CircularProgressIndicator())
: ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Icon(Icons.cloud, size: 48,
color: VoIdeaColors.primary),
const SizedBox(height: 12),
const Text(
'Подключите облачный диск для сохранения презентаций и экспорта',
textAlign: TextAlign.center,
),
],
),
),
),
const SizedBox(height: 16),
...state.availableProviders.map((provider) {
final connected = state.isConnected(provider);
final info = connected
? state.connectedProviders.firstWhere(
(p) => p['provider'] == provider,
orElse: () => {},
)
: null;
final icon = _providerIcon(provider);
final label = _providerLabel(provider);
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: CircleAvatar(
backgroundColor: connected
? VoIdeaColors.success.withOpacity(0.1)
: VoIdeaColors.muted.withOpacity(0.1),
child: Icon(icon,
color: connected
? VoIdeaColors.success
: VoIdeaColors.muted),
),
title: Text(label),
subtitle: connected
? Text(
info?['email'] ?? 'Подключён',
style: const TextStyle(fontSize: 12),
)
: const Text('Не подключён',
style: TextStyle(fontSize: 12)),
trailing: connected
? TextButton(
onPressed: () =>
_confirmDisconnect(context, notifier, provider),
child: const Text('Отключить',
style: TextStyle(color: VoIdeaColors.error)),
)
: ElevatedButton(
onPressed: () => _connect(context, notifier, provider),
child: const Text('Подключить'),
),
),
);
}),
],
),
);
}
IconData _providerIcon(String provider) {
switch (provider) {
case 'google':
return Icons.g_mobiledata;
case 'yandex':
return Icons.cloud;
case 'apple':
return Icons.apple;
default:
return Icons.drive_file_rename_outline;
}
}
String _providerLabel(String provider) {
switch (provider) {
case 'google':
return 'Google Диск';
case 'yandex':
return 'Яндекс Диск';
case 'apple':
return 'iCloud';
default:
return provider;
}
}
Future<void> _connect(
BuildContext context, DiskNotifier notifier, String provider) async {
final url = await notifier.getOAuthUrl(provider);
if (url != null && await canLaunchUrl(Uri.parse(url))) {
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
// After OAuth callback, app returns with code in URL
// For simplicity, show a dialog to paste the code
if (mounted) _showCodeDialog(context, notifier, provider);
}
}
void _showCodeDialog(
BuildContext context, DiskNotifier notifier, String provider) {
final controller = TextEditingController();
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('Подключить ${_providerLabel(provider)}'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('После авторизации вставьте код из URL-параметра'),
const SizedBox(height: 12),
TextField(
controller: controller,
decoration: const InputDecoration(
labelText: 'Код авторизации',
hintText: 'Вставьте code из URL',
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Отмена'),
),
ElevatedButton(
onPressed: () async {
if (controller.text.isNotEmpty) {
await notifier.connect(provider, controller.text.trim());
if (ctx.mounted) Navigator.pop(ctx);
}
},
child: const Text('Подключить'),
),
],
),
);
}
void _confirmDisconnect(
BuildContext context, DiskNotifier notifier, String provider) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('Отключить ${_providerLabel(provider)}?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('Отмена'),
),
ElevatedButton(
onPressed: () {
notifier.disconnect(provider);
Navigator.pop(ctx);
},
child: const Text('Отключить'),
),
],
),
);
}
}