You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
7.3 KiB

import 'package:flutter/material.dart';
import '../../core/service_locator.dart';
import '../../core/logger.dart';
import '../../data/recipes/models/bookmark_category_model.dart';
/// Dialog for selecting or creating a bookmark category.
class BookmarkDialog extends StatefulWidget {
final String recipeId;
final List<BookmarkCategory> currentCategories;
const BookmarkDialog({
super.key,
required this.recipeId,
required this.currentCategories,
});
@override
State<BookmarkDialog> createState() => _BookmarkDialogState();
}
class _BookmarkDialogState extends State<BookmarkDialog> {
final TextEditingController _categoryNameController = TextEditingController();
List<BookmarkCategory> _allCategories = [];
bool _isLoading = false;
bool _isCreating = false;
@override
void initState() {
super.initState();
_loadCategories();
}
@override
void dispose() {
_categoryNameController.dispose();
super.dispose();
}
Future<void> _loadCategories() async {
setState(() => _isLoading = true);
try {
final recipeService = ServiceLocator.instance.recipeService;
if (recipeService != null) {
final categories = await recipeService.getAllBookmarkCategories();
setState(() {
_allCategories = categories;
_isLoading = false;
});
}
} catch (e) {
Logger.error('Failed to load bookmark categories', e);
setState(() => _isLoading = false);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to load categories: $e')),
);
}
}
}
Future<void> _createCategory() async {
final name = _categoryNameController.text.trim();
if (name.isEmpty) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Category name cannot be empty')),
);
}
return;
}
setState(() => _isCreating = true);
try {
final recipeService = ServiceLocator.instance.recipeService;
if (recipeService != null) {
final category = await recipeService.createBookmarkCategory(name: name);
await recipeService.addRecipeToCategory(
recipeId: widget.recipeId,
categoryId: category.id,
);
if (mounted) {
Navigator.of(context).pop(category);
}
}
} catch (e) {
Logger.error('Failed to create bookmark category', e);
setState(() => _isCreating = false);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to create category: $e')),
);
}
}
}
Future<void> _selectCategory(BookmarkCategory category) async {
try {
final recipeService = ServiceLocator.instance.recipeService;
if (recipeService != null) {
final isInCategory = widget.currentCategories
.any((c) => c.id == category.id);
if (isInCategory) {
// Remove from category
await recipeService.removeRecipeFromCategory(
recipeId: widget.recipeId,
categoryId: category.id,
);
} else {
// Add to category
await recipeService.addRecipeToCategory(
recipeId: widget.recipeId,
categoryId: category.id,
);
}
if (mounted) {
Navigator.of(context).pop(category);
}
}
} catch (e) {
Logger.error('Failed to update bookmark', e);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to update bookmark: $e')),
);
}
}
}
Color _parseColor(String hexColor) {
try {
return Color(int.parse(hexColor.replaceAll('#', '0xFF')));
} catch (e) {
return Colors.blue;
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Bookmark Recipe'),
content: SizedBox(
width: double.maxFinite,
child: _isLoading
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Existing categories
if (_allCategories.isNotEmpty) ...[
const Text(
'Add to category:',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 200),
child: ListView.builder(
shrinkWrap: true,
itemCount: _allCategories.length,
itemBuilder: (context, index) {
final category = _allCategories[index];
final isSelected = widget.currentCategories
.any((c) => c.id == category.id);
return CheckboxListTile(
title: Text(category.name),
value: isSelected,
onChanged: (_) => _selectCategory(category),
secondary: category.color != null
? Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: _parseColor(category.color!),
shape: BoxShape.circle,
),
)
: const Icon(Icons.bookmark),
);
},
),
),
const SizedBox(height: 16),
],
// Create new category
const Text(
'Or create new category:',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
TextField(
controller: _categoryNameController,
decoration: const InputDecoration(
hintText: 'Category name',
border: OutlineInputBorder(),
),
enabled: !_isCreating,
onSubmitted: (_) => _createCategory(),
),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
if (!_isLoading)
TextButton(
onPressed: _isCreating ? null : _createCategory,
child: _isCreating
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Create & Add'),
),
],
);
}
}

Powered by TurnKey Linux.