diff --git a/src/Application/AzureBlobStorage/Constants/CommonFileNames.cs b/src/Application/AzureBlobStorage/Constants/CommonFileNames.cs index 7eabb9a..b6d44aa 100644 --- a/src/Application/AzureBlobStorage/Constants/CommonFileNames.cs +++ b/src/Application/AzureBlobStorage/Constants/CommonFileNames.cs @@ -3,4 +3,6 @@ public static class CommonFileNames { public static string ProfilePicture = "profilePicture"; + public static string BannerPicture = "bannerPicture"; + public static string WebsiteIcon = "websiteIcon"; } diff --git a/src/Application/Common/Interfaces/IIdentityService.cs b/src/Application/Common/Interfaces/IIdentityService.cs index d92663e..74e9240 100644 --- a/src/Application/Common/Interfaces/IIdentityService.cs +++ b/src/Application/Common/Interfaces/IIdentityService.cs @@ -9,10 +9,10 @@ public interface IIdentityService Task> CreateUserAsync(Userinfo userInfo); Task> CreateUserAsync(string email, string userName, string firstName, string lastName, string password); Task GetCurrentUserAsync(); - Task> UpdateCurrentUserAsync(string id, string firstName, string lastName, string occupation, - string phoneNumber, string birthDate, string country, string city, string address, string about, - string description, - SocialNetworksModel socialNetworks); + Task UpdateCurrentUserBannerPictureUrlAsync(string url); + Task UpdateCurrentUserProfilePictureUrlAsync(string url); + Task UpdateCurrentUserWebsiteIconUrlAsync(string url); + Task> UpdateCurrentUserAsync(UserModel userModel); Task> GetCurrentUserRolesAsync(); Task FindUserByIdAsync(string id); Task FindUserByEmailAsync(string email); diff --git a/src/Application/Common/Models/Result.cs b/src/Application/Common/Models/Result.cs index 9552531..294e2ed 100644 --- a/src/Application/Common/Models/Result.cs +++ b/src/Application/Common/Models/Result.cs @@ -19,25 +19,26 @@ public class Result( } public class Result( + T? value, bool succeeded, IEnumerable errors) { public bool Succeeded { get; init; } = succeeded; public string[] Errors { get; init; } = errors.ToArray(); - public T? Value { get; set; } + public T? Value { get; set; } = value; public T GetValueOrDefault() { return Value ?? default(T)!; } - public static Result Success() + public static Result Success(T value) { - return new Result(true, Array.Empty()); + return new Result(value, true, Array.Empty()); } - public static Result Failure(IEnumerable errors) + public static Result Failure(T value, IEnumerable errors) { - return new Result(false, errors); + return new Result(value, false, errors); } } diff --git a/src/Application/Common/Models/UserModel.cs b/src/Application/Common/Models/UserModel.cs index ac09db9..9fceab9 100644 --- a/src/Application/Common/Models/UserModel.cs +++ b/src/Application/Common/Models/UserModel.cs @@ -2,22 +2,22 @@ using Hutopy.Application.Users.Models; namespace Hutopy.Application.Common.Models; -// TODO: Review nullable affectation here public class UserModel { - public string? Id { get; set; } - public string? UserName { get; set; } - public string? FirstName { get; set; } - public string? LastName { get; set; } - public string? Occupation { get; set; } - public string? Email { get; init; } = String.Empty; - public string? Phone { get; init; } = String.Empty; - public string? BirthDate { get; init; } = String.Empty; - public string? Country { get; init; } = String.Empty; - public string? City { get; init; } = String.Empty; - public string? Address { get; init; } = String.Empty; - public string? About { get; init; } = String.Empty; - public string? Description { get; init; } = String.Empty; + public string Id { get; set; } = string.Empty; + public string UserName { get; set; } = string.Empty; + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string Occupation { get; set; } = string.Empty; + public string Email { get; init; } = string.Empty; + public string Phone { get; init; } = string.Empty; + public string BirthDate { get; init; } = string.Empty; + public string Country { get; init; } = string.Empty; + public string City { get; init; } = string.Empty; + public string Address { get; init; } = string.Empty; + public string About { get; init; } = string.Empty; + public string Description { get; init; } = string.Empty; public SocialNetworksModel SocialNetworks { get; init; } = new(); - public string? PortraitUrl { get; set; } + public ProfileColorsModel ProfileColors { get; init; } = new(); + public string ProfilePictureUrl { get; set; } = string.Empty; } diff --git a/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs b/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs index 0ddd17e..0c74658 100644 --- a/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs +++ b/src/Application/Stripe/Commands/ConfirmStripeTransaction.cs @@ -17,29 +17,29 @@ public class Data public class Object { - public string Id { get; set; } = String.Empty; + public string Id { get; set; } = string.Empty; public int Amount { get; set; } public BillingDetails Billing_details { get; set; } = new(); - public string Calculated_statement_descriptor { get; set; } = String.Empty; - public string Currency { get; set; } = String.Empty; + public string Calculated_statement_descriptor { get; set; } = string.Empty; + public string Currency { get; set; } = string.Empty; public bool Paid { get; set; } - public string Payment_intent { get; set; } = String.Empty; - public string Payment_method { get; set; } = String.Empty; - public string Receipt_url { get; set; } = String.Empty; - public string Status { get; set; } = String.Empty; - public string Failure_message { get; set; } = String.Empty; + public string Payment_intent { get; set; } = string.Empty; + public string Payment_method { get; set; } = string.Empty; + public string Receipt_url { get; set; } = string.Empty; + public string Status { get; set; } = string.Empty; + public string Failure_message { get; set; } = string.Empty; } public class BillingDetails { - public string Email { get; set; } = String.Empty; - public string Name { get; set; } = String.Empty; - public string Phone { get; set; } = String.Empty; + public string Email { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public string Phone { get; set; } = string.Empty; } public class Request { - public string Id { get; set; } = String.Empty; + public string Id { get; set; } = string.Empty; } public class ConfirmStripeTransactionCommandHandler( diff --git a/src/Application/Users/Commands/UpdateCurrentUserCommand.cs b/src/Application/Users/Commands/UpdateCurrentUserCommand.cs index 3a23915..708ea6b 100644 --- a/src/Application/Users/Commands/UpdateCurrentUserCommand.cs +++ b/src/Application/Users/Commands/UpdateCurrentUserCommand.cs @@ -16,6 +16,7 @@ public class UpdateCurrentUserCommand : IRequest public required string About { get; init; } public required string Description { get; init; } public required SocialNetworksModel SocialNetworks { get; init; } + public required ProfileColorsModel ProfileColors { get; init; } } public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIdentityService identityService) : @@ -27,10 +28,7 @@ public class UpdateCurrentUserCommandHandler(IApplicationDbContext context, IIde if (identityUser?.Id is null) return string.Empty; - var result = await identityService.UpdateCurrentUserAsync(identityUser.Id, request.FirstName, request.LastName, - request.Occupation, request.PhoneNumber, request.BirthDate, - request.Country, request.City, request.Address, request.About, - request.Description, request.SocialNetworks); + var result = await identityService.UpdateCurrentUserAsync(identityUser); await context.SaveChangesAsync(cancellationToken); diff --git a/src/Application/Users/Commands/UploadBannerPicture.cs b/src/Application/Users/Commands/UploadBannerPicture.cs new file mode 100644 index 0000000..66922a6 --- /dev/null +++ b/src/Application/Users/Commands/UploadBannerPicture.cs @@ -0,0 +1,27 @@ +using Hutopy.Application.AzureBlobStorage.Constants; +using Hutopy.Application.Common.Interfaces; + +namespace Hutopy.Application.Users.Commands; + +public class UploadBannerPictureCommand : IRequest +{ + public required Stream BannerPicture { get; init; } +} + +public class UploadBannerPictureCommandHandler(IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler +{ + public async Task Handle(UploadBannerPictureCommand request, CancellationToken cancellationToken) + { + var identityUser = await identityService.GetCurrentUserAsync(); + var currentUserId = new Guid(identityUser?.Id ?? "").ToString(); + + var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.BannerPicture}"; + + var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.BannerPicture); + + await identityService.UpdateCurrentUserBannerPictureUrlAsync(url); + + return url; + } +} + diff --git a/src/Application/Users/Commands/UploadProfilePicture.cs b/src/Application/Users/Commands/UploadProfilePicture.cs index 08746d9..6fc4714 100644 --- a/src/Application/Users/Commands/UploadProfilePicture.cs +++ b/src/Application/Users/Commands/UploadProfilePicture.cs @@ -19,6 +19,8 @@ public class UploadProfilePictureCommandHandler(IIdentityService identityService var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.ProfilePicture); + await identityService.UpdateCurrentUserProfilePictureUrlAsync(url); + return url; } } diff --git a/src/Application/Users/Commands/UploadWebsiteIcon.cs b/src/Application/Users/Commands/UploadWebsiteIcon.cs new file mode 100644 index 0000000..78a83a2 --- /dev/null +++ b/src/Application/Users/Commands/UploadWebsiteIcon.cs @@ -0,0 +1,27 @@ +using Hutopy.Application.AzureBlobStorage.Constants; +using Hutopy.Application.Common.Interfaces; + +namespace Hutopy.Application.Users.Commands; + +public class UploadWebsiteIconCommand : IRequest +{ + public required Stream WebsiteIcon { get; init; } +} + +public class UploadWebsiteIconCommandHandler(IIdentityService identityService, IAzureBlobStorageService azureBlobStorageService) : IRequestHandler +{ + public async Task Handle(UploadWebsiteIconCommand request, CancellationToken cancellationToken) + { + var identityUser = await identityService.GetCurrentUserAsync(); + var currentUserId = new Guid(identityUser?.Id ?? "").ToString(); + + var blobName = $"{currentUserId}/{SubDirectoryNames.Profile}/{CommonFileNames.WebsiteIcon}"; + + var url = await azureBlobStorageService.UploadFileAsync(ContainerNames.Users, blobName, request.WebsiteIcon); + + await identityService.UpdateCurrentUserWebsiteIconUrlAsync(url); + + return url; + } +} + diff --git a/src/Application/Users/Models/ProfileColorsModel.cs b/src/Application/Users/Models/ProfileColorsModel.cs new file mode 100644 index 0000000..04baadb --- /dev/null +++ b/src/Application/Users/Models/ProfileColorsModel.cs @@ -0,0 +1,9 @@ +namespace Hutopy.Application.Users.Models; + +public class ProfileColorsModel +{ + public string BannerTop { get; init; } = String.Empty; + public string BannerBottom { get; init; } = String.Empty; + public string Accent { get; init; } = String.Empty; + public string Menu { get; init; } = String.Empty; +} diff --git a/src/Application/Users/Models/SocialNetworksModel.cs b/src/Application/Users/Models/SocialNetworksModel.cs index edd3cce..19a2f02 100644 --- a/src/Application/Users/Models/SocialNetworksModel.cs +++ b/src/Application/Users/Models/SocialNetworksModel.cs @@ -2,12 +2,12 @@ public class SocialNetworksModel { - public string FacebookUrl { get; init; } = String.Empty; - public string InstagramUrl { get; init; } = String.Empty; - public string XUrl { get; init; } = String.Empty; - public string LinkedInUrl { get; init; } = String.Empty; - public string TikTokUrl { get; init; } = String.Empty; - public string YoutubeUrl { get; init; } = String.Empty; - public string RedditUrl { get; init; } = String.Empty; - public string YourWebsiteUrl { get; init; } = String.Empty; + public string FacebookUrl { get; init; } = string.Empty; + public string InstagramUrl { get; init; } = string.Empty; + public string XUrl { get; init; } = string.Empty; + public string LinkedInUrl { get; init; } = string.Empty; + public string TikTokUrl { get; init; } = string.Empty; + public string YoutubeUrl { get; init; } = string.Empty; + public string RedditUrl { get; init; } = string.Empty; + public string YourWebsiteUrl { get; init; } = string.Empty; } diff --git a/src/Application/Users/Queries/GetCurrentUser/UserDto.cs b/src/Application/Users/Queries/GetCurrentUser/UserDto.cs index c0b34c1..ec2cc5d 100644 --- a/src/Application/Users/Queries/GetCurrentUser/UserDto.cs +++ b/src/Application/Users/Queries/GetCurrentUser/UserDto.cs @@ -7,16 +7,16 @@ public class UserDto public Guid Id { get; init; } public required string FirstName { get; init; } public required string LastName { get; init; } - public string UserName { get; init; } = String.Empty; - public string Occupation { get; init; } = String.Empty; - public string Email { get; init; } = String.Empty; - public string Phone { get; init; } = String.Empty; - public string BirthDate { get; init; } = String.Empty; - public string Country { get; init; } = String.Empty; - public string City { get; init; } = String.Empty; - public string Address { get; init; } = String.Empty; - public string About { get; init; } = String.Empty; - public string Description { get; init; } = String.Empty; + public string UserName { get; init; } = string.Empty; + public string Occupation { get; init; } = string.Empty; + public string Email { get; init; } = string.Empty; + public string Phone { get; init; } = string.Empty; + public string BirthDate { get; init; } = string.Empty; + public string Country { get; init; } = string.Empty; + public string City { get; init; } = string.Empty; + public string Address { get; init; } = string.Empty; + public string About { get; init; } = string.Empty; + public string Description { get; init; } = string.Empty; public SocialNetworksModel SocialNetworks { get; init; } = new(); public List UserTransactions { get; init; } = []; public IList UserRoles { get; init; } = []; diff --git a/src/Infrastructure/AzureBlob/AzureBlobStorageService.cs b/src/Infrastructure/AzureBlob/AzureBlobStorageService.cs index f54f96b..b25521c 100644 --- a/src/Infrastructure/AzureBlob/AzureBlobStorageService.cs +++ b/src/Infrastructure/AzureBlob/AzureBlobStorageService.cs @@ -1,3 +1,6 @@ +using System; +using System.IO; +using System.Threading.Tasks; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Hutopy.Application.Common.Interfaces; diff --git a/src/Infrastructure/Data/ApplicationDbContextInitializer.cs b/src/Infrastructure/Data/ApplicationDbContextInitializer.cs index 6a985de..b771d57 100644 --- a/src/Infrastructure/Data/ApplicationDbContextInitializer.cs +++ b/src/Infrastructure/Data/ApplicationDbContextInitializer.cs @@ -1,4 +1,7 @@ -using Hutopy.Domain.Constants; +using System; +using System.Linq; +using System.Threading.Tasks; +using Hutopy.Domain.Constants; using Hutopy.Infrastructure.Identity; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Identity; diff --git a/src/Infrastructure/Data/Configurations/ApplicationUserConfiguration.cs b/src/Infrastructure/Data/Configurations/ApplicationUserConfiguration.cs index 0624b7a..b3a3735 100644 --- a/src/Infrastructure/Data/Configurations/ApplicationUserConfiguration.cs +++ b/src/Infrastructure/Data/Configurations/ApplicationUserConfiguration.cs @@ -12,5 +12,15 @@ public class ApplicationUserConfiguration : IEntityTypeConfiguration u.SocialNetworks) .ToTable($"{nameof(ApplicationUser)}_SocialNetworks"); + + // Relationship between ApplicationUser and ProfileColors + builder + .OwnsOne(u => u.ProfileColors) + .ToTable($"{nameof(ApplicationUser)}_ProfileColors"); + + // Relationship between ApplicationUser and StoredDataUrls + builder + .OwnsOne(u => u.StoredDataUrls) + .ToTable($"{nameof(ApplicationUser)}_StoredDataUrls"); } } diff --git a/src/Infrastructure/DependencyInjection.cs b/src/Infrastructure/DependencyInjection.cs index b8f43d6..2dfccb2 100644 --- a/src/Infrastructure/DependencyInjection.cs +++ b/src/Infrastructure/DependencyInjection.cs @@ -1,4 +1,5 @@ -using Hutopy.Application.Common.Interfaces; +using System; +using Hutopy.Application.Common.Interfaces; using Hutopy.Domain.Constants; using Hutopy.Infrastructure.AzureBlob; using Hutopy.Infrastructure.Data; diff --git a/src/Infrastructure/Identity/ApplicationUser.cs b/src/Infrastructure/Identity/ApplicationUser.cs index d956a9b..fbf49f7 100644 --- a/src/Infrastructure/Identity/ApplicationUser.cs +++ b/src/Infrastructure/Identity/ApplicationUser.cs @@ -15,4 +15,6 @@ public class ApplicationUser : IdentityUser public string About { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public SocialNetworks SocialNetworks { get; set; } = new(); + public ProfileColors ProfileColors { get; set; } = new(); + public StoredDataUrls StoredDataUrls { get; set; } = new(); } diff --git a/src/Infrastructure/Identity/IdentityService.cs b/src/Infrastructure/Identity/IdentityService.cs index d512ae5..c0f8a9b 100644 --- a/src/Infrastructure/Identity/IdentityService.cs +++ b/src/Infrastructure/Identity/IdentityService.cs @@ -1,6 +1,8 @@ -using System.Diagnostics; +using System; +using System.Collections.Generic; using Google.Apis.Oauth2.v2.Data; using System.Security.Claims; +using System.Threading.Tasks; using Hutopy.Application.Common.Interfaces; using Hutopy.Application.Common.Models; using Hutopy.Application.Users.Models; @@ -64,10 +66,8 @@ public class IdentityService( var applicationResult = identityResult.ToApplicationResult(); - var result = new Result(applicationResult.Succeeded, applicationResult.Errors); + var result = new Result(applicationUser.Id, applicationResult.Succeeded, applicationResult.Errors); - result.Value = applicationUser.Id; - return result; } @@ -83,51 +83,53 @@ public class IdentityService( var response = await userManager.CreateAsync(applicationUser, password); - var result = new Result(response.Succeeded, response.ToApplicationResult().Errors); - result.Value = applicationUser.Id; + var result = new Result(applicationUser.Id, response.Succeeded, response.ToApplicationResult().Errors); return result; } - public async Task> UpdateCurrentUserAsync(string id, string firstName, string lastName, string occupation, - string phoneNumber, string birthDate, string country, string city, string address, string about, string description, - SocialNetworksModel socialNetworks) + public async Task> UpdateCurrentUserAsync(UserModel userModel) { - var applicationUser = await userManager.FindByIdAsync(id); + var applicationUser = await userManager.FindByIdAsync(userModel.Id); - if (applicationUser is null) return Result.Failure(new[] { "User not found." }); + if (applicationUser is null) return Result.Failure("", new[] { "User not found." }); - applicationUser.FirstName = firstName; - applicationUser.LastName = lastName; - applicationUser.Occupation = occupation; - applicationUser.PhoneNumber = phoneNumber; - applicationUser.BirthDate = birthDate; - applicationUser.Country = country; - applicationUser.City = city; - applicationUser.Address = address; - applicationUser.About = about; - applicationUser.Description = description; - applicationUser.SocialNetworks = new SocialNetworks() + applicationUser.FirstName = userModel.FirstName; + applicationUser.LastName = userModel.LastName; + applicationUser.Occupation = userModel.Occupation; + applicationUser.PhoneNumber = userModel.Phone; + applicationUser.BirthDate = userModel.BirthDate; + applicationUser.Country = userModel.Country; + applicationUser.City = userModel.City; + applicationUser.Address = userModel.Address; + applicationUser.About = userModel.About; + applicationUser.Description = userModel.Description; + applicationUser.SocialNetworks = new SocialNetworks { - FacebookUrl = socialNetworks.FacebookUrl, - InstagramUrl = socialNetworks.InstagramUrl, - XUrl = socialNetworks.XUrl, - LinkedInUrl = socialNetworks.LinkedInUrl, - TikTokUrl = socialNetworks.TikTokUrl, - YoutubeUrl = socialNetworks.YoutubeUrl, - RedditUrl = socialNetworks.RedditUrl, - YourWebsiteUrl = socialNetworks.YourWebsiteUrl + FacebookUrl = userModel.SocialNetworks.FacebookUrl, + InstagramUrl = userModel.SocialNetworks.InstagramUrl, + XUrl = userModel.SocialNetworks.XUrl, + LinkedInUrl = userModel.SocialNetworks.LinkedInUrl, + TikTokUrl = userModel.SocialNetworks.TikTokUrl, + YoutubeUrl = userModel.SocialNetworks.YoutubeUrl, + RedditUrl = userModel.SocialNetworks.RedditUrl, + YourWebsiteUrl = userModel.SocialNetworks.YourWebsiteUrl + }; + applicationUser.ProfileColors = new ProfileColors + { + BannerTop = userModel.ProfileColors.BannerTop, + BannerBottom = userModel.ProfileColors.BannerBottom, + Accent = userModel.ProfileColors.Accent, + Menu = userModel.ProfileColors.Menu }; var response = await userManager.UpdateAsync(applicationUser); var applicationResult = response.ToApplicationResult(); - var result = new Result(applicationResult.Succeeded, + var result = new Result(userModel.Id, applicationResult.Succeeded, applicationResult.Errors); - - result.Value = id; - + return result; } @@ -140,12 +142,12 @@ public class IdentityService( var userModel = new UserModel { Id = response.Id, - UserName = response.UserName, + UserName = response.UserName ?? string.Empty, FirstName = response.FirstName, LastName = response.LastName, - Email = response.Email, + Email = response.Email ?? string.Empty, Occupation = response.Occupation, - Phone = response.PhoneNumber, + Phone = response.PhoneNumber ?? string.Empty, BirthDate = response.BirthDate, Country = response.Country, City = response.City, @@ -162,6 +164,57 @@ public class IdentityService( YoutubeUrl = response.SocialNetworks.YoutubeUrl, RedditUrl = response.SocialNetworks.RedditUrl, YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, + }, + ProfileColors = new ProfileColorsModel + { + BannerTop = response.ProfileColors.BannerTop, + BannerBottom = response.ProfileColors.BannerBottom, + Accent = response.ProfileColors.Accent, + Menu = response.ProfileColors.Menu + } + }; + + return userModel; + } + + public async Task FindUserByEmailAsync(string email) + { + var response = await userManager.FindByEmailAsync(email); + + if (response == null) return null; + + var userModel = new UserModel + { + Id = response.Id, + UserName = response.UserName ?? string.Empty, + FirstName = response.FirstName, + LastName = response.LastName, + Email = response.Email ?? string.Empty, + Occupation = response.Occupation, + Phone = response.PhoneNumber ?? string.Empty, + BirthDate = response.BirthDate, + Country = response.Country, + City = response.City, + Address = response.Address, + About = response.About, + Description = response.Description, + SocialNetworks = new SocialNetworksModel + { + FacebookUrl = response.SocialNetworks.FacebookUrl, + InstagramUrl = response.SocialNetworks.InstagramUrl, + XUrl = response.SocialNetworks.XUrl, + LinkedInUrl = response.SocialNetworks.LinkedInUrl, + TikTokUrl = response.SocialNetworks.TikTokUrl, + YoutubeUrl = response.SocialNetworks.YoutubeUrl, + RedditUrl = response.SocialNetworks.RedditUrl, + YourWebsiteUrl = response.SocialNetworks.YourWebsiteUrl, + }, + ProfileColors = new ProfileColorsModel + { + BannerTop = response.ProfileColors.BannerTop, + BannerBottom = response.ProfileColors.BannerBottom, + Accent = response.ProfileColors.Accent, + Menu = response.ProfileColors.Menu } }; @@ -179,22 +232,49 @@ public class IdentityService( return await FindUserByIdAsync(currentUserId); } - public async Task FindUserByEmailAsync(string email) + public async Task UpdateCurrentUserBannerPictureUrlAsync(string url) { - var response = await userManager.FindByEmailAsync(email); + var userModel = await GetCurrentUserAsync(); + if (userModel is null) return Result.Failure(new[] { "User not found." }); - if (response == null) return null; + var applicationUser = await userManager.FindByIdAsync(userModel.Id); + if (applicationUser is null) return Result.Failure(new[] { "ApplicationUser not found." }); - var userModel = new UserModel - { - Id = response.Id, - UserName = response.UserName, - FirstName = response.FirstName, - LastName = response.LastName, - Email = response.Email - }; + applicationUser.StoredDataUrls.BannerPictureUrl = url; + + var response = await userManager.UpdateAsync(applicationUser); - return userModel; + return response.ToApplicationResult(); + } + + public async Task UpdateCurrentUserProfilePictureUrlAsync(string url) + { + var userModel = await GetCurrentUserAsync(); + if (userModel is null) return Result.Failure(new[] { "User not found." }); + + var applicationUser = await userManager.FindByIdAsync(userModel.Id); + if (applicationUser is null) return Result.Failure(new[] { "ApplicationUser not found." }); + + applicationUser.StoredDataUrls.ProfilePictureUrl = url; + + var response = await userManager.UpdateAsync(applicationUser); + + return response.ToApplicationResult(); + } + + public async Task UpdateCurrentUserWebsiteIconUrlAsync(string url) + { + var userModel = await GetCurrentUserAsync(); + if (userModel is null) return Result.Failure(new[] { "User not found." }); + + var applicationUser = await userManager.FindByIdAsync(userModel.Id); + if (applicationUser is null) return Result.Failure(new[] { "ApplicationUser not found." }); + + applicationUser.StoredDataUrls.WebsiteIconUrl = url; + + var response = await userManager.UpdateAsync(applicationUser); + + return response.ToApplicationResult(); } public async Task IsInRoleAsync(string userId, string role) @@ -293,7 +373,7 @@ public class IdentityService( email: user.Email, firstname: user.FirstName, lastname: user.LastName, - portraitUrl: user.PortraitUrl); + portraitUrl: user.ProfilePictureUrl); return token; } diff --git a/src/Infrastructure/Identity/OwnedEntities/ProfileColors.cs b/src/Infrastructure/Identity/OwnedEntities/ProfileColors.cs new file mode 100644 index 0000000..3783044 --- /dev/null +++ b/src/Infrastructure/Identity/OwnedEntities/ProfileColors.cs @@ -0,0 +1,9 @@ +namespace Hutopy.Infrastructure.Identity.OwnedEntities; + +public class ProfileColors +{ + public string BannerTop { get; init; } = string.Empty; + public string BannerBottom { get; init; } = string.Empty; + public string Accent { get; init; } = string.Empty; + public string Menu { get; init; } = string.Empty; +} diff --git a/src/Infrastructure/Identity/OwnedEntities/SocialNetworks.cs b/src/Infrastructure/Identity/OwnedEntities/SocialNetworks.cs index 1e0d30e..a03e139 100644 --- a/src/Infrastructure/Identity/OwnedEntities/SocialNetworks.cs +++ b/src/Infrastructure/Identity/OwnedEntities/SocialNetworks.cs @@ -2,12 +2,12 @@ namespace Hutopy.Infrastructure.Identity.OwnedEntities; public class SocialNetworks { - public string FacebookUrl { get; init; } = String.Empty; - public string InstagramUrl { get; init; } = String.Empty; - public string XUrl { get; init; } = String.Empty; - public string LinkedInUrl { get; init; } = String.Empty; - public string TikTokUrl { get; init; } = String.Empty; - public string YoutubeUrl { get; init; } = String.Empty; - public string RedditUrl { get; init; } = String.Empty; - public string YourWebsiteUrl { get; init; } = String.Empty; + public string FacebookUrl { get; init; } = string.Empty; + public string InstagramUrl { get; init; } = string.Empty; + public string XUrl { get; init; } = string.Empty; + public string LinkedInUrl { get; init; } = string.Empty; + public string TikTokUrl { get; init; } = string.Empty; + public string YoutubeUrl { get; init; } = string.Empty; + public string RedditUrl { get; init; } = string.Empty; + public string YourWebsiteUrl { get; init; } = string.Empty; } diff --git a/src/Infrastructure/Identity/OwnedEntities/StoredDataUrls.cs b/src/Infrastructure/Identity/OwnedEntities/StoredDataUrls.cs new file mode 100644 index 0000000..bea6099 --- /dev/null +++ b/src/Infrastructure/Identity/OwnedEntities/StoredDataUrls.cs @@ -0,0 +1,8 @@ +namespace Hutopy.Infrastructure.Identity.OwnedEntities; + +public class StoredDataUrls +{ + public string BannerPictureUrl { get; set; } = string.Empty; + public string ProfilePictureUrl { get; set; } = string.Empty; + public string WebsiteIconUrl { get; set; } = string.Empty; +} diff --git a/src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.Designer.cs b/src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.Designer.cs new file mode 100644 index 0000000..af10f04 --- /dev/null +++ b/src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.Designer.cs @@ -0,0 +1,557 @@ +// +using System; +using Hutopy.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Hutopy.Infrastructure.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240630163057_AddMoreInformationsToUser")] + partial class AddMoreInformationsToUser + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Hutopy.Domain.Entities.FutureCreator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("EmailAddress") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReasonToJoin") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("SocialNetworkAccount") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("FutureCreators"); + }); + + modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("decimal(18,2)"); + + b.Property("ApplicationUserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Created") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Currency") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsConfirmed") + .HasColumnType("bit"); + + b.Property("LastModified") + .HasColumnType("datetimeoffset"); + + b.Property("LastModifiedBy") + .HasColumnType("nvarchar(max)"); + + b.Property("Paid") + .HasColumnType("bit"); + + b.Property("StripeBillingDetailEmail") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeBillingDetailName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeChargeId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeEventId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentIntent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripePaymentMethod") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("StripeReceiptUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("TipMessage") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationUserId"); + + b.ToTable("UserTransactions"); + }); + + modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("About") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Address") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("BirthDate") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("City") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Country") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("Occupation") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Hutopy.Domain.Entities.UserTransaction", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("ApplicationUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b => + { + b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.ProfileColors", "ProfileColors", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("Accent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerBottom") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerTop") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("Menu") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_ProfileColors", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.SocialNetworks", "SocialNetworks", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("FacebookUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("InstagramUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("LinkedInUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("RedditUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("TikTokUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("XUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("YourWebsiteUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("YoutubeUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_SocialNetworks", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.StoredDataUrls", "StoredDataUrls", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("BannerPictureUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("ProfilePictureUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("WebsiteIconUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_StoredDataUrls", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.Navigation("ProfileColors") + .IsRequired(); + + b.Navigation("SocialNetworks") + .IsRequired(); + + b.Navigation("StoredDataUrls") + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Hutopy.Infrastructure.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.cs b/src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.cs new file mode 100644 index 0000000..85e7b7e --- /dev/null +++ b/src/Infrastructure/Migrations/20240630163057_AddMoreInformationsToUser.cs @@ -0,0 +1,77 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Hutopy.Infrastructure.Migrations +{ + /// + public partial class AddMoreInformationsToUser : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ApplicationUser_ProfileColors", + columns: table => new + { + ApplicationUserId = table.Column(type: "nvarchar(450)", nullable: false), + BannerTop = table.Column(type: "nvarchar(max)", nullable: false), + BannerBottom = table.Column(type: "nvarchar(max)", nullable: false), + Accent = table.Column(type: "nvarchar(max)", nullable: false), + Menu = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ApplicationUser_ProfileColors", x => x.ApplicationUserId); + table.ForeignKey( + name: "FK_ApplicationUser_ProfileColors_AspNetUsers_ApplicationUserId", + column: x => x.ApplicationUserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.Sql(@" + INSERT INTO ApplicationUser_ProfileColors (ApplicationUserId, BannerTop, BannerBottom, Accent, Menu) + SELECT Id, '', '', '', '' + FROM AspNetUsers + "); + + migrationBuilder.CreateTable( + name: "ApplicationUser_StoredDataUrls", + columns: table => new + { + ApplicationUserId = table.Column(type: "nvarchar(450)", nullable: false), + BannerPictureUrl = table.Column(type: "nvarchar(max)", nullable: false), + ProfilePictureUrl = table.Column(type: "nvarchar(max)", nullable: false), + WebsiteIconUrl = table.Column(type: "nvarchar(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ApplicationUser_StoredDataUrls", x => x.ApplicationUserId); + table.ForeignKey( + name: "FK_ApplicationUser_StoredDataUrls_AspNetUsers_ApplicationUserId", + column: x => x.ApplicationUserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.Sql(@" + INSERT INTO ApplicationUser_StoredDataUrls (ApplicationUserId, BannerPictureUrl, ProfilePictureUrl, WebsiteIconUrl) + SELECT Id, '', '', '' + FROM AspNetUsers + "); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ApplicationUser_ProfileColors"); + + migrationBuilder.DropTable( + name: "ApplicationUser_StoredDataUrls"); + } + } +} diff --git a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index cd78f54..12a98ec 100644 --- a/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -389,6 +389,35 @@ namespace Hutopy.Infrastructure.Migrations modelBuilder.Entity("Hutopy.Infrastructure.Identity.ApplicationUser", b => { + b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.ProfileColors", "ProfileColors", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("Accent") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerBottom") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("BannerTop") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("Menu") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_ProfileColors", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.SocialNetworks", "SocialNetworks", b1 => { b1.Property("ApplicationUserId") @@ -434,8 +463,39 @@ namespace Hutopy.Infrastructure.Migrations .HasForeignKey("ApplicationUserId"); }); + b.OwnsOne("Hutopy.Infrastructure.Identity.OwnedEntities.StoredDataUrls", "StoredDataUrls", b1 => + { + b1.Property("ApplicationUserId") + .HasColumnType("nvarchar(450)"); + + b1.Property("BannerPictureUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("ProfilePictureUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.Property("WebsiteIconUrl") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b1.HasKey("ApplicationUserId"); + + b1.ToTable("ApplicationUser_StoredDataUrls", (string)null); + + b1.WithOwner() + .HasForeignKey("ApplicationUserId"); + }); + + b.Navigation("ProfileColors") + .IsRequired(); + b.Navigation("SocialNetworks") .IsRequired(); + + b.Navigation("StoredDataUrls") + .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => diff --git a/src/Infrastructure/Stripe/StripeService.cs b/src/Infrastructure/Stripe/StripeService.cs index 7b78fff..3f4e366 100644 --- a/src/Infrastructure/Stripe/StripeService.cs +++ b/src/Infrastructure/Stripe/StripeService.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; using Stripe; using Stripe.Checkout; using Hutopy.Application.Common.Interfaces; diff --git a/src/Web/Controllers/GoogleController.cs b/src/Web/Controllers/GoogleController.cs index dfaa91e..8206069 100644 --- a/src/Web/Controllers/GoogleController.cs +++ b/src/Web/Controllers/GoogleController.cs @@ -71,7 +71,7 @@ public class GoogleController(IIdentityService identityService, IHttpClientFacto user.Email, user.FirstName, user.LastName, - user.PortraitUrl); + user.ProfilePictureUrl); return Ok(new { accessToken = token, email }); } diff --git a/src/Web/Endpoints/UpdateMyUser.cs b/src/Web/Endpoints/UpdateMyUser.cs index 60121be..9aca4dc 100644 --- a/src/Web/Endpoints/UpdateMyUser.cs +++ b/src/Web/Endpoints/UpdateMyUser.cs @@ -8,6 +8,9 @@ public class UpdateMyUser : EndpointGroupBase { app.MapGroup(this) .RequireAuthorization() + .MapPost(UpdateCurrentUserProfilePicture, "/profile-picture") + .MapPost(UpdateCurrentUserBannerPicture, "/banner-picture") + .MapPost(UpdateCurrentUserWebsiteIcon, "/website-icon") .MapPatch("/profile", UpdateCurrentUser); } @@ -16,5 +19,21 @@ public class UpdateMyUser : EndpointGroupBase return await sender.Send(command); } + private static async Task UpdateCurrentUserProfilePicture(ISender sender, Stream stream) + { + var command = new UploadProfilePictureCommand { ProfilePicture = stream }; + return await sender.Send(command); + } + private static async Task UpdateCurrentUserBannerPicture(ISender sender, Stream stream) + { + var command = new UploadBannerPictureCommand { BannerPicture = stream }; + return await sender.Send(command); + } + + private static async Task UpdateCurrentUserWebsiteIcon(ISender sender, Stream stream) + { + var command = new UploadWebsiteIconCommand { WebsiteIcon = stream }; + return await sender.Send(command); + } }