Compare commits
	
		
			7 Commits
		
	
	
		
			525ac2e938
			...
			f44cd9cc9f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| f44cd9cc9f | |||
| 68c8064083 | |||
| 0ab5fee8b2 | |||
| eca2b6324b | |||
| 90fccde9e6 | |||
| c389381b1e | |||
| 4480080496 | 
							
								
								
									
										32
									
								
								API/Application/Users/Queries/QueryUsersByIds.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								API/Application/Users/Queries/QueryUsersByIds.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | using API.Models; | ||||||
|  | using API.Persistence.Repositories; | ||||||
|  | using Microsoft.AspNetCore.Mvc; | ||||||
|  | using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages.Manage; | ||||||
|  | using Newtonsoft.Json.Linq; | ||||||
|  | 
 | ||||||
|  | namespace API.Application.Users.Queries | ||||||
|  | { | ||||||
|  |     public class QueryUsersByIds | ||||||
|  |     { | ||||||
|  |         private readonly IUserRepository _repository; | ||||||
|  | 
 | ||||||
|  |         public QueryUsersByIds(IUserRepository repository) | ||||||
|  |         { | ||||||
|  |             _repository = repository; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public async Task<ActionResult<List<UserDTO>>> Handle(List<string> ids) | ||||||
|  |         { | ||||||
|  |             List<User> users = await _repository.QueryUsersByIdsAsync(ids); | ||||||
|  | 
 | ||||||
|  |             if (users == null) | ||||||
|  |             { | ||||||
|  |                 return new ConflictObjectResult(new { message = "No user on given Id" }); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             return new OkObjectResult(users.Select(user => new { id = user.Id, email = user.Email, username = user.Username, profilePicture = user.ProfilePicture, createdAt = user.CreatedAt })); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -22,18 +22,18 @@ namespace API.Controllers | |||||||
|     { |     { | ||||||
|         private readonly QueryAllUsers _queryAllUsers; |         private readonly QueryAllUsers _queryAllUsers; | ||||||
|         private readonly QueryUserById _queryUserById; |         private readonly QueryUserById _queryUserById; | ||||||
|  |         private readonly QueryUsersByIds _queryUsersByIds; | ||||||
|         private readonly CreateUser _createUser; |         private readonly CreateUser _createUser; | ||||||
|         private readonly UpdateUser _updateUser; |         private readonly UpdateUser _updateUser; | ||||||
|         private readonly DeleteUser _deleteUser; |         private readonly DeleteUser _deleteUser; | ||||||
|         private readonly LoginUser _loginUser; |         private readonly LoginUser _loginUser; | ||||||
|         private readonly TokenHelper _tokenHelper; |         private readonly TokenHelper _tokenHelper; | ||||||
| 
 |  | ||||||
|          |  | ||||||
|         private readonly IUserRepository _repository; |         private readonly IUserRepository _repository; | ||||||
| 
 | 
 | ||||||
|         public UsersController( |         public UsersController( | ||||||
|             QueryAllUsers queryAllUsers, |             QueryAllUsers queryAllUsers, | ||||||
|             QueryUserById queryUserById, |             QueryUserById queryUserById, | ||||||
|  |             QueryUsersByIds queryUsersByIds, | ||||||
|             CreateUser createUser, |             CreateUser createUser, | ||||||
|             UpdateUser updateUser, |             UpdateUser updateUser, | ||||||
|             DeleteUser deleteUser, |             DeleteUser deleteUser, | ||||||
| @ -44,14 +44,13 @@ namespace API.Controllers | |||||||
|         { |         { | ||||||
|             _queryAllUsers = queryAllUsers; |             _queryAllUsers = queryAllUsers; | ||||||
|             _queryUserById = queryUserById; |             _queryUserById = queryUserById; | ||||||
|  |             _queryUsersByIds = queryUsersByIds; | ||||||
|             _createUser = createUser; |             _createUser = createUser; | ||||||
|             _updateUser = updateUser; |             _updateUser = updateUser; | ||||||
|             _deleteUser = deleteUser; |             _deleteUser = deleteUser; | ||||||
|             _loginUser = loginUser; |             _loginUser = loginUser; | ||||||
|             _tokenHelper = tokenHelper; |             _tokenHelper = tokenHelper; | ||||||
|             _repository = repository; |             _repository = repository; | ||||||
| 
 |  | ||||||
|              |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         [HttpPost("login")] |         [HttpPost("login")] | ||||||
| @ -73,6 +72,13 @@ namespace API.Controllers | |||||||
|             return await _queryUserById.Handle(id); |             return await _queryUserById.Handle(id); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         [HttpGet("UsersByIds")] | ||||||
|  |         public async Task<ActionResult<List<UserDTO>>> GetUsersByIds(string userIds)  | ||||||
|  |         { | ||||||
|  |             List<string> ids = userIds.Split(",").ToList(); | ||||||
|  |             return await _queryUsersByIds.Handle(ids); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         [Authorize] |         [Authorize] | ||||||
|         [HttpPut] |         [HttpPut] | ||||||
|         public async Task<IActionResult> PutUser([FromForm] UpdateUserDTO UpdateUserDTO) |         public async Task<IActionResult> PutUser([FromForm] UpdateUserDTO UpdateUserDTO) | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ namespace API.Persistence.Repositories | |||||||
|         Task<bool> DeleteUserAsync(string id); |         Task<bool> DeleteUserAsync(string id); | ||||||
|         Task<List<User>> QueryAllUsersAsync(); |         Task<List<User>> QueryAllUsersAsync(); | ||||||
|         Task<User> QueryUserByIdAsync(string id); |         Task<User> QueryUserByIdAsync(string id); | ||||||
|  |         Task<List<User>> QueryUsersByIdsAsync(List<string> ids); | ||||||
|         Task<User> QueryUserByEmailAsync(string email); |         Task<User> QueryUserByEmailAsync(string email); | ||||||
|         Task<bool> UpdateUserAsync(User user); |         Task<bool> UpdateUserAsync(User user); | ||||||
|         Task<User> QueryUserByRefreshTokenAsync(string refreshToken); |         Task<User> QueryUserByRefreshTokenAsync(string refreshToken); | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| using API.Models; | using API.Models; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
|  | using Microsoft.EntityFrameworkCore.Internal; | ||||||
| using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages; | using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages; | ||||||
| 
 | 
 | ||||||
| namespace API.Persistence.Repositories | namespace API.Persistence.Repositories | ||||||
| @ -25,6 +26,18 @@ namespace API.Persistence.Repositories | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         public async Task<List<User>> QueryUsersByIdsAsync(List<string> ids)  | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 return _context.Users.Where(user => ids.Contains(user.Id)).ToList(); | ||||||
|  |             } | ||||||
|  |             catch (Exception)  | ||||||
|  |             { | ||||||
|  |                 return []; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         public async Task<string> CreateUserAsync(User user) |         public async Task<string> CreateUserAsync(User user) | ||||||
|         { |         { | ||||||
|             try |             try | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ namespace API | |||||||
|             builder.Services.AddScoped<DeleteUser>(); |             builder.Services.AddScoped<DeleteUser>(); | ||||||
|             builder.Services.AddScoped<LoginUser>(); |             builder.Services.AddScoped<LoginUser>(); | ||||||
|             builder.Services.AddScoped<IUserRepository, UserRepository>(); |             builder.Services.AddScoped<IUserRepository, UserRepository>(); | ||||||
| 
 |             builder.Services.AddScoped<QueryUsersByIds>(); | ||||||
| 
 | 
 | ||||||
|             IConfiguration Configuration = builder.Configuration; |             IConfiguration Configuration = builder.Configuration; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,6 +13,8 @@ enum ApiService { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Future<String?> request(BuildContext? context, ApiService service, String method, String path, dynamic body) async { | Future<String?> request(BuildContext? context, ApiService service, String method, String path, dynamic body) async { | ||||||
|  |   var debug = '$method $path\n    $body\n'; | ||||||
|  | 
 | ||||||
|   final messenger = context != null ? ScaffoldMessenger.of(context) : null; |   final messenger = context != null ? ScaffoldMessenger.of(context) : null; | ||||||
|   final prefs = await SharedPreferences.getInstance(); |   final prefs = await SharedPreferences.getInstance(); | ||||||
| 
 | 
 | ||||||
| @ -47,18 +49,22 @@ Future<String?> request(BuildContext? context, ApiService service, String method | |||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   } catch (e) { |   } catch (e) { | ||||||
|     debugPrint(e.toString()); |  | ||||||
|     messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server'))); |     messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server'))); | ||||||
|  | 
 | ||||||
|  |     debug += 'FAILED\n    $e'; | ||||||
|  |     debugPrint(debug); | ||||||
|  | 
 | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   debug += 'HTTP ${response.statusCode}\n    ${response.body}'; | ||||||
|  |   debugPrint(debug); | ||||||
|  | 
 | ||||||
|   if (response.statusCode < 200 || response.statusCode >= 300) { |   if (response.statusCode < 200 || response.statusCode >= 300) { | ||||||
|     try { |     try { | ||||||
|       final json = jsonDecode(response.body); |       final json = jsonDecode(response.body); | ||||||
|       messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title']))); |       messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title']))); | ||||||
|       debugPrint('API error: ' + json['message']); |  | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|       debugPrint(e.toString()); |  | ||||||
|       messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})'))); |       messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})'))); | ||||||
|     } |     } | ||||||
|     return null; |     return null; | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | import 'dart:convert'; | ||||||
|  | 
 | ||||||
| import 'package:flutter/cupertino.dart'; | import 'package:flutter/cupertino.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:mobile/base/sidemenu.dart'; | import 'package:mobile/base/sidemenu.dart'; | ||||||
| @ -12,6 +14,37 @@ class ReviewListPage extends StatefulWidget { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class _ReviewListState extends State<ReviewListPage> { | class _ReviewListState extends State<ReviewListPage> { | ||||||
|  |   List<models.User> _users = []; | ||||||
|  | 
 | ||||||
|  |   models.User? _getReviewUser(models.Review review) { | ||||||
|  |     try { | ||||||
|  |       return _users.firstWhere((user) => user.id == review.userId); | ||||||
|  |     } catch(e) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @override | ||||||
|  |   void didChangeDependencies() async { | ||||||
|  |     super.didChangeDependencies(); | ||||||
|  | 
 | ||||||
|  |     final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList; | ||||||
|  |     final reviews = arg.reviews; | ||||||
|  | 
 | ||||||
|  |     if (reviews.isEmpty) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     final userIds = reviews.map((review) => review.userId).toSet().toList(); | ||||||
|  | 
 | ||||||
|  |     final response = await api.request(context, api.ApiService.auth, 'GET', '/api/Users/UsersByIds?userIds=' + userIds.join(','), null); | ||||||
|  |     if (response == null) return; | ||||||
|  | 
 | ||||||
|  |     setState(() { | ||||||
|  |       _users = (jsonDecode(response) as List<dynamic>).map((user) => models.User.fromJson(user)).toList(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList; |     final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList; | ||||||
| @ -22,12 +55,15 @@ class _ReviewListState extends State<ReviewListPage> { | |||||||
|       selectedIndex: -1, |       selectedIndex: -1, | ||||||
|       body: Scaffold( |       body: Scaffold( | ||||||
|         backgroundColor: const Color(0xFFF9F9F9), |         backgroundColor: const Color(0xFFF9F9F9), | ||||||
|         body: SingleChildScrollView(child: Container( |         body: reviews.isEmpty | ||||||
|  |           ? const Center(child: Text('No reviews yet. Be the first to review this place')) | ||||||
|  |           : SingleChildScrollView(child: Container( | ||||||
|             decoration: const BoxDecoration(color: Color(0xFFF9F9F9)), |             decoration: const BoxDecoration(color: Color(0xFFF9F9F9)), | ||||||
|             width: MediaQuery.of(context).size.width, |             width: MediaQuery.of(context).size.width, | ||||||
|             padding: const EdgeInsets.all(20.0), |             padding: const EdgeInsets.all(20.0), | ||||||
|           child: Column(children: |             child: Column(children: [ | ||||||
|             reviews.map((review) => Container( |               for (final review in reviews) | ||||||
|  |                 Container( | ||||||
|                   width: double.infinity, |                   width: double.infinity, | ||||||
|                   padding: const EdgeInsets.all(20), |                   padding: const EdgeInsets.all(20), | ||||||
|                   margin: const EdgeInsets.only(bottom: 10), |                   margin: const EdgeInsets.only(bottom: 10), | ||||||
| @ -44,9 +80,23 @@ class _ReviewListState extends State<ReviewListPage> { | |||||||
|                   child: Row( |                   child: Row( | ||||||
|                     crossAxisAlignment: CrossAxisAlignment.start, |                     crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|                     children: [ |                     children: [ | ||||||
|                   const Padding( |                       Padding( | ||||||
|                     padding: EdgeInsets.only(top: 3), |                         padding: const EdgeInsets.only(top: 3), | ||||||
|                     child: Icon(Icons.rate_review, color: Colors.purple, size: 36), |                         child: | ||||||
|  |                           _getReviewUser(review)?.profilePicture.isNotEmpty == true | ||||||
|  |                           ? ClipOval( | ||||||
|  |                               child: Image( | ||||||
|  |                                 image: NetworkImage(_getReviewUser(review)!.profilePicture), | ||||||
|  |                                 height: 36, | ||||||
|  |                                 width: 36, | ||||||
|  |                                 fit: BoxFit.cover, | ||||||
|  |                               ), | ||||||
|  |                             ) | ||||||
|  |                           : const Icon( | ||||||
|  |                             Icons.account_circle, | ||||||
|  |                             size: 36, | ||||||
|  |                             color: Colors.grey, | ||||||
|  |                           ) | ||||||
|                       ), |                       ), | ||||||
|                       const SizedBox(width: 20), |                       const SizedBox(width: 20), | ||||||
|                       Expanded( |                       Expanded( | ||||||
| @ -56,19 +106,21 @@ class _ReviewListState extends State<ReviewListPage> { | |||||||
|                             Text(review.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), |                             Text(review.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), | ||||||
|                             Text(review.content), |                             Text(review.content), | ||||||
|                             const SizedBox(height: 10), |                             const SizedBox(height: 10), | ||||||
|                         if (review.image != null) Image.network(review.image!.imageUrl, height: 200,), |                             if (review.image != null) Image.network(review.image!.imageUrl, height: 200), | ||||||
|                             if (review.image != null) const SizedBox(height: 15), |                             if (review.image != null) const SizedBox(height: 15), | ||||||
|                             Row(children: [ |                             Row(children: [ | ||||||
|                               for (var i = 0; i < review.rating; i++) const Icon(Icons.star, color: Colors.yellow), |                               for (var i = 0; i < review.rating; i++) const Icon(Icons.star, color: Colors.yellow), | ||||||
|                               for (var i = review.rating; i < 5; i++) const Icon(Icons.star_border), |                               for (var i = review.rating; i < 5; i++) const Icon(Icons.star_border), | ||||||
|                             ]), |                             ]), | ||||||
|  |                             const SizedBox(height: 10), | ||||||
|  |                             Text('Submitted by ' + (_getReviewUser(review)?.username ?? ''), style: const TextStyle(color: Colors.grey, fontSize: 12)), | ||||||
|                           ], |                           ], | ||||||
|                         ), |                         ), | ||||||
|                       ), |                       ), | ||||||
|                     ], |                     ], | ||||||
|                   ), |                   ), | ||||||
|             )).toList(), |  | ||||||
|                 ), |                 ), | ||||||
|  |             ]), | ||||||
|           )), |           )), | ||||||
|         floatingActionButton: FloatingActionButton( |         floatingActionButton: FloatingActionButton( | ||||||
|           onPressed: () async { |           onPressed: () async { | ||||||
|  | |||||||
| @ -194,10 +194,10 @@ packages: | |||||||
|     dependency: "direct dev" |     dependency: "direct dev" | ||||||
|     description: |     description: | ||||||
|       name: flutter_lints |       name: flutter_lints | ||||||
|       sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" |       sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" | ||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "3.0.2" |     version: "4.0.0" | ||||||
|   flutter_map: |   flutter_map: | ||||||
|     dependency: "direct main" |     dependency: "direct main" | ||||||
|     description: |     description: | ||||||
| @ -412,10 +412,10 @@ packages: | |||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
|       name: lints |       name: lints | ||||||
|       sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 |       sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" | ||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "3.0.0" |     version: "4.0.0" | ||||||
|   lists: |   lists: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
|  | |||||||
| @ -53,7 +53,7 @@ dev_dependencies: | |||||||
|   # activated in the `analysis_options.yaml` file located at the root of your |   # activated in the `analysis_options.yaml` file located at the root of your | ||||||
|   # package. See that file for information about deactivating specific lint |   # package. See that file for information about deactivating specific lint | ||||||
|   # rules and activating additional ones. |   # rules and activating additional ones. | ||||||
|   flutter_lints: ^3.0.0 |   flutter_lints: ^4.0.0 | ||||||
| 
 | 
 | ||||||
| # For information on the generic Dart part of this file, see the | # For information on the generic Dart part of this file, see the | ||||||
| # following page: https://dart.dev/tools/pub/pubspec | # following page: https://dart.dev/tools/pub/pubspec | ||||||
|  | |||||||
| @ -219,7 +219,9 @@ async fn create_review(auth: AuthorizedUser, data: web::Data<AppData>, input: we | |||||||
|             content: input.content.clone(), |             content: input.content.clone(), | ||||||
|             rating: input.rating.clone(), |             rating: input.rating.clone(), | ||||||
|             image_id: input.image_id, |             image_id: input.image_id, | ||||||
|             image: None, |             image: input.image_id.and_then(|image_id| { | ||||||
|  |                 db.query_row("SELECT * FROM images WHERE id = :id LIMIT 1", &[(":id", &image_id.to_string())], |row| Image::from_row(row)).ok() | ||||||
|  |             }), | ||||||
|         }), |         }), | ||||||
|         Err(_) => HttpResponse::InternalServerError().finish(), |         Err(_) => HttpResponse::InternalServerError().finish(), | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user