162 Start
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_navigation/models/category.dart';
|
||||
import 'package:flutter_navigation/models/meal.dart';
|
||||
|
||||
// Constants in Dart should be written in lowerCamelcase.
|
||||
const availableCategories = [
|
||||
Category(
|
||||
id: 'c1',
|
||||
@@ -53,4 +55,355 @@ const availableCategories = [
|
||||
title: 'Summer',
|
||||
color: Colors.teal,
|
||||
),
|
||||
];
|
||||
|
||||
const dummyMeals = [
|
||||
Meal(
|
||||
id: 'm1',
|
||||
categories: [
|
||||
'c1',
|
||||
'c2',
|
||||
],
|
||||
title: 'Spaghetti with Tomato Sauce',
|
||||
affordability: Affordability.affordable,
|
||||
complexity: Complexity.simple,
|
||||
imageUrl:
|
||||
'https://upload.wikimedia.org/wikipedia/commons/thumb/2/20/Spaghetti_Bolognese_mit_Parmesan_oder_Grana_Padano.jpg/800px-Spaghetti_Bolognese_mit_Parmesan_oder_Grana_Padano.jpg',
|
||||
duration: 20,
|
||||
ingredients: [
|
||||
'4 Tomatoes',
|
||||
'1 Tablespoon of Olive Oil',
|
||||
'1 Onion',
|
||||
'250g Spaghetti',
|
||||
'Spices',
|
||||
'Cheese (optional)'
|
||||
],
|
||||
steps: [
|
||||
'Cut the tomatoes and the onion into small pieces.',
|
||||
'Boil some water - add salt to it once it boils.',
|
||||
'Put the spaghetti into the boiling water - they should be done in about 10 to 12 minutes.',
|
||||
'In the meantime, heaten up some olive oil and add the cut onion.',
|
||||
'After 2 minutes, add the tomato pieces, salt, pepper and your other spices.',
|
||||
'The sauce will be done once the spaghetti are.',
|
||||
'Feel free to add some cheese on top of the finished dish.'
|
||||
],
|
||||
isGlutenFree: false,
|
||||
isVegan: true,
|
||||
isVegetarian: true,
|
||||
isLactoseFree: true,
|
||||
),
|
||||
Meal(
|
||||
id: 'm2',
|
||||
categories: [
|
||||
'c2',
|
||||
],
|
||||
title: 'Toast Hawaii',
|
||||
affordability: Affordability.affordable,
|
||||
complexity: Complexity.simple,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2018/07/11/21/51/toast-3532016_1280.jpg',
|
||||
duration: 10,
|
||||
ingredients: [
|
||||
'1 Slice White Bread',
|
||||
'1 Slice Ham',
|
||||
'1 Slice Pineapple',
|
||||
'1-2 Slices of Cheese',
|
||||
'Butter'
|
||||
],
|
||||
steps: [
|
||||
'Butter one side of the white bread',
|
||||
'Layer ham, the pineapple and cheese on the white bread',
|
||||
'Bake the toast for round about 10 minutes in the oven at 200°C'
|
||||
],
|
||||
isGlutenFree: false,
|
||||
isVegan: false,
|
||||
isVegetarian: false,
|
||||
isLactoseFree: false,
|
||||
),
|
||||
Meal(
|
||||
id: 'm3',
|
||||
categories: [
|
||||
'c2',
|
||||
'c3',
|
||||
],
|
||||
title: 'Classic Hamburger',
|
||||
affordability: Affordability.pricey,
|
||||
complexity: Complexity.simple,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2014/10/23/18/05/burger-500054_1280.jpg',
|
||||
duration: 45,
|
||||
ingredients: [
|
||||
'300g Cattle Hack',
|
||||
'1 Tomato',
|
||||
'1 Cucumber',
|
||||
'1 Onion',
|
||||
'Ketchup',
|
||||
'2 Burger Buns'
|
||||
],
|
||||
steps: [
|
||||
'Form 2 patties',
|
||||
'Fry the patties for c. 4 minutes on each side',
|
||||
'Quickly fry the buns for c. 1 minute on each side',
|
||||
'Bruch buns with ketchup',
|
||||
'Serve burger with tomato, cucumber and onion'
|
||||
],
|
||||
isGlutenFree: false,
|
||||
isVegan: false,
|
||||
isVegetarian: false,
|
||||
isLactoseFree: true,
|
||||
),
|
||||
Meal(
|
||||
id: 'm4',
|
||||
categories: [
|
||||
'c4',
|
||||
],
|
||||
title: 'Wiener Schnitzel',
|
||||
affordability: Affordability.luxurious,
|
||||
complexity: Complexity.challenging,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2018/03/31/19/29/schnitzel-3279045_1280.jpg',
|
||||
duration: 60,
|
||||
ingredients: [
|
||||
'8 Veal Cutlets',
|
||||
'4 Eggs',
|
||||
'200g Bread Crumbs',
|
||||
'100g Flour',
|
||||
'300ml Butter',
|
||||
'100g Vegetable Oil',
|
||||
'Salt',
|
||||
'Lemon Slices'
|
||||
],
|
||||
steps: [
|
||||
'Tenderize the veal to about 2–4mm, and salt on both sides.',
|
||||
'On a flat plate, stir the eggs briefly with a fork.',
|
||||
'Lightly coat the cutlets in flour then dip into the egg, and finally, coat in breadcrumbs.',
|
||||
'Heat the butter and oil in a large pan (allow the fat to get very hot) and fry the schnitzels until golden brown on both sides.',
|
||||
'Make sure to toss the pan regularly so that the schnitzels are surrounded by oil and the crumbing becomes ‘fluffy’.',
|
||||
'Remove, and drain on kitchen paper. Fry the parsley in the remaining oil and drain.',
|
||||
'Place the schnitzels on awarmed plate and serve garnishedwith parsley and slices of lemon.'
|
||||
],
|
||||
isGlutenFree: false,
|
||||
isVegan: false,
|
||||
isVegetarian: false,
|
||||
isLactoseFree: false,
|
||||
),
|
||||
Meal(
|
||||
id: 'm5',
|
||||
categories: [
|
||||
'c2'
|
||||
'c5',
|
||||
'c10',
|
||||
],
|
||||
title: 'Salad with Smoked Salmon',
|
||||
affordability: Affordability.luxurious,
|
||||
complexity: Complexity.simple,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2016/10/25/13/29/smoked-salmon-salad-1768890_1280.jpg',
|
||||
duration: 15,
|
||||
ingredients: [
|
||||
'Arugula',
|
||||
'Lamb\'s Lettuce',
|
||||
'Parsley',
|
||||
'Fennel',
|
||||
'200g Smoked Salmon',
|
||||
'Mustard',
|
||||
'Balsamic Vinegar',
|
||||
'Olive Oil',
|
||||
'Salt and Pepper'
|
||||
],
|
||||
steps: [
|
||||
'Wash and cut salad and herbs',
|
||||
'Dice the salmon',
|
||||
'Process mustard, vinegar and olive oil into a dessing',
|
||||
'Prepare the salad',
|
||||
'Add salmon cubes and dressing'
|
||||
],
|
||||
isGlutenFree: true,
|
||||
isVegan: false,
|
||||
isVegetarian: true,
|
||||
isLactoseFree: true,
|
||||
),
|
||||
Meal(
|
||||
id: 'm6',
|
||||
categories: [
|
||||
'c6',
|
||||
'c10',
|
||||
],
|
||||
title: 'Delicious Orange Mousse',
|
||||
affordability: Affordability.affordable,
|
||||
complexity: Complexity.hard,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2017/05/01/05/18/pastry-2274750_1280.jpg',
|
||||
duration: 240,
|
||||
ingredients: [
|
||||
'4 Sheets of Gelatine',
|
||||
'150ml Orange Juice',
|
||||
'80g Sugar',
|
||||
'300g Yoghurt',
|
||||
'200g Cream',
|
||||
'Orange Peel',
|
||||
],
|
||||
steps: [
|
||||
'Dissolve gelatine in pot',
|
||||
'Add orange juice and sugar',
|
||||
'Take pot off the stove',
|
||||
'Add 2 tablespoons of yoghurt',
|
||||
'Stir gelatin under remaining yoghurt',
|
||||
'Cool everything down in the refrigerator',
|
||||
'Whip the cream and lift it under die orange mass',
|
||||
'Cool down again for at least 4 hours',
|
||||
'Serve with orange peel',
|
||||
],
|
||||
isGlutenFree: true,
|
||||
isVegan: false,
|
||||
isVegetarian: true,
|
||||
isLactoseFree: false,
|
||||
),
|
||||
Meal(
|
||||
id: 'm7',
|
||||
categories: [
|
||||
'c7',
|
||||
],
|
||||
title: 'Pancakes',
|
||||
affordability: Affordability.affordable,
|
||||
complexity: Complexity.simple,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2018/07/10/21/23/pancake-3529653_1280.jpg',
|
||||
duration: 20,
|
||||
ingredients: [
|
||||
'1 1/2 Cups all-purpose Flour',
|
||||
'3 1/2 Teaspoons Baking Powder',
|
||||
'1 Teaspoon Salt',
|
||||
'1 Tablespoon White Sugar',
|
||||
'1 1/4 cups Milk',
|
||||
'1 Egg',
|
||||
'3 Tablespoons Butter, melted',
|
||||
],
|
||||
steps: [
|
||||
'In a large bowl, sift together the flour, baking powder, salt and sugar.',
|
||||
'Make a well in the center and pour in the milk, egg and melted butter; mix until smooth.',
|
||||
'Heat a lightly oiled griddle or frying pan over medium high heat.',
|
||||
'Pour or scoop the batter onto the griddle, using approximately 1/4 cup for each pancake. Brown on both sides and serve hot.'
|
||||
],
|
||||
isGlutenFree: true,
|
||||
isVegan: false,
|
||||
isVegetarian: true,
|
||||
isLactoseFree: false,
|
||||
),
|
||||
Meal(
|
||||
id: 'm8',
|
||||
categories: [
|
||||
'c8',
|
||||
],
|
||||
title: 'Creamy Indian Chicken Curry',
|
||||
affordability: Affordability.pricey,
|
||||
complexity: Complexity.challenging,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2018/06/18/16/05/indian-food-3482749_1280.jpg',
|
||||
duration: 35,
|
||||
ingredients: [
|
||||
'4 Chicken Breasts',
|
||||
'1 Onion',
|
||||
'2 Cloves of Garlic',
|
||||
'1 Piece of Ginger',
|
||||
'4 Tablespoons Almonds',
|
||||
'1 Teaspoon Cayenne Pepper',
|
||||
'500ml Coconut Milk',
|
||||
],
|
||||
steps: [
|
||||
'Slice and fry the chicken breast',
|
||||
'Process onion, garlic and ginger into paste and sauté everything',
|
||||
'Add spices and stir fry',
|
||||
'Add chicken breast + 250ml of water and cook everything for 10 minutes',
|
||||
'Add coconut milk',
|
||||
'Serve with rice'
|
||||
],
|
||||
isGlutenFree: true,
|
||||
isVegan: false,
|
||||
isVegetarian: false,
|
||||
isLactoseFree: true,
|
||||
),
|
||||
Meal(
|
||||
id: 'm9',
|
||||
categories: [
|
||||
'c9',
|
||||
],
|
||||
title: 'Chocolate Souffle',
|
||||
affordability: Affordability.affordable,
|
||||
complexity: Complexity.hard,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2014/08/07/21/07/souffle-412785_1280.jpg',
|
||||
duration: 45,
|
||||
ingredients: [
|
||||
'1 Teaspoon melted Butter',
|
||||
'2 Tablespoons white Sugar',
|
||||
'2 Ounces 70% dark Chocolate, broken into pieces',
|
||||
'1 Tablespoon Butter',
|
||||
'1 Tablespoon all-purpose Flour',
|
||||
'4 1/3 tablespoons cold Milk',
|
||||
'1 Pinch Salt',
|
||||
'1 Pinch Cayenne Pepper',
|
||||
'1 Large Egg Yolk',
|
||||
'2 Large Egg Whites',
|
||||
'1 Pinch Cream of Tartar',
|
||||
'1 Tablespoon white Sugar',
|
||||
],
|
||||
steps: [
|
||||
'Preheat oven to 190°C. Line a rimmed baking sheet with parchment paper.',
|
||||
'Brush bottom and sides of 2 ramekins lightly with 1 teaspoon melted butter; cover bottom and sides right up to the rim.',
|
||||
'Add 1 tablespoon white sugar to ramekins. Rotate ramekins until sugar coats all surfaces.',
|
||||
'Place chocolate pieces in a metal mixing bowl.',
|
||||
'Place bowl over a pan of about 3 cups hot water over low heat.',
|
||||
'Melt 1 tablespoon butter in a skillet over medium heat. Sprinkle in flour. Whisk until flour is incorporated into butter and mixture thickens.',
|
||||
'Whisk in cold milk until mixture becomes smooth and thickens. Transfer mixture to bowl with melted chocolate.',
|
||||
'Add salt and cayenne pepper. Mix together thoroughly. Add egg yolk and mix to combine.',
|
||||
'Leave bowl above the hot (not simmering) water to keep chocolate warm while you whip the egg whites.',
|
||||
'Place 2 egg whites in a mixing bowl; add cream of tartar. Whisk until mixture begins to thicken and a drizzle from the whisk stays on the surface about 1 second before disappearing into the mix.',
|
||||
'Add 1/3 of sugar and whisk in. Whisk in a bit more sugar about 15 seconds.',
|
||||
'whisk in the rest of the sugar. Continue whisking until mixture is about as thick as shaving cream and holds soft peaks, 3 to 5 minutes.',
|
||||
'Transfer a little less than half of egg whites to chocolate.',
|
||||
'Mix until egg whites are thoroughly incorporated into the chocolate.',
|
||||
'Add the rest of the egg whites; gently fold into the chocolate with a spatula, lifting from the bottom and folding over.',
|
||||
'Stop mixing after the egg white disappears. Divide mixture between 2 prepared ramekins. Place ramekins on prepared baking sheet.',
|
||||
'Bake in preheated oven until scuffles are puffed and have risen above the top of the rims, 12 to 15 minutes.',
|
||||
],
|
||||
isGlutenFree: true,
|
||||
isVegan: false,
|
||||
isVegetarian: true,
|
||||
isLactoseFree: false,
|
||||
),
|
||||
Meal(
|
||||
id: 'm10',
|
||||
categories: [
|
||||
'c2',
|
||||
'c5',
|
||||
'c10',
|
||||
],
|
||||
title: 'Asparagus Salad with Cherry Tomatoes',
|
||||
affordability: Affordability.luxurious,
|
||||
complexity: Complexity.simple,
|
||||
imageUrl:
|
||||
'https://cdn.pixabay.com/photo/2018/04/09/18/26/asparagus-3304997_1280.jpg',
|
||||
duration: 30,
|
||||
ingredients: [
|
||||
'White and Green Asparagus',
|
||||
'30g Pine Nuts',
|
||||
'300g Cherry Tomatoes',
|
||||
'Salad',
|
||||
'Salt, Pepper and Olive Oil'
|
||||
],
|
||||
steps: [
|
||||
'Wash, peel and cut the asparagus',
|
||||
'Cook in salted water',
|
||||
'Salt and pepper the asparagus',
|
||||
'Roast the pine nuts',
|
||||
'Halve the tomatoes',
|
||||
'Mix with asparagus, salad and dressing',
|
||||
'Serve with Baguette'
|
||||
],
|
||||
isGlutenFree: true,
|
||||
isVegan: true,
|
||||
isVegetarian: true,
|
||||
isLactoseFree: true,
|
||||
),
|
||||
];
|
||||
@@ -0,0 +1,43 @@
|
||||
enum Complexity {
|
||||
simple,
|
||||
challenging,
|
||||
hard,
|
||||
}
|
||||
|
||||
enum Affordability {
|
||||
affordable,
|
||||
pricey,
|
||||
luxurious,
|
||||
}
|
||||
|
||||
class Meal {
|
||||
const Meal({
|
||||
required this.id,
|
||||
required this.categories,
|
||||
required this.title,
|
||||
required this.imageUrl,
|
||||
required this.ingredients,
|
||||
required this.steps,
|
||||
required this.duration,
|
||||
required this.complexity,
|
||||
required this.affordability,
|
||||
required this.isGlutenFree,
|
||||
required this.isLactoseFree,
|
||||
required this.isVegan,
|
||||
required this.isVegetarian,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final List<String> categories;
|
||||
final String title;
|
||||
final String imageUrl;
|
||||
final List<String> ingredients;
|
||||
final List<String> steps;
|
||||
final int duration;
|
||||
final Complexity complexity;
|
||||
final Affordability affordability;
|
||||
final bool isGlutenFree;
|
||||
final bool isLactoseFree;
|
||||
final bool isVegan;
|
||||
final bool isVegetarian;
|
||||
}
|
||||
@@ -1,11 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_navigation/data/dummy_data.dart';
|
||||
import 'package:flutter_navigation/screens/meals.dart';
|
||||
|
||||
import '../models/category.dart';
|
||||
import '../widgets/category_grid_item.dart';
|
||||
|
||||
class CategoriesScreen extends StatelessWidget {
|
||||
const CategoriesScreen({super.key});
|
||||
|
||||
void _selectCategory(BuildContext context, Category category) {
|
||||
final filteredMeals = dummyMeals.where((meal) => meal.categories.contains(category.id)).toList();
|
||||
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (ctx) => MealsScreen(
|
||||
title: category.title,
|
||||
meals: filteredMeals,
|
||||
),
|
||||
),
|
||||
); //Navigator.push(context, route);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -22,7 +37,12 @@ class CategoriesScreen extends StatelessWidget {
|
||||
),
|
||||
children: [
|
||||
for (final category in availableCategories)
|
||||
CategoryGridItem(category: category)
|
||||
CategoryGridItem(
|
||||
category: category,
|
||||
onSelectCategory: () {
|
||||
_selectCategory(context, category);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_navigation/widgets/meal_item.dart';
|
||||
|
||||
import '../models/meal.dart';
|
||||
|
||||
class MealsScreen extends StatelessWidget {
|
||||
const MealsScreen({super.key, required this.title, required this.meals});
|
||||
|
||||
final String title;
|
||||
final List<Meal> meals;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget content = Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'Uh oh ... nothing here!',
|
||||
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Try selecting a different category!',
|
||||
style: Theme.of(context).textTheme.bodyLarge!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onBackground,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (meals.isNotEmpty) {
|
||||
content = ListView.builder(
|
||||
itemCount: meals.length,
|
||||
itemBuilder: (ctx, index) => MealItem(meal: meals[index]),
|
||||
);
|
||||
}
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(title),
|
||||
),
|
||||
body: content,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,14 +3,19 @@ import 'package:flutter/material.dart';
|
||||
import '../models/category.dart';
|
||||
|
||||
class CategoryGridItem extends StatelessWidget {
|
||||
const CategoryGridItem({super.key, required this.category});
|
||||
const CategoryGridItem({
|
||||
super.key,
|
||||
required this.category,
|
||||
required this.onSelectCategory,
|
||||
});
|
||||
|
||||
final Category category;
|
||||
final void Function() onSelectCategory;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
onTap: onSelectCategory,
|
||||
splashColor: Theme.of(context).primaryColor,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: Container(
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_navigation/models/meal.dart';
|
||||
import 'package:transparent_image/transparent_image.dart';
|
||||
|
||||
class MealItem extends StatelessWidget {
|
||||
const MealItem({
|
||||
super.key,
|
||||
required this.meal,
|
||||
});
|
||||
|
||||
final Meal meal;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
elevation: 2,
|
||||
child: InkWell(
|
||||
onTap: () {},
|
||||
child: Stack(
|
||||
children: [
|
||||
FadeInImage(
|
||||
placeholder: MemoryImage(kTransparentImage),
|
||||
image: NetworkImage(meal.imageUrl),
|
||||
fit: BoxFit.cover,
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Container(
|
||||
color: Colors.black54,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 6,
|
||||
horizontal: 44,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
meal.title,
|
||||
maxLines: 2,
|
||||
textAlign: TextAlign.center,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -272,6 +272,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
transparent_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: transparent_image
|
||||
sha256: e8991d955a2094e197ca24c645efec2faf4285772a4746126ca12875e54ca02f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -36,6 +36,7 @@ dependencies:
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.2
|
||||
google_fonts: ^5.1.0
|
||||
transparent_image: ^2.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user