From e3a4b4fc1e14917abceb5d99931db235f6db2475 Mon Sep 17 00:00:00 2001 From: Dhiogo Acioli Date: Mon, 9 Sep 2024 11:58:16 +0700 Subject: [PATCH] geral revision --- MM.API/Core/IsolatedFunctionHelper.cs | 6 +- .../Middleware/ExceptionHandlingMiddleware.cs | 7 +- .../Middleware/FunctionContextExtensions.cs | 2 +- MM.API/Functions/CacheFunction.cs | 162 +++++++++++++++--- MM.API/Functions/LoginFunction.cs | 27 ++- MM.API/Functions/ProfileFunction.cs | 10 +- MM.API/Functions/SendgridFunction.cs | 2 +- MM.API/MM.API.csproj | 8 +- MM.API/Program.cs | 1 + MM.API/Repository/CosmosCacheRepository.cs | 25 ++- MM.API/Repository/CosmosEmailRepository.cs | 11 +- MM.API/Repository/CosmosProfileRepository.cs | 112 ++++++++++++ MM.API/Repository/CosmosRepository.cs | 143 ++++++++++------ MM.Shared/Core/CosmosDocument.cs | 2 +- MM.Shared/Core/Models/MainDocument.cs | 2 +- MM.Shared/Models/Auth/ClienteLogin.cs | 11 +- MM.Shared/Models/HereJson.cs | 15 +- MM.Shared/Models/Profile/ProfileModel.cs | 17 +- MM.WEB/Core/AppStateStatic.cs | 1 + MM.WEB/Core/CacheApi.cs | 20 ++- MM.WEB/MM.WEB.csproj | 20 +-- .../Components/FeedbackComponent.razor | 1 + MM.WEB/Modules/Auth/Core/LoginApi.cs | 17 +- MM.WEB/Modules/Auth/ProfilePopup.razor | 2 +- MM.WEB/Modules/Index.razor | 31 ++++ .../Profile/Components/ProfileHeader.razor | 120 +++++++++++++ MM.WEB/Modules/Profile/Profile.razor | 53 +++--- MM.WEB/Modules/Profile/ProfileData.razor | 33 ++-- MM.WEB/Modules/Profile/ProfileData.razor.cs | 26 +-- .../Modules/Profile/ProfilePreference.razor | 6 +- MM.WEB/Modules/RedirectExternalLink.razor | 17 ++ MM.WEB/Modules/Shared/Field/FieldSelect.razor | 5 +- .../Modules/Shared/Field/FieldSelect.razor.cs | 1 + .../Components/SubscriptionPopup.razor | 1 + .../Support/Component/DownloadComponent.razor | 4 +- MM.WEB/Resources/BuildDate.txt | 2 +- .../Resources/GlobalTranslations.Designer.cs | 27 +++ MM.WEB/Resources/GlobalTranslations.es.resx | 12 ++ MM.WEB/Resources/GlobalTranslations.pt.resx | 12 ++ MM.WEB/Resources/GlobalTranslations.resx | 9 + MM.WEB/Shared/MainLayout.razor | 8 +- MM.WEB/Shared/SettingsPopup.razor | 2 +- MM.WEB/wwwroot/logo/trustpilot.png | Bin 0 -> 1979 bytes 43 files changed, 773 insertions(+), 220 deletions(-) create mode 100644 MM.API/Repository/CosmosProfileRepository.cs create mode 100644 MM.WEB/Modules/Profile/Components/ProfileHeader.razor create mode 100644 MM.WEB/Modules/RedirectExternalLink.razor create mode 100644 MM.WEB/wwwroot/logo/trustpilot.png diff --git a/MM.API/Core/IsolatedFunctionHelper.cs b/MM.API/Core/IsolatedFunctionHelper.cs index cee19e4..282309e 100644 --- a/MM.API/Core/IsolatedFunctionHelper.cs +++ b/MM.API/Core/IsolatedFunctionHelper.cs @@ -10,7 +10,7 @@ namespace MM.API.Core { public static class IsolatedFunctionHelper { - public static async Task GetBody(this HttpRequestData req, CancellationToken cancellationToken) where T : MainDocument, new() + public static async Task GetBody(this HttpRequestData req, CancellationToken cancellationToken) where T : CosmosDocument, new() { var model = await JsonSerializer.DeserializeAsync(req.Body, cancellationToken: cancellationToken); @@ -35,6 +35,10 @@ public static class IsolatedFunctionHelper { priv.Initialize(userId); } + else if (model is CosmosDocument doc) //generic document + { + doc.SetIds(userId); + } return model; } diff --git a/MM.API/Core/Middleware/ExceptionHandlingMiddleware.cs b/MM.API/Core/Middleware/ExceptionHandlingMiddleware.cs index c08fd06..c70a3a3 100644 --- a/MM.API/Core/Middleware/ExceptionHandlingMiddleware.cs +++ b/MM.API/Core/Middleware/ExceptionHandlingMiddleware.cs @@ -1,4 +1,5 @@ -using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Cosmos; +using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Middleware; using Microsoft.Extensions.Logging; using System.Net; @@ -15,6 +16,10 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next { await next(context); } + catch (CosmosOperationCanceledException) + { + await context.SetHttpResponseStatusCode(HttpStatusCode.RequestTimeout, "Request Timeout!"); + } catch (Exception ex) { _logger.LogError(ex, "Error processing invocation"); diff --git a/MM.API/Core/Middleware/FunctionContextExtensions.cs b/MM.API/Core/Middleware/FunctionContextExtensions.cs index 3b1da32..6bd22b7 100644 --- a/MM.API/Core/Middleware/FunctionContextExtensions.cs +++ b/MM.API/Core/Middleware/FunctionContextExtensions.cs @@ -10,7 +10,7 @@ public static async Task SetHttpResponseStatusCode(this FunctionContext context, { var req = await context.GetHttpRequestDataAsync(); - var newHttpResponse = req.CreateResponse(statusCode); + var newHttpResponse = req!.CreateResponse(statusCode); // You need to explicitly pass the status code in WriteAsJsonAsync method. // https://github.com/Azure/azure-functions-dotnet-worker/issues/776 diff --git a/MM.API/Functions/CacheFunction.cs b/MM.API/Functions/CacheFunction.cs index a7a953d..503f1f3 100644 --- a/MM.API/Functions/CacheFunction.cs +++ b/MM.API/Functions/CacheFunction.cs @@ -28,7 +28,7 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // compactModels.Items.Add(new Shared.Models.News.Item(item.id, item.title, item.mainImage?.url, item.link)); // } - // var compactResult = await cacheRepo.CreateItemAsync(new FlixsterCache(compactModels, "lastnews_compact"), cancellationToken); + // var compactResult = await cacheRepo.UpsertItemAsync(new FlixsterCache(compactModels, "lastnews_compact"), cancellationToken); // return compactResult?.Data; // } @@ -42,7 +42,7 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // fullModels.Items.Add(new Shared.Models.News.Item(item.id, item.title, item.mainImage?.url, item.link)); // } - // var fullResult = await cacheRepo.CreateItemAsync(new FlixsterCache(fullModels, "lastnews_full"), cancellationToken); + // var fullResult = await cacheRepo.UpsertItemAsync(new FlixsterCache(fullModels, "lastnews_full"), cancellationToken); // return fullResult?.Data; // } @@ -52,6 +52,19 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // return model.Data; // } // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); @@ -85,7 +98,7 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // compactModels.Items.Add(new Shared.Models.Trailers.Item(item.videoId, item.title, item.thumbnails[0].url)); // } - // var compactResult = await cacheRepo.CreateItemAsync(new YoutubeCache(compactModels, "lasttrailers_compact"), cancellationToken); + // var compactResult = await cacheRepo.UpsertItemAsync(new YoutubeCache(compactModels, "lasttrailers_compact"), cancellationToken); // return compactResult?.Data; // } @@ -99,7 +112,7 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // fullModels.Items.Add(new Shared.Models.Trailers.Item(item.videoId, item.title, item.thumbnails[2].url)); // } - // var fullResult = await cacheRepo.CreateItemAsync(new YoutubeCache(fullModels, "lasttrailers_full"), cancellationToken); + // var fullResult = await cacheRepo.UpsertItemAsync(new YoutubeCache(fullModels, "lasttrailers_full"), cancellationToken); // return fullResult?.Data; // } @@ -109,6 +122,19 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // return model.Data; // } // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); @@ -143,7 +169,7 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // compactModels.Items.Add(item); // } - // var compactResult = await cacheRepo.CreateItemAsync(new MostPopularDataCache(compactModels, "popularmovies_compact"), cancellationToken); + // var compactResult = await cacheRepo.UpsertItemAsync(new MostPopularDataCache(compactModels, "popularmovies_compact"), cancellationToken); // return compactResult?.Data; // } @@ -157,7 +183,7 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // fullModels.Items.Add(item); // } - // var fullResult = await cacheRepo.CreateItemAsync(new MostPopularDataCache(fullModels, "popularmovies_full"), cancellationToken); + // var fullResult = await cacheRepo.UpsertItemAsync(new MostPopularDataCache(fullModels, "popularmovies_full"), cancellationToken); // return fullResult?.Data; // } @@ -167,6 +193,19 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // return model.Data; // } // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); @@ -190,11 +229,24 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // var obj = scraping.GetTvData(); // if (obj == null) return null; - // model = await cacheRepo.CreateItemAsync(new MostPopularDataCache(obj, "populartvs"), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MostPopularDataCache(obj, "populartvs"), cancellationToken); // } // return model?.Data; // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); @@ -215,8 +267,12 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // var id = req.GetQueryParameters()["id"]; // var tmdb_rating = req.GetQueryParameters()["tmdb_rating"]; // var title = req.GetQueryParameters()["title"]; + // DateTime.TryParseExact(req.GetQueryParameters()["release_date"], "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime release_date); - // model = await cacheRepo.Get($"rating_new_{id}", cancellationToken); + + // if (id.Empty()) throw new NotificationException($"id null ({title} - {release_date.Year})"); + + // model = await cacheRepo.Get($"rating_{id}", cancellationToken); // if (model == null) // { @@ -226,24 +282,37 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // if (release_date.Date == DateTime.MinValue.Date || release_date.Date == DateTime.MaxValue.Date) //invalid date // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddDays(-7)) // < 1 week launch // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddMonths(-1)) // < 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_week), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_week), cancellationToken); // } // else // > 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_month), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_month), cancellationToken); // } // } // return model?.Data; // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); @@ -264,8 +333,12 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // var id = req.GetQueryParameters()["id"]; // var tmdb_rating = req.GetQueryParameters()["tmdb_rating"]; // var title = req.GetQueryParameters()["title"]; + // DateTime.TryParseExact(req.GetQueryParameters()["release_date"], "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime release_date); - // model = await cacheRepo.Get($"rating_new_{id}", cancellationToken); + + // if (id.Empty()) throw new NotificationException($"id null ({title} - {release_date.Year})"); + + // model = await cacheRepo.Get($"rating_{id}", cancellationToken); // if (model == null) // { @@ -275,24 +348,37 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // if (release_date.Date == DateTime.MinValue.Date || release_date.Date == DateTime.MaxValue.Date) //invalid date // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddDays(-7)) // < 1 week launch // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddMonths(-1)) // < 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_week), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_week), cancellationToken); // } // else // > 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new RatingsCache(id, obj, ttlCache.one_month), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new RatingsCache(id, obj, ttlCache.one_month), cancellationToken); // } // } // return model?.Data; // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); @@ -329,24 +415,37 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // if (release_date.Date == DateTime.MinValue.Date || release_date.Date == DateTime.MaxValue.Date) //invalid date // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddDays(-7)) // < 1 week launch // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddMonths(-1)) // < 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_week), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_week), cancellationToken); // } // else // > 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_month), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_month), cancellationToken); // } // } // return model?.Data; // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); @@ -390,24 +489,37 @@ public class CacheFunction(CosmosCacheRepository cacheRepo) // if (release_date.Date == DateTime.MinValue.Date || release_date.Date == DateTime.MaxValue.Date) //invalid date // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddDays(-7)) // < 1 week launch // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_day), cancellationToken); // } // else if (release_date > DateTime.Now.AddMonths(-1)) // < 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_week), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_week), cancellationToken); // } // else // > 1 month launch // { - // model = await cacheRepo.CreateItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_month), cancellationToken); + // model = await cacheRepo.UpsertItemAsync(new MetaCriticCache(newModel, $"review_{id}", ttlCache.one_month), cancellationToken); // } // } // return model?.Data; // } + // catch (TaskCanceledException ex) + // { + // if (ex.CancellationToken.IsCancellationRequested) + // { + // req.ProcessException(new NotificationException("Cancellation Requested")); + // return default; + // } + // else + // { + // req.ProcessException(new NotificationException("Timeout occurred")); + // throw new UnhandledException(ex.BuildException()); + // } + // } // catch (Exception ex) // { // req.ProcessException(ex); diff --git a/MM.API/Functions/LoginFunction.cs b/MM.API/Functions/LoginFunction.cs index 162bad3..ac27571 100644 --- a/MM.API/Functions/LoginFunction.cs +++ b/MM.API/Functions/LoginFunction.cs @@ -1,4 +1,3 @@ -using Microsoft.Azure.Cosmos; using Microsoft.Azure.Functions.Worker; using Microsoft.Azure.Functions.Worker.Http; using MM.Shared.Models.Auth; @@ -15,34 +14,28 @@ public async Task Add( { try { - var platform = req.GetQueryParameters()["platform"]; - if (platform.Empty()) platform = "webapp"; + var platform = req.GetQueryParameters()["platform"] ?? "webapp"; + var ip = req.GetQueryParameters()["ip"] ?? "null ip"; var userId = req.GetUserId(); if (string.IsNullOrEmpty(userId)) throw new InvalidOperationException("unauthenticated user"); var login = await repo.Get(DocumentType.Login, userId, cancellationToken); if (login == null) { - var newLogin = new ClienteLogin { UserId = userId, Logins = [DateTimeOffset.Now], Platforms = [platform!] }; + var newLogin = new ClienteLogin + { + UserId = userId, + Accesses = [new Access { Date = DateTimeOffset.Now, Platform = platform, Ip = ip }] + }; newLogin.Initialize(userId); await repo.Upsert(newLogin, cancellationToken); } else { - if (Array.Exists(login.Platforms, a => a == platform)) - { - await repo.PatchItem(DocumentType.Login, userId, - [ - PatchOperation.Add("/logins/-", DateTimeOffset.Now), - ], cancellationToken); - } - else - { - login.Logins = login.Logins.Union([DateTimeOffset.Now]).ToArray(); - login.Platforms = login.Platforms.Union([platform]).ToArray(); - await repo.Upsert(login, cancellationToken); - } + login.Accesses = login.Accesses.Union([new Access { Date = DateTimeOffset.Now, Platform = platform, Ip = ip }]).ToArray(); + + await repo.Upsert(login, cancellationToken); } } catch (Exception ex) diff --git a/MM.API/Functions/ProfileFunction.cs b/MM.API/Functions/ProfileFunction.cs index 1eacb7e..d874598 100644 --- a/MM.API/Functions/ProfileFunction.cs +++ b/MM.API/Functions/ProfileFunction.cs @@ -5,9 +5,9 @@ namespace VerusDate.Api.Function { - public class ProfileFunction(CosmosRepository repo) + public class ProfileFunction(CosmosProfileRepository repo) { - private readonly CosmosRepository _repo = repo; + private readonly CosmosProfileRepository _repo = repo; [Function("ProfileGet")] public async Task Get( @@ -17,7 +17,7 @@ public class ProfileFunction(CosmosRepository repo) { var userId = req.GetUserId(); - return await _repo.Get(DocumentType.Profile, userId, cancellationToken); + return await _repo.Get(userId, cancellationToken); } catch (Exception ex) { @@ -103,7 +103,7 @@ public async Task UpdateLooking( PatchOperation.Add("/dtUpdate", DateTime.UtcNow) }; - return await _repo.PatchItem(DocumentType.Profile, userId, operations, cancellationToken); + return await _repo.PatchItem(userId, operations, cancellationToken); } catch (Exception ex) { @@ -129,7 +129,7 @@ public async Task UpdateLooking( var email = req.GetQueryParameters()["email"]; var userId = req.GetUserId(); - var obj = await _repo.Get(DocumentType.Profile, userId, cancellationToken); + var obj = await _repo.Get(userId, cancellationToken); obj?.UpdatePartner(id, email); diff --git a/MM.API/Functions/SendgridFunction.cs b/MM.API/Functions/SendgridFunction.cs index e45e4ae..1943303 100644 --- a/MM.API/Functions/SendgridFunction.cs +++ b/MM.API/Functions/SendgridFunction.cs @@ -65,7 +65,7 @@ public async Task SendgridInbound( SenderIp = inboundMail.SenderIp }; - await repo.CreateItemAsync(model, cancellationToken); + await repo.UpsertItemAsync(model, cancellationToken); } catch (Exception ex) { diff --git a/MM.API/MM.API.csproj b/MM.API/MM.API.csproj index b4988e0..d8ae4cc 100644 --- a/MM.API/MM.API.csproj +++ b/MM.API/MM.API.csproj @@ -23,11 +23,11 @@ - + - - - + + + diff --git a/MM.API/Program.cs b/MM.API/Program.cs index 299aa11..0d4988a 100644 --- a/MM.API/Program.cs +++ b/MM.API/Program.cs @@ -34,6 +34,7 @@ static void ConfigureServices(HostBuilderContext context, IServiceCollection ser services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddApplicationInsightsTelemetryWorkerService(); services.ConfigureFunctionsApplicationInsights(); } diff --git a/MM.API/Repository/CosmosCacheRepository.cs b/MM.API/Repository/CosmosCacheRepository.cs index b00f92f..9b6aaf7 100644 --- a/MM.API/Repository/CosmosCacheRepository.cs +++ b/MM.API/Repository/CosmosCacheRepository.cs @@ -26,29 +26,40 @@ public CosmosCacheRepository(IConfiguration config, ILogger?>(id, new PartitionKey(id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); - if (response.RequestCharge > 1.5) + if (response.RequestCharge > 1.7) { _logger.LogWarning("Get - Id {0}, RequestCharge {1}", id, response.RequestCharge); } return response.Resource; } + catch (CosmosOperationCanceledException) + { + return null; + } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { return null; } } - public async Task?> CreateItemAsync(CacheDocument cache, CancellationToken cancellationToken) where TData : class + public async Task?> UpsertItemAsync(CacheDocument cache, CancellationToken cancellationToken) where TData : class { - var response = await Container.CreateItemAsync(cache, new PartitionKey(cache.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + try + { + var response = await Container.UpsertItemAsync(cache, new PartitionKey(cache.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + + if (response.RequestCharge > 12) + { + _logger.LogWarning("Add - Id {0}, RequestCharge {1}", cache.Id, response.RequestCharge); + } - if (response.RequestCharge > 12) + return response.Resource; + } + catch (CosmosOperationCanceledException) { - _logger.LogWarning("Add - Id {0}, RequestCharge {1}", cache.Id, response.RequestCharge); + return null; } - - return response.Resource; } } } \ No newline at end of file diff --git a/MM.API/Repository/CosmosEmailRepository.cs b/MM.API/Repository/CosmosEmailRepository.cs index 032130e..bef0695 100644 --- a/MM.API/Repository/CosmosEmailRepository.cs +++ b/MM.API/Repository/CosmosEmailRepository.cs @@ -27,6 +27,10 @@ public CosmosEmailRepository(IConfiguration config) return response.Resource; } + catch (CosmosOperationCanceledException) + { + return null; + } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { return null; @@ -68,12 +72,5 @@ public async Task> Query(Expression CreateItemAsync(EmailDocument email, CancellationToken cancellationToken) - { - var response = await Container.CreateItemAsync(email, new PartitionKey(email.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); - - return response.Resource; - } } } \ No newline at end of file diff --git a/MM.API/Repository/CosmosProfileRepository.cs b/MM.API/Repository/CosmosProfileRepository.cs new file mode 100644 index 0000000..9e3d0cf --- /dev/null +++ b/MM.API/Repository/CosmosProfileRepository.cs @@ -0,0 +1,112 @@ +using Microsoft.Azure.Cosmos; +using Microsoft.Azure.Cosmos.Linq; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using MM.API.Repository.Core; +using System.Linq.Expressions; + +namespace MM.API.Repository +{ + public class CosmosProfileRepository + { + public Container Container { get; private set; } + private readonly ILogger _logger; + + public CosmosProfileRepository(IConfiguration config, ILogger logger) + { + _logger = logger; + + var databaseId = config.GetValue("RepositoryOptions_DatabaseId"); + var containerId = config.GetValue("RepositoryOptions_ContainerProfileId"); + + Container = ApiStartup.CosmosClient.GetContainer(databaseId, containerId); + } + + public async Task Get(string? id, CancellationToken cancellationToken) where T : CosmosDocument + { + if (string.IsNullOrEmpty(id)) throw new ArgumentNullException(nameof(id)); + + try + { + var response = await Container.ReadItemAsync(id, new PartitionKey(id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + + if (response.RequestCharge > 1.5) + { + _logger.LogWarning("Get - ID {0}, RequestCharge {1}", id, response.RequestCharge); + } + + return response.Resource; + } + catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) + { + return null; + } + } + + public async Task> Query(Expression> predicate, CancellationToken cancellationToken) where T : MainDocument + { + var query = Container + .GetItemLinqQueryable(requestOptions: CosmosRepositoryExtensions.GetQueryRequestOptions()) + .Where(predicate); + + using var iterator = query.ToFeedIterator(); + var results = new List(); + + double charges = 0; + while (iterator.HasMoreResults) + { + var response = await iterator.ReadNextAsync(cancellationToken); + charges += response.RequestCharge; + results.AddRange(response.Resource); + } + + if (charges > 5) + { + _logger.LogWarning("Query, RequestCharge {Charges}", charges); + } + + return results; + } + + public async Task Upsert(T item, CancellationToken cancellationToken) where T : CosmosDocument + { + var response = await Container.UpsertItemAsync(item, new PartitionKey(item.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + + if (response.RequestCharge > 12) + { + _logger.LogWarning("Upsert - ID {Id}, RequestCharge {Charges}", item.Id, response.RequestCharge); + } + + return response.Resource; + } + + public async Task PatchItem(string? id, List operations, CancellationToken cancellationToken) where T : CosmosDocument + { + //https://learn.microsoft.com/en-us/azure/cosmos-db/partial-document-update-getting-started?tabs=dotnet + + var response = await Container.PatchItemAsync(id, new PartitionKey(id), operations, CosmosRepositoryExtensions.GetPatchItemRequestOptions(), cancellationToken); + + if (response.RequestCharge > 12) + { + _logger.LogWarning("PatchItem - ID {Id}, RequestCharge {Charges}", id, response.RequestCharge); + } + + return response.Resource; + } + + public async Task Delete(T item, CancellationToken cancellationToken) where T : CosmosDocument + { + var response = await Container.DeleteItemAsync(item.Id, new PartitionKey(item.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + + if (response.RequestCharge > 12) + { + _logger.LogWarning("Delete - ID {Id}, RequestCharge {Charges}", item.Id, response.RequestCharge); + } + + return response.StatusCode == System.Net.HttpStatusCode.OK; + } + + //Overview of indexing in Azure Cosmos DB + //https://learn.microsoft.com/en-us/azure/cosmos-db/index-overview + } +} \ No newline at end of file diff --git a/MM.API/Repository/CosmosRepository.cs b/MM.API/Repository/CosmosRepository.cs index dee170c..4060eb2 100644 --- a/MM.API/Repository/CosmosRepository.cs +++ b/MM.API/Repository/CosmosRepository.cs @@ -37,6 +37,10 @@ public CosmosRepository(IConfiguration config, ILogger logger) return response.Resource; } + catch (CosmosOperationCanceledException) + { + return null; + } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { return null; @@ -45,99 +49,128 @@ public CosmosRepository(IConfiguration config, ILogger logger) public async Task> ListAll(DocumentType Type, CancellationToken cancellationToken) where T : MainDocument { - var query = Container - .GetItemLinqQueryable(requestOptions: CosmosRepositoryExtensions.GetQueryRequestOptions()) - .Where(item => item.Type == Type); + try + { + var query = Container + .GetItemLinqQueryable(requestOptions: CosmosRepositoryExtensions.GetQueryRequestOptions()) + .Where(item => item.Type == Type); - using var iterator = query.ToFeedIterator(); - var results = new List(); + using var iterator = query.ToFeedIterator(); + var results = new List(); - double charges = 0; - while (iterator.HasMoreResults) - { - var response = await iterator.ReadNextAsync(cancellationToken); - charges += response.RequestCharge; - results.AddRange(response.Resource); - } + double charges = 0; + while (iterator.HasMoreResults) + { + var response = await iterator.ReadNextAsync(cancellationToken); + charges += response.RequestCharge; + results.AddRange(response.Resource); + } + + if (charges > 7) + { + _logger.LogWarning("ListAll - Type {Type}, RequestCharge {Charges}", Type.ToString(), charges); + } - if (charges > 5) + return results; + } + catch (CosmosOperationCanceledException) { - _logger.LogWarning("ListAll - Type {Type}, RequestCharge {Charges}", Type.ToString(), charges); + return []; } - - return results; } public async Task> Query(Expression> predicate, DocumentType Type, CancellationToken cancellationToken) where T : MainDocument { - var query = Container + try + { + var query = Container .GetItemLinqQueryable(requestOptions: CosmosRepositoryExtensions.GetQueryRequestOptions()) .Where(predicate.Compose(item => item.Type == Type, Expression.AndAlso)); - using var iterator = query.ToFeedIterator(); - var results = new List(); + using var iterator = query.ToFeedIterator(); + var results = new List(); - double charges = 0; - while (iterator.HasMoreResults) - { - var response = await iterator.ReadNextAsync(cancellationToken); - charges += response.RequestCharge; - results.AddRange(response.Resource); - } + double charges = 0; + while (iterator.HasMoreResults) + { + var response = await iterator.ReadNextAsync(cancellationToken); + charges += response.RequestCharge; + results.AddRange(response.Resource); + } + + if (charges > 7) + { + _logger.LogWarning("Query - Type {Type}, RequestCharge {Charges}", Type.ToString(), charges); + } - if (charges > 5) + return results; + } + catch (CosmosOperationCanceledException) { - _logger.LogWarning("Query - Type {Type}, RequestCharge {Charges}", Type.ToString(), charges); + return []; } - - return results; } - public async Task Upsert(T item, CancellationToken cancellationToken) where T : CosmosDocument + public async Task Upsert(T item, CancellationToken cancellationToken) where T : CosmosDocument, new() { - var response = await Container.UpsertItemAsync(item, new PartitionKey(item.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + try + { + var response = await Container.UpsertItemAsync(item, new PartitionKey(item.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); - if (response.RequestCharge > 12) + if (response.RequestCharge > 12) + { + _logger.LogWarning("Upsert - ID {Id}, RequestCharge {Charges}", item.Id, response.RequestCharge); + } + + return response.Resource; + } + catch (CosmosOperationCanceledException) { - _logger.LogWarning("Upsert - ID {Id}, RequestCharge {Charges}", item.Id, response.RequestCharge); + return new T(); } - - return response.Resource; } - public async Task PatchItem(DocumentType type, string? id, List operations, CancellationToken cancellationToken) where T : CosmosDocument + public async Task PatchItem(DocumentType type, string? id, List operations, CancellationToken cancellationToken) where T : CosmosDocument, new() { //https://learn.microsoft.com/en-us/azure/cosmos-db/partial-document-update-getting-started?tabs=dotnet - var response = await Container.PatchItemAsync($"{type}:{id}", new PartitionKey($"{type}:{id}"), operations, CosmosRepositoryExtensions.GetPatchItemRequestOptions(), cancellationToken); + try + { + var response = await Container.PatchItemAsync($"{type}:{id}", new PartitionKey($"{type}:{id}"), operations, CosmosRepositoryExtensions.GetPatchItemRequestOptions(), cancellationToken); + + if (response.RequestCharge > 12) + { + _logger.LogWarning("PatchItem - ID {Id}, RequestCharge {Charges}", id, response.RequestCharge); + } - if (response.RequestCharge > 12) + return response.Resource; + } + catch (CosmosOperationCanceledException) { - _logger.LogWarning("PatchItem - ID {Id}, RequestCharge {Charges}", id, response.RequestCharge); + return new T(); } - - return response.Resource; } public async Task Delete(T item, CancellationToken cancellationToken) where T : CosmosDocument { - var response = await Container.DeleteItemAsync(item.Id, new PartitionKey(item.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + try + { + var response = await Container.DeleteItemAsync(item.Id, new PartitionKey(item.Id), CosmosRepositoryExtensions.GetItemRequestOptions(), cancellationToken); + + if (response.RequestCharge > 12) + { + _logger.LogWarning("Delete - ID {Id}, RequestCharge {Charges}", item.Id, response.RequestCharge); + } - if (response.RequestCharge > 12) + return response.StatusCode == System.Net.HttpStatusCode.OK; + } + catch (CosmosOperationCanceledException) { - _logger.LogWarning("Delete - ID {Id}, RequestCharge {Charges}", item.Id, response.RequestCharge); + return false; } - - return response.StatusCode == System.Net.HttpStatusCode.OK; } - //multiple transactions - //https://docs.microsoft.com/pt-br/learn/modules/perform-cross-document-transactional-operations-azure-cosmos-db-sql-api/2-create-transactional-batch-sdk - - //bulk insert - //https://docs.microsoft.com/pt-br/learn/modules/process-bulk-data-azure-cosmos-db-sql-api/2-create-bulk-operations-sdk - - //composite indexes - //https://docs.microsoft.com/pt-br/learn/modules/customize-indexes-azure-cosmos-db-sql-api/3-evaluate-composite-indexes + //Overview of indexing in Azure Cosmos DB + //https://learn.microsoft.com/en-us/azure/cosmos-db/index-overview } } \ No newline at end of file diff --git a/MM.Shared/Core/CosmosDocument.cs b/MM.Shared/Core/CosmosDocument.cs index 83b23d7..fd29ab3 100644 --- a/MM.Shared/Core/CosmosDocument.cs +++ b/MM.Shared/Core/CosmosDocument.cs @@ -29,7 +29,7 @@ protected CosmosDocument(string id) public abstract bool HasValidData(); - protected void SetIds(string id) + public void SetIds(string id) { if (FixedId) throw new InvalidOperationException(); diff --git a/MM.Shared/Core/Models/MainDocument.cs b/MM.Shared/Core/Models/MainDocument.cs index 4ed32b8..61faa9d 100644 --- a/MM.Shared/Core/Models/MainDocument.cs +++ b/MM.Shared/Core/Models/MainDocument.cs @@ -5,7 +5,7 @@ public enum DocumentType Principal = 1, Ticket = 2, TicketVote = 3, - Profile = 4, + //Profile = 4, Invite = 5, Announcement = 6, Login = 7, diff --git a/MM.Shared/Models/Auth/ClienteLogin.cs b/MM.Shared/Models/Auth/ClienteLogin.cs index 3c236bd..fae39a0 100644 --- a/MM.Shared/Models/Auth/ClienteLogin.cs +++ b/MM.Shared/Models/Auth/ClienteLogin.cs @@ -7,8 +7,8 @@ public ClienteLogin() : base(DocumentType.Login) } public string? UserId { get; set; } - public DateTimeOffset[] Logins { get; set; } = []; - public string[] Platforms { get; set; } = []; + + public Access[] Accesses { get; set; } = []; public override void Initialize(string userId) { @@ -23,4 +23,11 @@ public override bool HasValidData() return true; } } + + public class Access + { + public DateTimeOffset Date { get; set; } + public string? Platform { get; set; } + public string? Ip { get; set; } + } } \ No newline at end of file diff --git a/MM.Shared/Models/HereJson.cs b/MM.Shared/Models/HereJson.cs index 006c98f..3d4b4fb 100644 --- a/MM.Shared/Models/HereJson.cs +++ b/MM.Shared/Models/HereJson.cs @@ -18,12 +18,23 @@ public class HereAddress public string? county { get; set; } public string? city { get; set; } - public string GetLocation() + public string? GetCountry() { if (countryName.Empty()) throw new NotificationException("country not found"); + + return countryName; + } + + public string? GetState() + { + return state ?? county ?? countryName; + } + + public string? GetCity() + { if (city.Empty()) throw new NotificationException("city not found"); - return $"{countryName} - {state ?? county ?? countryName} - {city}"; + return city; } } diff --git a/MM.Shared/Models/Profile/ProfileModel.cs b/MM.Shared/Models/Profile/ProfileModel.cs index 776dce8..8a40f31 100644 --- a/MM.Shared/Models/Profile/ProfileModel.cs +++ b/MM.Shared/Models/Profile/ProfileModel.cs @@ -3,12 +3,8 @@ namespace MM.Shared.Models.Profile { - public class ProfileModel : PrivateMainDocument + public class ProfileModel : CosmosDocument { - public ProfileModel() : base(DocumentType.Profile) - { - } - #region BASIC [Custom(Name = "Modality_Name", ResourceType = typeof(Resources.ProfileBasicModel))] @@ -20,8 +16,13 @@ public ProfileModel() : base(DocumentType.Profile) [Custom(Name = "Description_Name", Prompt = "Description_Prompt", ResourceType = typeof(Resources.ProfileBasicModel))] public string? Description { get; set; } + [JsonIgnore] [Custom(Name = "Location_Name", Prompt = "Location_Prompt", Description = "Location_Description", ResourceType = typeof(Resources.ProfileBasicModel))] - public string? Location { get; set; } + public string? Location => $"{Country} - {State} - {City}"; + + public string? Country { get; set; } + public string? State { get; set; } + public string? City { get; set; } [Custom(Name = "Languages_Name", Description = "Languages_Description", FieldInfo = "Dizem que uma boa comunicação é a chave para qualquer relacionamento duradouro e bem-sucedido. É absolutamente essencial que duas pessoas compartilhem seus sentimentos, expressem seus pensamentos e, talvez o mais importante, ouçam atentamente uma à outra. Infelizmente, no mundo acelerado e agitado de hoje, muitos casais não encontram tempo para sentar e ter uma conversa significativa um com o outro. Telefonemas e mensagens de texto substituíram os bate-papos pessoais entre duas pessoas. A falta de comunicação adequada é uma das principais razões pelas quais muitos relacionamentos não duram tanto quanto deveriam. Tendo tudo isso em mente, é realmente uma boa ideia você namorar uma pessoa que não fala a mesma língua que você?", ResourceType = typeof(Resources.ProfileBasicModel))] public HashSet Languages { get; set; } = []; @@ -218,7 +219,9 @@ public void UpdateData(ProfileModel profile) Modality = profile.Modality; NickName = profile.NickName; Description = profile.Description; - Location = profile.Location; + Country = profile.Country; + State = profile.State; + City = profile.City; Languages = profile.Languages; CurrentSituation = profile.CurrentSituation; Intentions = profile.Intentions; diff --git a/MM.WEB/Core/AppStateStatic.cs b/MM.WEB/Core/AppStateStatic.cs index 4f766e2..8121cd5 100644 --- a/MM.WEB/Core/AppStateStatic.cs +++ b/MM.WEB/Core/AppStateStatic.cs @@ -8,6 +8,7 @@ public static class AppStateStatic { public static List Logs { get; private set; } = []; + [Custom(Name = "Language", ResourceType = typeof(GlobalTranslations))] public static Language Language { get; private set; } public static Bar? Sidebar { get; set; } diff --git a/MM.WEB/Core/CacheApi.cs b/MM.WEB/Core/CacheApi.cs index c7a39f3..fdc230c 100644 --- a/MM.WEB/Core/CacheApi.cs +++ b/MM.WEB/Core/CacheApi.cs @@ -15,7 +15,7 @@ public struct Endpoint public static string GetShowReviews(string? id, string? title, DateTime? date) => $"Public/Cache/Reviews/Shows?id={id}&title={title}&release_date={date?.ToString("yyyy-MM-dd")}"; } - //public class CacheFlixsterApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCore(http, memoryCache, "NewsModel") + //public class CacheFlixsterApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCosmos(http, memoryCache, null) //{ // public async Task GetNews(string mode, RenderControlCore? core) // { @@ -23,7 +23,7 @@ public struct Endpoint // } //} - //public class CacheYoutubeApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCore(http, memoryCache, "TrailerModel") + //public class CacheYoutubeApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCosmos(http, memoryCache, null) //{ // public async Task GetTrailers(string mode, RenderControlCore? core) // { @@ -31,20 +31,32 @@ public struct Endpoint // } //} - //public class CacheRatingsApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCore(http, memoryCache, "Ratings") + //public class CacheRatingsApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCosmos(http, memoryCache, null) //{ // public async Task GetMovieRatings(string? id, string? title, DateTime? releaseDate, string? tmdb_rating, RenderControlCore? core) // { + // if (id.Empty()) + // { + // core?.LoadingFinished?.Invoke(new()); + // return new(); + // } + // return await GetAsync(Endpoint.GetMovieRatings(id, title, releaseDate, tmdb_rating), core, $"TrailerModel-{id}"); // } // public async Task GetShowRatings(string? id, string? title, DateTime? releaseDate, string? tmdb_rating, RenderControlCore? core) // { + // if (id.Empty()) + // { + // core?.LoadingFinished?.Invoke(new()); + // return new(); + // } + // return await GetAsync(Endpoint.GetShowRatings(id, title, releaseDate, tmdb_rating), core, $"TrailerModel-{id}"); // } //} - //public class CacheMetaCriticApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCore(http, memoryCache, "ReviewModel") + //public class CacheMetaCriticApi(IHttpClientFactory http, IMemoryCache memoryCache) : ApiCosmos(http, memoryCache, null) //{ // public async Task GetMovieReviews(string? id, string? title, DateTime? releaseDate, RenderControlCore? core) // { diff --git a/MM.WEB/MM.WEB.csproj b/MM.WEB/MM.WEB.csproj index 02d3610..27b3722 100644 --- a/MM.WEB/MM.WEB.csproj +++ b/MM.WEB/MM.WEB.csproj @@ -23,24 +23,24 @@ - - - - - + + + + + - - + + true - - + + true - + diff --git a/MM.WEB/Modules/Administrator/Components/FeedbackComponent.razor b/MM.WEB/Modules/Administrator/Components/FeedbackComponent.razor index 1fd4796..112d861 100644 --- a/MM.WEB/Modules/Administrator/Components/FeedbackComponent.razor +++ b/MM.WEB/Modules/Administrator/Components/FeedbackComponent.razor @@ -5,6 +5,7 @@ @using MM.WEB.Modules.Administrator.Core @using MM.WEB.Modules.Auth.Core @using MM.WEB.Modules.Support.Core + @inherits ComponentCore @attribute [Authorize(Roles = "administrator")] diff --git a/MM.WEB/Modules/Auth/Core/LoginApi.cs b/MM.WEB/Modules/Auth/Core/LoginApi.cs index d038179..697c376 100644 --- a/MM.WEB/Modules/Auth/Core/LoginApi.cs +++ b/MM.WEB/Modules/Auth/Core/LoginApi.cs @@ -7,12 +7,25 @@ public class LoginApi(IHttpClientFactory factory, IMemoryCache memoryCache) : Ap { private struct Endpoint { - public static string Add(string platform) => $"login/add?platform={platform}"; + public static string Add(string platform, string? ip) => $"login/add?platform={platform}&ip={ip}"; } public async Task Add(string platform) { - await PostAsync(Endpoint.Add(platform), null, null); + var ip = ""; + + try + { + //TODO: TypeError: Failed to fetch + var response = await _http.GetAsync(new Uri("https://ipinfo.io/ip")); + ip = await response.Content.ReadAsStringAsync(); + } + catch (Exception) + { + //do nothing; + } + + await PostAsync(Endpoint.Add(platform, ip?.Trim()), null, null); } } } \ No newline at end of file diff --git a/MM.WEB/Modules/Auth/ProfilePopup.razor b/MM.WEB/Modules/Auth/ProfilePopup.razor index 2f1e427..6311a17 100644 --- a/MM.WEB/Modules/Auth/ProfilePopup.razor +++ b/MM.WEB/Modules/Auth/ProfilePopup.razor @@ -107,7 +107,7 @@ { try { - if (await MessageService.Confirm(GlobalTranslations.SureDeleteAccount, "Streaming Discovery", (opt) => { opt.ShowMessageIcon = false; })) + if (await MessageService.Confirm(GlobalTranslations.SureDeleteAccount, "Modern Matchmaker", (opt) => { opt.ShowMessageIcon = false; })) { //remove data from cosmos db await PrincipalApi.Remove(); diff --git a/MM.WEB/Modules/Index.razor b/MM.WEB/Modules/Index.razor index 002983f..da0819c 100644 --- a/MM.WEB/Modules/Index.razor +++ b/MM.WEB/Modules/Index.razor @@ -3,6 +3,37 @@ @inject IJSRuntime JsRuntime + + + + + Countries 0 + + + + + + + Cities 0 + + + + + + + Users 0 + + + + + + + Couples 0 + + + + + diff --git a/MM.WEB/Modules/Profile/Components/ProfileHeader.razor b/MM.WEB/Modules/Profile/Components/ProfileHeader.razor new file mode 100644 index 0000000..850f387 --- /dev/null +++ b/MM.WEB/Modules/Profile/Components/ProfileHeader.razor @@ -0,0 +1,120 @@ +@using MM.Shared.Models.Auth +@using MM.WEB.Modules.Auth +@using MM.WEB.Modules.Auth.Core +@using MM.WEB.Modules.Profile.Core +@using MM.WEB.Modules.Profile.Resources + +@inherits PageCore + +@inject PaddleConfigurationApi ConfigurationApi +@inject GravatarApi GravatarApi + +@inject IHttpClientFactory httpClientFactory +@inject IJSRuntime JsRuntime + + + + +
+ +
+
+ + + @Gravatar?.displayName +
+ + @Client?.Email + +
+ + + + +
+
+@if (Platform.NotEmpty() && Reviewed.Empty() && Client != null && (Client.ClientePaddle == null || !Client.ClientePaddle.IsPaidUser)) +{ + + + @GlobalTranslations.WriteReviewTitle + + + @GlobalTranslations.WriteReviewMessage + @if (Platform == "play") + { + + } + @if (Platform == "windows") + { + + } + @if (Platform == "webapp") + { + + } + + +} + +@code { + [SupplyParameterFromQuery][Parameter] public string? _ptxn { get; set; } + + public ClientePrincipal? Client { get; set; } + public Gravatar? Gravatar { get; set; } + public string? Platform { get; set; } + public string? Reviewed { get; set; } + + protected override void OnInitialized() + { + PrincipalApi.DataChanged += (ClientePrincipal? model) => { Client = model; StateHasChanged(); }; + } + + protected override async Task LoadDataRender() + { + Client = await PrincipalApi.Get(IsAuthenticated); + Gravatar = await GravatarApi.GetGravatar(Client?.Email); + + if (!string.IsNullOrEmpty(_ptxn)) + { + var config = await ConfigurationApi.GetConfigurations(); + if (config == null) throw new NotificationException("server comunication error"); + + await JsRuntime.InvokeVoidAsync("startPaddle", config.Token); + } + + Platform = await JsRuntime.InvokeAsync("GetLocalStorage", "platform"); + Reviewed = await JsRuntime.InvokeAsync("GetLocalStorage", "reviewed"); + } + + private async Task MyAccount() + { + await ModalService.OpenPopup(x => { x.Add(x => x.principal, Client); x.Add(x => x.Gravatar, Gravatar); }, ModalSize.Large); + } + + private async Task OpenSubscription() + { + await ModalService.SubscriptionPopup(Client, IsAuthenticated); + } + + private async Task SetReviewed() + { + await JsRuntime.InvokeAsync("SetLocalStorage", "reviewed", "true"); + Reviewed = "true"; + } +} diff --git a/MM.WEB/Modules/Profile/Profile.razor b/MM.WEB/Modules/Profile/Profile.razor index 4c3ebe5..4e56976 100644 --- a/MM.WEB/Modules/Profile/Profile.razor +++ b/MM.WEB/Modules/Profile/Profile.razor @@ -8,9 +8,14 @@ @inject ProfileApi ProfileApi +@if (IsAuthenticated) +{ + +} + - Seu Perfil (@profile?.NickName) + @(profile?.NickName ?? User?.Identity?.Name) @foreach (var item in imageDataUriGallery) @@ -167,29 +172,29 @@ @view?.GetLocation(ProfileModel.LocationType.City) *@ @* @if (view.ActivityStatus == ActivityStatus.Today) - { - - logado hoje - - } - else if (view.ActivityStatus == ActivityStatus.Week) - { - - logado esta semana - - } - else if (view.ActivityStatus == ActivityStatus.Month) - { - - logado este mês - - } - else - { - - usuário desativado - - } *@ + { + + logado hoje + + } + else if (view.ActivityStatus == ActivityStatus.Week) + { + + logado esta semana + + } + else if (view.ActivityStatus == ActivityStatus.Month) + { + + logado este mês + + } + else + { + + usuário desativado + + } *@ @* @if (view.Reports.Any()) { diff --git a/MM.WEB/Modules/Profile/ProfileData.razor b/MM.WEB/Modules/Profile/ProfileData.razor index 2ee6b13..f24a528 100644 --- a/MM.WEB/Modules/Profile/ProfileData.razor +++ b/MM.WEB/Modules/Profile/ProfileData.razor @@ -46,16 +46,13 @@ } - @if (profile?.Modality == Modality.RelationshipAnalysis) - { - - @WEB.Resources.GlobalTranslations.MyRelationship - @if (!IsValid(Tabs.MyRelationship)) - { - - } - - } + + Goals + @if (!IsValid(Tabs.Goals)) + { + + } + @@ -66,7 +63,7 @@ - @@ -155,8 +152,6 @@ - - - + + @@ -240,7 +236,7 @@ @@ -277,7 +273,7 @@ Lifestyle, Personality, Interests, - MyRelationship + Goals } private bool IsValid(Tabs tab) @@ -301,9 +297,6 @@ case Tabs.Interests: valid = _validator?.Validate(options => options.IncludeRuleSets("INTEREST")) ?? false; break; - case Tabs.MyRelationship: - valid = _validator?.Validate(options => options.IncludeRuleSets("MYRELATIONSHIP")) ?? false; - break; default: break; } diff --git a/MM.WEB/Modules/Profile/ProfileData.razor.cs b/MM.WEB/Modules/Profile/ProfileData.razor.cs index ad3b2cc..041ed3d 100644 --- a/MM.WEB/Modules/Profile/ProfileData.razor.cs +++ b/MM.WEB/Modules/Profile/ProfileData.razor.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Components.Forms; using Microsoft.JSInterop; using MM.Shared.Models.Profile; -using MM.WEB.Api; using MM.WEB.Modules.Profile.Core; using MM.WEB.Shared; @@ -12,7 +11,6 @@ namespace MM.WEB.Modules.Profile public partial class ProfileData : PageCore { [Inject] protected ProfileApi ProfileApi { get; set; } = default!; - [Inject] protected InviteApi InviteApi { get; set; } = default!; [Inject] protected MapApi MapApi { get; set; } = default!; [Inject] protected IJSRuntime JsRuntime { get; set; } = default!; @@ -23,9 +21,9 @@ public partial class ProfileData : PageCore protected override async Task LoadDataRender() { - //LoadingProfile?.Start(); - profile = await ProfileApi.Get(Core); - //LoadingProfile?.Finish(profile == null); + Core?.LoadingStarted?.Invoke(); + + profile = await ProfileApi.Get(null); profile ??= new() { @@ -34,6 +32,8 @@ protected override async Task LoadDataRender() BirthDate = DateTime.UtcNow.AddYears(-18).AddDays(1).Date, Diet = Diet.Omnivore, }; + + Core?.LoadingFinished?.Invoke(profile); } private async Task SetLocation(ProfileModel profile) @@ -63,7 +63,9 @@ private async Task SetLocation(ProfileModel profile) if (here != null && here.items.Count != 0) { var address = here.items[0].address; - profile.Location = address?.GetLocation(); + profile.Country = address?.GetCountry(); + profile.State = address?.GetState(); + profile.City = address?.GetCity(); var obj = EnumHelper.GetList().Single(s => s.Tips == (address?.countryCode ?? "USA")); var country = (Country)obj.Value; @@ -72,7 +74,7 @@ private async Task SetLocation(ProfileModel profile) } else { - profile.Location = "Unknown Location"; + //profile.Location = "Unknown Location"; } } else @@ -101,12 +103,12 @@ private async Task HandleValidSubmit() if (profile?.Modality == Modality.Matchmaker) { - foreach (var item in profile.Partners) - { - //TODO: remove the conections on others users - } + //foreach (var item in profile.Partners) + //{ + // //TODO: remove the conections on others users + //} - profile.Partners = []; + //profile.Partners = []; } else { diff --git a/MM.WEB/Modules/Profile/ProfilePreference.razor b/MM.WEB/Modules/Profile/ProfilePreference.razor index f8c1425..b2ec707 100644 --- a/MM.WEB/Modules/Profile/ProfilePreference.razor +++ b/MM.WEB/Modules/Profile/ProfilePreference.razor @@ -7,7 +7,7 @@ @inject ProfileApi ProfileApi - +
@@ -168,7 +168,7 @@ protected override async Task LoadDataRender() { - // Loading?.Start(); + Core?.LoadingStarted?.Invoke(); profile = await ProfileApi.Get(Core); @@ -187,7 +187,7 @@ await PopulateFields(); } - // Loading?.Finish(preference == null); + Core?.LoadingFinished?.Invoke(profile); } private async Task HandleValidSubmit() diff --git a/MM.WEB/Modules/RedirectExternalLink.razor b/MM.WEB/Modules/RedirectExternalLink.razor new file mode 100644 index 0000000..b1df528 --- /dev/null +++ b/MM.WEB/Modules/RedirectExternalLink.razor @@ -0,0 +1,17 @@ +@page "/redirect" +@inject NavigationManager navigator +@inject IJSRuntime JsRuntime + +@code { + [Parameter][SupplyParameterFromQuery] public string? link { get; set; } + + protected override void OnInitialized() + { + var external_link = link; + if (!string.IsNullOrEmpty(external_link)) + { + // await JsRuntime.InvokeVoidAsync("eval", $"let _discard_ = open(`{external_link}`, `_blank`)"); + navigator.NavigateTo(external_link.ShowExternalLink()); + } + } +} diff --git a/MM.WEB/Modules/Shared/Field/FieldSelect.razor b/MM.WEB/Modules/Shared/Field/FieldSelect.razor index 0df8e26..0a7dd9c 100644 --- a/MM.WEB/Modules/Shared/Field/FieldSelect.razor +++ b/MM.WEB/Modules/Shared/Field/FieldSelect.razor @@ -101,7 +101,10 @@ } - + @if (IsForm) + { + + } @if (!string.IsNullOrEmpty(Description) && ShowDescription) { diff --git a/MM.WEB/Modules/Shared/Field/FieldSelect.razor.cs b/MM.WEB/Modules/Shared/Field/FieldSelect.razor.cs index e8550a7..2adac47 100644 --- a/MM.WEB/Modules/Shared/Field/FieldSelect.razor.cs +++ b/MM.WEB/Modules/Shared/Field/FieldSelect.razor.cs @@ -16,6 +16,7 @@ public partial class FieldSelect : FormBase @inject NavigationManager navigation diff --git a/MM.WEB/Modules/Support/Component/DownloadComponent.razor b/MM.WEB/Modules/Support/Component/DownloadComponent.razor index 9e78a94..1f06909 100644 --- a/MM.WEB/Modules/Support/Component/DownloadComponent.razor +++ b/MM.WEB/Modules/Support/Component/DownloadComponent.razor @@ -27,9 +27,9 @@ Quick Links - + @Resources.Translations.LinkTerm - + @Resources.Translations.LinkPrivacy diff --git a/MM.WEB/Resources/BuildDate.txt b/MM.WEB/Resources/BuildDate.txt index 8dcd6df..0aae45b 100644 --- a/MM.WEB/Resources/BuildDate.txt +++ b/MM.WEB/Resources/BuildDate.txt @@ -1 +1 @@ -2024.07.13 +2024.09.09 diff --git a/MM.WEB/Resources/GlobalTranslations.Designer.cs b/MM.WEB/Resources/GlobalTranslations.Designer.cs index f37940c..3440f8f 100644 --- a/MM.WEB/Resources/GlobalTranslations.Designer.cs +++ b/MM.WEB/Resources/GlobalTranslations.Designer.cs @@ -195,6 +195,15 @@ public static string InvitationNotAccepted { } } + /// + /// Looks up a localized string similar to Language. + /// + public static string Language { + get { + return ResourceManager.GetString("Language", resourceCulture); + } + } + /// /// Looks up a localized string similar to My Relationship. /// @@ -438,6 +447,24 @@ public static string ValidationErrorsDetected { } } + /// + /// Looks up a localized string similar to Help us reach a larger audience.. + /// + public static string WriteReviewMessage { + get { + return ResourceManager.GetString("WriteReviewMessage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Write a review. + /// + public static string WriteReviewTitle { + get { + return ResourceManager.GetString("WriteReviewTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Years. /// diff --git a/MM.WEB/Resources/GlobalTranslations.es.resx b/MM.WEB/Resources/GlobalTranslations.es.resx index ba23f8f..707a853 100644 --- a/MM.WEB/Resources/GlobalTranslations.es.resx +++ b/MM.WEB/Resources/GlobalTranslations.es.resx @@ -246,4 +246,16 @@ Conexión activa + + Ajustes + + + Idioma + + + Ayúdanos a llegar a un público más amplio. + + + Escribe una reseña + \ No newline at end of file diff --git a/MM.WEB/Resources/GlobalTranslations.pt.resx b/MM.WEB/Resources/GlobalTranslations.pt.resx index be6211f..6f004da 100644 --- a/MM.WEB/Resources/GlobalTranslations.pt.resx +++ b/MM.WEB/Resources/GlobalTranslations.pt.resx @@ -246,4 +246,16 @@ Conexão ativa + + Configurações + + + Linguagem + + + Ajude -nos a alcançar um público maior. + + + Escreva uma crítica + \ No newline at end of file diff --git a/MM.WEB/Resources/GlobalTranslations.resx b/MM.WEB/Resources/GlobalTranslations.resx index 30f0c42..fe9cd7d 100644 --- a/MM.WEB/Resources/GlobalTranslations.resx +++ b/MM.WEB/Resources/GlobalTranslations.resx @@ -249,4 +249,13 @@ Active Connection + + Language + + + Help us reach a larger audience. + + + Write a review + \ No newline at end of file diff --git a/MM.WEB/Shared/MainLayout.razor b/MM.WEB/Shared/MainLayout.razor index dfb9c51..b3a0e1b 100644 --- a/MM.WEB/Shared/MainLayout.razor +++ b/MM.WEB/Shared/MainLayout.razor @@ -99,11 +99,15 @@ - @MM.WEB.Modules.Support.Resources.Translations.Home + + @MM.WEB.Modules.Support.Resources.Translations.Home + @MM.WEB.Modules.Support.Resources.Translations.Updates - @MM.WEB.Modules.Support.Resources.Translations.Feedback + + @MM.WEB.Modules.Support.Resources.Translations.Feedback + diff --git a/MM.WEB/Shared/SettingsPopup.razor b/MM.WEB/Shared/SettingsPopup.razor index bf3ac39..4c8e744 100644 --- a/MM.WEB/Shared/SettingsPopup.razor +++ b/MM.WEB/Shared/SettingsPopup.razor @@ -7,7 +7,7 @@ + CssIcon="@FontAwesomeIcons.Language" Order="(o)=>o.Name" IsForm="false" ShowHelper="false">