diff --git a/lib/ui/add_recipe/add_recipe_screen.dart b/lib/ui/add_recipe/add_recipe_screen.dart index f2d3b1f..0433e74 100644 --- a/lib/ui/add_recipe/add_recipe_screen.dart +++ b/lib/ui/add_recipe/add_recipe_screen.dart @@ -106,7 +106,7 @@ class _AddRecipeScreenState extends State { Future _showBookmarkDialog() async { if (widget.recipe == null) return; - final result = await showDialog( + await showDialog( context: context, builder: (context) => BookmarkDialog( recipeId: widget.recipe!.id, @@ -123,25 +123,8 @@ class _AddRecipeScreenState extends State { void _navigateToBookmark() { if (widget.recipe == null) return; - // If recipe has bookmarks, navigate to the first one - if (_bookmarkCategories.isNotEmpty) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => BookmarkCategoryRecipesScreen( - category: _bookmarkCategories.first, - ), - ), - ).then((_) { - // Reload bookmark categories when returning - if (mounted && widget.viewMode) { - _loadBookmarkCategories(); - } - }); - } else { - // If no bookmarks, show dialog to add bookmarks - _showBookmarkDialog(); - } + // Always show the bookmark dialog to manage bookmarks + _showBookmarkDialog(); } void _onScroll() { @@ -363,7 +346,7 @@ class _AddRecipeScreenState extends State { Future _showRatingDialog() async { await showGeneralDialog( context: context, - barrierColor: Colors.black.withOpacity(0.2), + barrierColor: Colors.black.withValues(alpha: 0.2), barrierDismissible: true, barrierLabel: 'Select rating', transitionDuration: const Duration(milliseconds: 300), @@ -414,7 +397,7 @@ class _AddRecipeScreenState extends State { borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.2), + color: Colors.black.withValues(alpha: 0.2), blurRadius: 15, spreadRadius: 1, offset: const Offset(0, 2), @@ -1176,14 +1159,6 @@ class _AddRecipeScreenState extends State { } } - void _editRecipe() { - Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => AddRecipeScreen(recipe: widget.recipe), - ), - ); - } - Color _getRatingColor(int rating) { if (rating >= 4) return Colors.green; if (rating >= 2) return Colors.orange; @@ -1714,7 +1689,7 @@ class _AddRecipeScreenState extends State { color: Theme.of(context).colorScheme.surface, boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: Colors.black.withValues(alpha: 0.1), blurRadius: 4, offset: const Offset(0, -2), ), @@ -2033,7 +2008,7 @@ class _AddRecipeScreenState extends State { vertical: 8, ), decoration: BoxDecoration( - color: _getRatingColor(_rating).withOpacity(0.1), + color: _getRatingColor(_rating).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(24), ), child: Row( @@ -2333,45 +2308,6 @@ class _AddRecipeScreenState extends State { ); } - Widget _buildVideoThumbnailForTile(String videoUrl) { - return Stack( - fit: StackFit.expand, - children: [ - Container( - color: Colors.black87, - child: const Center( - child: Icon( - Icons.play_circle_filled, - size: 60, - color: Colors.white70, - ), - ), - ), - Positioned( - bottom: 8, - left: 8, - right: 8, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 3), - decoration: BoxDecoration( - color: Colors.black.withValues(alpha: 0.7), - borderRadius: BorderRadius.circular(4), - ), - child: const Text( - 'MP4', - style: TextStyle( - color: Colors.white, - fontSize: 12, - fontWeight: FontWeight.bold, - ), - textAlign: TextAlign.center, - ), - ), - ), - ], - ); - } - /// Builds an image preview specifically for tiled layouts (ensures proper fit). Widget _buildImagePreviewForTile(String imageUrl) { final mediaService = ServiceLocator.instance.mediaService; @@ -2466,39 +2402,6 @@ class _AddRecipeScreenState extends State { ); } - Widget _buildStatusBarButton({ - required IconData icon, - required String label, - required Color color, - required VoidCallback onTap, - }) { - return InkWell( - onTap: onTap, - borderRadius: BorderRadius.circular(8), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - icon, - color: color, - size: 20, - ), - const SizedBox(height: 2), - Text( - label, - style: TextStyle( - fontSize: 11, - color: color, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ); - } } /// Dialog for adding tags with existing tags selection diff --git a/lib/ui/bookmarks/bookmark_category_recipes_screen.dart b/lib/ui/bookmarks/bookmark_category_recipes_screen.dart index 5ac2b01..60597aa 100644 --- a/lib/ui/bookmarks/bookmark_category_recipes_screen.dart +++ b/lib/ui/bookmarks/bookmark_category_recipes_screen.dart @@ -137,13 +137,6 @@ class _BookmarkCategoryRecipesScreenState extends State _getAllTags() { final tags = {}; @@ -322,9 +315,6 @@ class _BookmarkCategoryRecipesScreenState extends State with SingleTickerProv }).toList(); } - void _viewRecipe(RecipeModel recipe) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => AddRecipeScreen( - recipe: recipe, - viewMode: true, - ), - ), - ).then((_) { - // Reload bookmarks when returning from recipe view - _loadBookmarks(); - }); - } - void _openCategory(BookmarkCategory category) { Navigator.push( context, @@ -850,224 +832,3 @@ class _CategoryCard extends StatelessWidget { } } -/// Widget for displaying a recipe item in a bookmark category. -class _BookmarkRecipeItem extends StatelessWidget { - final RecipeModel recipe; - final VoidCallback onTap; - final ValueChanged onPhotoTap; - - const _BookmarkRecipeItem({ - required this.recipe, - required this.onTap, - required this.onPhotoTap, - }); - - /// Builds an image widget using ImmichService for authenticated access. - Widget _buildRecipeImage(String imageUrl) { - final mediaService = ServiceLocator.instance.mediaService; - if (mediaService == null) { - return Container( - width: 60, - height: 60, - color: Colors.grey.shade200, - child: const Icon(Icons.image, size: 32), - ); - } - - // Try to extract asset ID from Immich URL (format: .../api/assets/{id}/original) - final assetIdMatch = RegExp(r'/api/assets/([^/]+)/').firstMatch(imageUrl); - String? assetId; - - if (assetIdMatch != null) { - assetId = assetIdMatch.group(1); - } else { - // For Blossom URLs, use the full URL - assetId = imageUrl; - } - - if (assetId != null) { - return FutureBuilder( - future: mediaService.fetchImageBytes(assetId, isThumbnail: true), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return Container( - color: Colors.grey.shade200, - child: const Center( - child: CircularProgressIndicator(), - ), - ); - } - - if (snapshot.hasError || !snapshot.hasData || snapshot.data == null) { - return Container( - color: Colors.grey.shade200, - child: const Icon(Icons.broken_image, size: 32), - ); - } - - return Image.memory( - snapshot.data!, - fit: BoxFit.cover, - width: double.infinity, - height: double.infinity, - ); - }, - ); - } - - return Image.network( - imageUrl, - fit: BoxFit.cover, - width: double.infinity, - height: double.infinity, - errorBuilder: (context, error, stackTrace) { - return Container( - color: Colors.grey.shade200, - child: const Icon(Icons.broken_image, size: 32), - ); - }, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return Container( - color: Colors.grey.shade200, - child: Center( - child: CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! - : null, - ), - ), - ); - }, - ); - } - - Color _getRatingColor(double rating) { - if (rating >= 4.5) return Colors.green; - if (rating >= 3.5) return Colors.orange; - if (rating >= 2.5) return Colors.amber; - return Colors.red; - } - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Recipe image - recipe.imageUrls.isNotEmpty - ? GestureDetector( - onTap: () => onPhotoTap(0), - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: Container( - width: 80, - height: 80, - decoration: BoxDecoration( - color: Colors.grey.shade200, - borderRadius: BorderRadius.circular(12), - ), - child: _buildRecipeImage(recipe.imageUrls.first), - ), - ), - ) - : Container( - width: 80, - height: 80, - decoration: BoxDecoration( - color: Colors.grey.shade200, - borderRadius: BorderRadius.circular(12), - ), - child: Icon( - Icons.restaurant_menu, - color: Colors.grey.shade400, - size: 32, - ), - ), - const SizedBox(width: 16), - // Recipe info - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - recipe.title, - style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - if (recipe.description != null && recipe.description!.isNotEmpty) ...[ - const SizedBox(height: 6), - Text( - recipe.description!, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.grey.shade600, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ], - const SizedBox(height: 8), - Row( - children: [ - // Rating badge - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 4, - ), - decoration: BoxDecoration( - color: _getRatingColor(recipe.rating.toDouble()).withOpacity(0.15), - borderRadius: BorderRadius.circular(12), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.star, - size: 14, - color: Colors.amber, - ), - const SizedBox(width: 4), - Text( - recipe.rating.toString(), - style: Theme.of(context).textTheme.labelSmall?.copyWith( - fontWeight: FontWeight.bold, - color: _getRatingColor(recipe.rating.toDouble()), - ), - ), - ], - ), - ), - if (recipe.isFavourite) ...[ - const SizedBox(width: 8), - Icon( - Icons.favorite, - size: 16, - color: Colors.red.shade400, - ), - ], - ], - ), - ], - ), - ), - // Chevron icon - Icon( - Icons.chevron_right, - color: Colors.grey.shade400, - ), - ], - ), - ), - ); - } -} - diff --git a/lib/ui/favourites/favourites_screen.dart b/lib/ui/favourites/favourites_screen.dart index 2bb383c..9cab2b4 100644 --- a/lib/ui/favourites/favourites_screen.dart +++ b/lib/ui/favourites/favourites_screen.dart @@ -114,56 +114,6 @@ class _FavouritesScreenState extends State { } } - Future _deleteRecipe(RecipeModel recipe) async { - if (_recipeService == null) return; - - final confirmed = await showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Delete Recipe'), - content: Text('Are you sure you want to delete "${recipe.title}"?'), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(false), - child: const Text('Cancel'), - ), - TextButton( - onPressed: () => Navigator.of(context).pop(true), - style: TextButton.styleFrom(foregroundColor: Colors.red), - child: const Text('Delete'), - ), - ], - ), - ); - - if (confirmed != true) return; - - try { - await _recipeService!.deleteRecipe(recipe.id); - if (mounted) { - _hasChanges = true; // Mark that changes were made - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text('Recipe deleted successfully'), - backgroundColor: Colors.green, - duration: Duration(seconds: 1), - ), - ); - _loadFavourites(); - } - } catch (e) { - Logger.error('Failed to delete recipe', e); - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Failed to delete recipe: $e'), - backgroundColor: Colors.red, - ), - ); - } - } - } - void _viewRecipe(RecipeModel recipe) async { await Navigator.of(context).push( Material3PageRoute( @@ -177,18 +127,6 @@ class _FavouritesScreenState extends State { } } - void _editRecipe(RecipeModel recipe) async { - final result = await Navigator.of(context).push( - Material3PageRoute( - child: AddRecipeScreen(recipe: recipe), - useEmphasized: true, // Use emphasized easing for more fluid feel - ), - ); - if (result == true || mounted) { - _loadFavourites(); - } - } - Future _toggleFavorite(RecipeModel recipe) async { if (_recipeService == null) return; diff --git a/lib/ui/recipes/bookmark_dialog.dart b/lib/ui/recipes/bookmark_dialog.dart index 2031a36..cba4a4c 100644 --- a/lib/ui/recipes/bookmark_dialog.dart +++ b/lib/ui/recipes/bookmark_dialog.dart @@ -52,7 +52,7 @@ class _BookmarkDialogState extends State { setState(() => _isLoading = false); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Failed to load categories: $e')), + SnackBar(content: Text('Failed to load bookmarks: $e')), ); } } @@ -63,7 +63,7 @@ class _BookmarkDialogState extends State { if (name.isEmpty) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Category name cannot be empty')), + const SnackBar(content: Text('Bookmark name cannot be empty')), ); } return; @@ -87,7 +87,7 @@ class _BookmarkDialogState extends State { setState(() => _isCreating = false); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Failed to create category: $e')), + SnackBar(content: Text('Failed to create bookmark: $e')), ); } } @@ -151,7 +151,7 @@ class _BookmarkDialogState extends State { // Existing categories if (_allCategories.isNotEmpty) ...[ const Text( - 'Add to category:', + 'Add to bookmark:', style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), @@ -186,14 +186,14 @@ class _BookmarkDialogState extends State { ], // Create new category const Text( - 'Or create new category:', + 'Or create new bookmark:', style: TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), TextField( controller: _categoryNameController, decoration: const InputDecoration( - hintText: 'Category name', + hintText: 'Bookmark name', border: OutlineInputBorder(), ), enabled: !_isCreating, diff --git a/lib/ui/relay_management/relay_management_screen.dart b/lib/ui/relay_management/relay_management_screen.dart index 550dde8..4e8b507 100644 --- a/lib/ui/relay_management/relay_management_screen.dart +++ b/lib/ui/relay_management/relay_management_screen.dart @@ -484,24 +484,6 @@ class _RelayManagementScreenState extends State { } } - void _setDefaultServer(MediaServerConfig server) { - setState(() { - // Unset current default - for (var i = 0; i < _mediaServers.length; i++) { - if (_mediaServers[i].isDefault) { - _mediaServers[i] = _mediaServers[i].copyWith(isDefault: false); - } - } - - // Set new default - final index = _mediaServers.indexWhere((s) => s.id == server.id); - if (index != -1) { - _mediaServers[index] = server.copyWith(isDefault: true); - _defaultMediaServer = _mediaServers[index]; - } - }); - _updateMediaServerSettings(); - } @override Widget build(BuildContext context) {