From 4480080496a15c88e6962eca277486d2eedfb4bf Mon Sep 17 00:00:00 2001 From: Alexandertp Date: Wed, 11 Sep 2024 15:34:03 +0200 Subject: [PATCH 1/6] Implement backend for displaying user info Frontend display currently not working Co-authored-by: Reimar --- .../Users/Queries/QueryUsersByIds.cs | 32 +++++++++++++++++++ API/Controllers/UsersController.cs | 17 ++++++---- .../Repositories/IUserRepository.cs | 1 + .../Repositories/UserRepository.cs | 13 ++++++++ API/Program.cs | 2 +- Mobile/lib/api.dart | 6 ++-- Mobile/lib/reviewlist.dart | 23 +++++++++++++ Mobile/pubspec.lock | 8 ++--- Mobile/pubspec.yaml | 2 +- 9 files changed, 89 insertions(+), 15 deletions(-) create mode 100644 API/Application/Users/Queries/QueryUsersByIds.cs diff --git a/API/Application/Users/Queries/QueryUsersByIds.cs b/API/Application/Users/Queries/QueryUsersByIds.cs new file mode 100644 index 0000000..034f56d --- /dev/null +++ b/API/Application/Users/Queries/QueryUsersByIds.cs @@ -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>> Handle(List ids) + { + List 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 })); + + } + + } +} diff --git a/API/Controllers/UsersController.cs b/API/Controllers/UsersController.cs index 985d255..f9959e7 100644 --- a/API/Controllers/UsersController.cs +++ b/API/Controllers/UsersController.cs @@ -22,36 +22,35 @@ namespace API.Controllers { private readonly QueryAllUsers _queryAllUsers; private readonly QueryUserById _queryUserById; + private readonly QueryUsersByIds _queryUsersByIds; private readonly CreateUser _createUser; private readonly UpdateUser _updateUser; private readonly DeleteUser _deleteUser; private readonly LoginUser _loginUser; private readonly TokenHelper _tokenHelper; - - private readonly IUserRepository _repository; public UsersController( QueryAllUsers queryAllUsers, QueryUserById queryUserById, + QueryUsersByIds queryUsersByIds, CreateUser createUser, UpdateUser updateUser, DeleteUser deleteUser, LoginUser loginUser, TokenHelper tokenHelper, IUserRepository repository - ) + ) { _queryAllUsers = queryAllUsers; _queryUserById = queryUserById; + _queryUsersByIds = queryUsersByIds; _createUser = createUser; _updateUser = updateUser; _deleteUser = deleteUser; _loginUser = loginUser; _tokenHelper = tokenHelper; _repository = repository; - - } [HttpPost("login")] @@ -73,9 +72,15 @@ namespace API.Controllers return await _queryUserById.Handle(id); } + [HttpGet("UsersByIds")] + public async Task>> GetUsersByIds(List userIds) + { + return await _queryUsersByIds.Handle(userIds); + } + [Authorize] [HttpPut] - public async Task PutUser([FromForm ]UpdateUserDTO UpdateUserDTO) + public async Task PutUser([FromForm] UpdateUserDTO UpdateUserDTO) { return await _updateUser.Handle(UpdateUserDTO); } diff --git a/API/Persistence/Repositories/IUserRepository.cs b/API/Persistence/Repositories/IUserRepository.cs index 9f2584d..737fb2d 100644 --- a/API/Persistence/Repositories/IUserRepository.cs +++ b/API/Persistence/Repositories/IUserRepository.cs @@ -8,6 +8,7 @@ namespace API.Persistence.Repositories Task DeleteUserAsync(string id); Task> QueryAllUsersAsync(); Task QueryUserByIdAsync(string id); + Task> QueryUsersByIdsAsync(List ids); Task QueryUserByEmailAsync(string email); Task UpdateUserAsync(User user); Task QueryUserByRefreshTokenAsync(string refreshToken); diff --git a/API/Persistence/Repositories/UserRepository.cs b/API/Persistence/Repositories/UserRepository.cs index e007973..12a04c0 100644 --- a/API/Persistence/Repositories/UserRepository.cs +++ b/API/Persistence/Repositories/UserRepository.cs @@ -1,5 +1,6 @@ using API.Models; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages; namespace API.Persistence.Repositories @@ -25,6 +26,18 @@ namespace API.Persistence.Repositories } } + public async Task> QueryUsersByIdsAsync(List ids) + { + try + { + return _context.Users.Where(user => ids.Contains(user.Id)).ToList(); + } + catch (Exception) + { + return []; + } + } + public async Task CreateUserAsync(User user) { try diff --git a/API/Program.cs b/API/Program.cs index 522abfe..a5cd0be 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -43,7 +43,7 @@ namespace API builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - + builder.Services.AddScoped(); IConfiguration Configuration = builder.Configuration; diff --git a/Mobile/lib/api.dart b/Mobile/lib/api.dart index e99a012..fa8b837 100644 --- a/Mobile/lib/api.dart +++ b/Mobile/lib/api.dart @@ -22,7 +22,7 @@ Future request(BuildContext? context, ApiService service, String method }; final token = prefs.getString('token'); - final Map headers = {}; + final Map headers = {'Accept': 'application/json'}; if (token != null) headers.addAll({'Authorization': 'Bearer $token'}); final http.Response response; @@ -47,7 +47,7 @@ Future request(BuildContext? context, ApiService service, String method ); } } catch (e) { - debugPrint(e.toString()); + debugPrint('Can\'t send requst: ' + e.toString()); messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server'))); return null; } @@ -58,7 +58,7 @@ Future request(BuildContext? context, ApiService service, String method messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title']))); debugPrint('API error: ' + json['message']); } catch (e) { - debugPrint(e.toString()); + debugPrint('Can\'t parse response: ' + e.toString()); messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})'))); } return null; diff --git a/Mobile/lib/reviewlist.dart b/Mobile/lib/reviewlist.dart index 4dc2d1b..7be225d 100644 --- a/Mobile/lib/reviewlist.dart +++ b/Mobile/lib/reviewlist.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:mobile/base/sidemenu.dart'; @@ -12,6 +14,27 @@ class ReviewListPage extends StatefulWidget { } class _ReviewListState extends State { + List users = []; + + @override + void didChangeDependencies() async { + super.didChangeDependencies(); + + final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList; + final reviews = arg.reviews; + + final userIds = reviews.map((review) => review.userId).toSet().toList(); + final queryParams = userIds.map((id) => "userIds=$id"); + final queryString = "?${queryParams.join("&")}"; + + final response = await api.request(context, api.ApiService.auth, 'GET', '/api/Users/UsersByIds$queryString', null); + if (response == null) return; + + debugPrint('response: ' + response); + + users = (jsonDecode(response) as List).map((user) => models.User.fromJson(user)).toList(); + } + @override Widget build(BuildContext context) { final arg = ModalRoute.of(context)!.settings.arguments as models.ReviewList; diff --git a/Mobile/pubspec.lock b/Mobile/pubspec.lock index f388662..f835c02 100644 --- a/Mobile/pubspec.lock +++ b/Mobile/pubspec.lock @@ -194,10 +194,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "4.0.0" flutter_map: dependency: "direct main" description: @@ -412,10 +412,10 @@ packages: dependency: transitive description: name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" lists: dependency: transitive description: diff --git a/Mobile/pubspec.yaml b/Mobile/pubspec.yaml index 9b609b5..3f66b54 100644 --- a/Mobile/pubspec.yaml +++ b/Mobile/pubspec.yaml @@ -53,7 +53,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # 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 # following page: https://dart.dev/tools/pub/pubspec From c389381b1e72f84b919ce065d080f5d387ac3e11 Mon Sep 17 00:00:00 2001 From: Reimar Date: Fri, 13 Sep 2024 08:40:32 +0200 Subject: [PATCH 2/6] Fix userIds request not working, show username on reviews --- API/Controllers/UsersController.cs | 5 +++-- Mobile/lib/api.dart | 6 ++++-- Mobile/lib/reviewlist.dart | 12 ++++++++---- Mobile/pubspec.lock | 24 ++++++++++++------------ 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/API/Controllers/UsersController.cs b/API/Controllers/UsersController.cs index f9959e7..14f50b7 100644 --- a/API/Controllers/UsersController.cs +++ b/API/Controllers/UsersController.cs @@ -73,9 +73,10 @@ namespace API.Controllers } [HttpGet("UsersByIds")] - public async Task>> GetUsersByIds(List userIds) + public async Task>> GetUsersByIds(string userIds) { - return await _queryUsersByIds.Handle(userIds); + List ids = userIds.Split(",").ToList(); + return await _queryUsersByIds.Handle(ids); } [Authorize] diff --git a/Mobile/lib/api.dart b/Mobile/lib/api.dart index fa8b837..1ab512a 100644 --- a/Mobile/lib/api.dart +++ b/Mobile/lib/api.dart @@ -13,6 +13,8 @@ enum ApiService { } Future request(BuildContext? context, ApiService service, String method, String path, dynamic body) async { + debugPrint('$method $path'); + final messenger = context != null ? ScaffoldMessenger.of(context) : null; final prefs = await SharedPreferences.getInstance(); @@ -22,7 +24,7 @@ Future request(BuildContext? context, ApiService service, String method }; final token = prefs.getString('token'); - final Map headers = {'Accept': 'application/json'}; + final Map headers = {}; if (token != null) headers.addAll({'Authorization': 'Bearer $token'}); final http.Response response; @@ -58,7 +60,7 @@ Future request(BuildContext? context, ApiService service, String method messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title']))); debugPrint('API error: ' + json['message']); } catch (e) { - debugPrint('Can\'t parse response: ' + e.toString()); + debugPrint('Can\'t parse response: ' + response.body); messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})'))); } return null; diff --git a/Mobile/lib/reviewlist.dart b/Mobile/lib/reviewlist.dart index 7be225d..325eb8a 100644 --- a/Mobile/lib/reviewlist.dart +++ b/Mobile/lib/reviewlist.dart @@ -16,6 +16,10 @@ class ReviewListPage extends StatefulWidget { class _ReviewListState extends State { List users = []; + models.User? _getReviewUser(models.Review review) { + return users.firstWhere((user) => user.id == review.userId); + } + @override void didChangeDependencies() async { super.didChangeDependencies(); @@ -24,10 +28,8 @@ class _ReviewListState extends State { final reviews = arg.reviews; final userIds = reviews.map((review) => review.userId).toSet().toList(); - final queryParams = userIds.map((id) => "userIds=$id"); - final queryString = "?${queryParams.join("&")}"; - final response = await api.request(context, api.ApiService.auth, 'GET', '/api/Users/UsersByIds$queryString', null); + final response = await api.request(context, api.ApiService.auth, 'GET', '/api/Users/UsersByIds?userIds=' + userIds.join(','), null); if (response == null) return; debugPrint('response: ' + response); @@ -79,12 +81,14 @@ class _ReviewListState extends State { Text(review.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), Text(review.content), 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), Row(children: [ 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), ]), + const SizedBox(height: 10), + Text('Submitted by ' + (_getReviewUser(review)?.username ?? ''), style: const TextStyle(color: Colors.grey, fontSize: 12)), ], ), ), diff --git a/Mobile/pubspec.lock b/Mobile/pubspec.lock index f835c02..c537257 100644 --- a/Mobile/pubspec.lock +++ b/Mobile/pubspec.lock @@ -388,18 +388,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -444,18 +444,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" mgrs_dart: dependency: transitive description: @@ -673,10 +673,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -713,10 +713,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" web: dependency: transitive description: From 90fccde9e6e0ac2c3e3ce164232da6a03982eebe Mon Sep 17 00:00:00 2001 From: Reimar Date: Fri, 13 Sep 2024 12:02:05 +0200 Subject: [PATCH 3/6] Add better debugging for http requests --- Mobile/lib/api.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Mobile/lib/api.dart b/Mobile/lib/api.dart index 1ab512a..63b0bfa 100644 --- a/Mobile/lib/api.dart +++ b/Mobile/lib/api.dart @@ -13,7 +13,7 @@ enum ApiService { } Future request(BuildContext? context, ApiService service, String method, String path, dynamic body) async { - debugPrint('$method $path'); + var debug = '$method $path\n $body\n'; final messenger = context != null ? ScaffoldMessenger.of(context) : null; final prefs = await SharedPreferences.getInstance(); @@ -49,18 +49,22 @@ Future request(BuildContext? context, ApiService service, String method ); } } catch (e) { - debugPrint('Can\'t send requst: ' + e.toString()); messenger?.showSnackBar(const SnackBar(content: Text('Unable to connect to server'))); + + debug += 'FAILED\n $e'; + debugPrint(debug); + return null; } + debug += 'HTTP ${response.statusCode}\n ${response.body}'; + debugPrint(debug); + if (response.statusCode < 200 || response.statusCode >= 300) { try { final json = jsonDecode(response.body); messenger?.showSnackBar(SnackBar(content: Text(json['message'] ?? json['title']))); - debugPrint('API error: ' + json['message']); } catch (e) { - debugPrint('Can\'t parse response: ' + response.body); messenger?.showSnackBar(SnackBar(content: Text('Something went wrong (HTTP ${response.statusCode})'))); } return null; From eca2b6324ba1ef8000046249e00c8c3d47ae4be0 Mon Sep 17 00:00:00 2001 From: Reimar Date: Fri, 13 Sep 2024 12:02:22 +0200 Subject: [PATCH 4/6] Fix errors on review page, show text when no reviews --- Mobile/lib/reviewlist.dart | 117 ++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 53 deletions(-) diff --git a/Mobile/lib/reviewlist.dart b/Mobile/lib/reviewlist.dart index 325eb8a..13f5fd0 100644 --- a/Mobile/lib/reviewlist.dart +++ b/Mobile/lib/reviewlist.dart @@ -14,10 +14,14 @@ class ReviewListPage extends StatefulWidget { } class _ReviewListState extends State { - List users = []; + List _users = []; models.User? _getReviewUser(models.Review review) { - return users.firstWhere((user) => user.id == review.userId); + try { + return _users.firstWhere((user) => user.id == review.userId); + } catch(e) { + return null; + } } @override @@ -27,14 +31,18 @@ class _ReviewListState extends State { 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; - debugPrint('response: ' + response); - - users = (jsonDecode(response) as List).map((user) => models.User.fromJson(user)).toList(); + setState(() { + _users = (jsonDecode(response) as List).map((user) => models.User.fromJson(user)).toList(); + }); } @override @@ -47,56 +55,59 @@ class _ReviewListState extends State { selectedIndex: -1, body: Scaffold( backgroundColor: const Color(0xFFF9F9F9), - body: SingleChildScrollView(child: Container( - decoration: const BoxDecoration(color: Color(0xFFF9F9F9)), - width: MediaQuery.of(context).size.width, - padding: const EdgeInsets.all(20.0), - child: Column(children: - reviews.map((review) => Container( - width: double.infinity, - padding: const EdgeInsets.all(20), - margin: const EdgeInsets.only(bottom: 10), - decoration: const BoxDecoration( - boxShadow: [ - BoxShadow( - color: Color(0x20000000), - offset: Offset(0,1), - blurRadius: 4, + 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)), + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.all(20.0), + child: Column(children: [ + for (final review in reviews) + Container( + width: double.infinity, + padding: const EdgeInsets.all(20), + margin: const EdgeInsets.only(bottom: 10), + decoration: const BoxDecoration( + boxShadow: [ + BoxShadow( + color: Color(0x20000000), + offset: Offset(0,1), + blurRadius: 4, + ), + ], + color: Colors.white, ), - ], - color: Colors.white, - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Padding( - padding: EdgeInsets.only(top: 3), - child: Icon(Icons.rate_review, color: Colors.purple, size: 36), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Padding( + padding: EdgeInsets.only(top: 3), + child: Icon(Icons.rate_review, color: Colors.purple, size: 36), + ), + const SizedBox(width: 20), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(review.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), + Text(review.content), + const SizedBox(height: 10), + if (review.image != null) Image.network(review.image!.imageUrl, height: 200), + if (review.image != null) const SizedBox(height: 15), + Row(children: [ + 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), + ]), + const SizedBox(height: 10), + Text('Submitted by ' + (_getReviewUser(review)?.username ?? ''), style: const TextStyle(color: Colors.grey, fontSize: 12)), + ], + ), + ), + ], ), - const SizedBox(width: 20), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(review.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24)), - Text(review.content), - const SizedBox(height: 10), - if (review.image != null) Image.network(review.image!.imageUrl, height: 200), - if (review.image != null) const SizedBox(height: 15), - Row(children: [ - 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), - ]), - const SizedBox(height: 10), - Text('Submitted by ' + (_getReviewUser(review)?.username ?? ''), style: const TextStyle(color: Colors.grey, fontSize: 12)), - ], - ), - ), - ], - ), - )).toList(), - ), - )), + ), + ]), + )), floatingActionButton: FloatingActionButton( onPressed: () async { if (!await api.isLoggedIn(context)) { From 0ab5fee8b2152066d01e3ceabc03e43db62fba6b Mon Sep 17 00:00:00 2001 From: Reimar Date: Fri, 13 Sep 2024 12:06:45 +0200 Subject: [PATCH 5/6] Show image immediately after submitting review --- rust-backend/src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust-backend/src/main.rs b/rust-backend/src/main.rs index 004ddbe..ba3e442 100644 --- a/rust-backend/src/main.rs +++ b/rust-backend/src/main.rs @@ -217,9 +217,11 @@ async fn create_review(auth: AuthorizedUser, data: web::Data, input: we place_description: input.place_description.clone(), title: input.title.clone(), content: input.content.clone(), - rating: input.rating.clone(), + rating: input.rating.clone(), 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(), } From 68c806408312ba47e03c22e137c9c319c9004463 Mon Sep 17 00:00:00 2001 From: Reimar Date: Fri, 13 Sep 2024 12:12:58 +0200 Subject: [PATCH 6/6] Show profile picture on reviews --- Mobile/lib/reviewlist.dart | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Mobile/lib/reviewlist.dart b/Mobile/lib/reviewlist.dart index 13f5fd0..edbd566 100644 --- a/Mobile/lib/reviewlist.dart +++ b/Mobile/lib/reviewlist.dart @@ -80,9 +80,23 @@ class _ReviewListState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Padding( - padding: EdgeInsets.only(top: 3), - child: Icon(Icons.rate_review, color: Colors.purple, size: 36), + Padding( + padding: const EdgeInsets.only(top: 3), + 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), Expanded(