diff --git a/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Data/FairPlayCombinedDbContext.cs b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Data/FairPlayCombinedDbContext.cs index 953b5342..e5f5fc27 100644 --- a/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Data/FairPlayCombinedDbContext.cs +++ b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Data/FairPlayCombinedDbContext.cs @@ -114,6 +114,8 @@ public FairPlayCombinedDbContext(DbContextOptions opt public virtual DbSet UserMessage { get; set; } + public virtual DbSet UserMessage1 { get; set; } + public virtual DbSet UserProfile { get; set; } public virtual DbSet VideoCaptions { get; set; } @@ -405,6 +407,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasConstraintName("FK_ToApplicationUserId_ApplicationUser"); }); + modelBuilder.Entity(entity => + { + entity.HasOne(d => d.FromApplicationUser).WithMany(p => p.UserMessage1FromApplicationUser) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_FromApplicationUserId_AspNetUsers"); + + entity.HasOne(d => d.ToApplicationUser).WithMany(p => p.UserMessage1ToApplicationUser) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_ToApplicationUserId_AspNetUsers"); + }); + modelBuilder.Entity(entity => { entity.HasOne(d => d.ApplicationUser).WithOne(p => p.UserProfile) diff --git a/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Models/FairPlayTube/UserMessage1.cs b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Models/FairPlayTube/UserMessage1.cs new file mode 100644 index 00000000..57da1426 --- /dev/null +++ b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Models/FairPlayTube/UserMessage1.cs @@ -0,0 +1,58 @@ +// This file has been auto generated by EF Core Power Tools. +#nullable disable +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using FairPlayCombined.DataAccess.Models.dboSchema; +using FairPlayCombined.DataAccess.Models.FairPlayBudgetSchema; +using FairPlayCombined.DataAccess.Models.FairPlayDatingSchema; +using FairPlayCombined.DataAccess.Models.FairPlayShopSchema; +using FairPlayCombined.DataAccess.Models.FairPlaySocialSchema; +using FairPlayCombined.DataAccess.Models.FairPlayTubeSchema; + + +namespace FairPlayCombined.DataAccess.Models.FairPlayTubeSchema; + +[Table("UserMessage", Schema = "FairPlayTube")] +public partial class UserMessage1 +{ + [Key] + public long UserMessageId { get; set; } + + [Required] + [StringLength(450)] + public string FromApplicationUserId { get; set; } + + [Required] + [StringLength(450)] + public string ToApplicationUserId { get; set; } + + [Required] + public string Message { get; set; } + + [Required] + [StringLength(250)] + public string SourceApplication { get; set; } + + [Required] + [StringLength(100)] + public string OriginatorIpaddress { get; set; } + + public DateTimeOffset RowCreationDateTime { get; set; } + + [Required] + [StringLength(256)] + public string RowCreationUser { get; set; } + + public bool ReadByDestinatary { get; set; } + + [ForeignKey("FromApplicationUserId")] + [InverseProperty("UserMessage1FromApplicationUser")] + public virtual AspNetUsers FromApplicationUser { get; set; } + + [ForeignKey("ToApplicationUserId")] + [InverseProperty("UserMessage1ToApplicationUser")] + public virtual AspNetUsers ToApplicationUser { get; set; } +} \ No newline at end of file diff --git a/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Models/dbo/AspNetUsers.cs b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Models/dbo/AspNetUsers.cs index f8a49bae..5d4d6c71 100644 --- a/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Models/dbo/AspNetUsers.cs +++ b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/Models/dbo/AspNetUsers.cs @@ -104,6 +104,12 @@ public partial class AspNetUsers [InverseProperty("ApplicationUser")] public virtual ICollection UserActivity { get; set; } = new List(); + [InverseProperty("FromApplicationUser")] + public virtual ICollection UserMessage1FromApplicationUser { get; set; } = new List(); + + [InverseProperty("ToApplicationUser")] + public virtual ICollection UserMessage1ToApplicationUser { get; set; } = new List(); + [InverseProperty("FromApplicationUser")] public virtual ICollection UserMessageFromApplicationUser { get; set; } = new List(); diff --git a/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/efpt.config.json b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/efpt.config.json index ea19400f..c8edb385 100644 --- a/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/efpt.config.json +++ b/src/FairPlayCombinedSln/FairPlayCombined.DataAccess/efpt.config.json @@ -213,6 +213,10 @@ "Name": "[FairPlaySocial].[PostVisibility]", "ObjectType": 0 }, + { + "Name": "[FairPlayTube].[UserMessage]", + "ObjectType": 0 + }, { "Name": "[FairPlayTube].[VideoCaptions]", "ObjectType": 0 diff --git a/src/FairPlayCombinedSln/FairPlayCombined.Models/FairPlayTube/Conversation/ConversationUserModel.cs b/src/FairPlayCombinedSln/FairPlayCombined.Models/FairPlayTube/Conversation/ConversationUserModel.cs new file mode 100644 index 00000000..8e0c011e --- /dev/null +++ b/src/FairPlayCombinedSln/FairPlayCombined.Models/FairPlayTube/Conversation/ConversationUserModel.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FairPlayCombined.Models.FairPlayTube.Conversation +{ + /// + /// Holds the data regarding an ApplicationUser + /// + public class ConversationsUserModel + { + [Required] + [StringLength(450)] + /// + /// Id of an application user + /// + public string? ApplicationUserId { get; set; } + /// + /// User's Full Name + /// + [Required] + [StringLength(150)] + public string? FullName { get; set; } + } +} diff --git a/src/FairPlayCombinedSln/FairPlayCombined.Models/FairPlayTube/UserMessage/UserMessageModel.cs b/src/FairPlayCombinedSln/FairPlayCombined.Models/FairPlayTube/UserMessage/UserMessageModel.cs new file mode 100644 index 00000000..90ebb9f2 --- /dev/null +++ b/src/FairPlayCombinedSln/FairPlayCombined.Models/FairPlayTube/UserMessage/UserMessageModel.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FairPlayCombined.Models.FairPlayTube.UserMessage +{ + + /// + /// Represents the User Message entry + /// + public class UserMessageModel + { + /// + /// ApplicationUserId of the user to whom the message is sent + /// + public string? ToApplicationUserId { get; set; } + /// + /// Full Name of the To Application User + /// + public string? ToApplicationUserFullName { get; set; } + /// + /// Full Name of the From Application User + /// + public string? FromApplicationUserFullName { get; set; } + /// + /// Message to be sent + /// + [Required] + public string? Message { get; set; } + /// + /// UTC DateTime the message was created + /// + public DateTimeOffset? RowCreationDateTime { get; set; } + /// + /// Indicated if the message has been read by the destinatary + /// + public bool ReadByDestinatary { get; set; } + } +} diff --git a/src/FairPlayCombinedSln/FairPlayCombined.Services/FairPlayTube/UserMessageService.cs b/src/FairPlayCombinedSln/FairPlayCombined.Services/FairPlayTube/UserMessageService.cs new file mode 100644 index 00000000..f65be741 --- /dev/null +++ b/src/FairPlayCombinedSln/FairPlayCombined.Services/FairPlayTube/UserMessageService.cs @@ -0,0 +1,92 @@ +using FairPlayCombined.DataAccess.Data; +using FairPlayCombined.Interfaces; +using FairPlayCombined.Models.FairPlayTube.Conversation; +using FairPlayCombined.Models.FairPlayTube.UserMessage; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FairPlayCombined.Services.FairPlayTube +{ + public partial class UserMessageService( + IDbContextFactory dbContextFactory, + IUserProviderService userProviderService) : BaseService + { + public async Task GetMyConversationsUsersAsync( + CancellationToken cancellationToken) + { + var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); + var currentUser = await + dbContext.AspNetUsers + .SingleAsync(p => p.Id == + userProviderService.GetCurrentUserId(), cancellationToken: cancellationToken); + var receivedMessagesUsers = await dbContext.UserMessage1 + .Include(p => p.FromApplicationUser) + .Where(p => p.ToApplicationUserId == currentUser.Id) + .Select(p => p.FromApplicationUser) + .Distinct().ToListAsync(); + var sentMessagesUsers = await dbContext.UserMessage1 + .Include(p => p.ToApplicationUser) + .Where(p => p.FromApplicationUserId == currentUser.Id) + .Select(p => p.ToApplicationUser) + .Distinct().ToListAsync(); + var result = receivedMessagesUsers + .Union(sentMessagesUsers) + .Distinct() + .Select(p=>new ConversationsUserModel() + { + ApplicationUserId = p.Id, + FullName = p.UserName + }) + .ToArray(); + return result; + } + + public async Task GetMyConversationsWithUserAsync(string? userId, CancellationToken cancellationToken) + { + var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); + var currentUser = await + dbContext.AspNetUsers + .SingleAsync(p => p.Id == + userProviderService.GetCurrentUserId(), cancellationToken: cancellationToken); + return await dbContext.UserMessage1 + .Include(p => p.ToApplicationUser) + .Include(p => p.FromApplicationUser) + .Where(p => + (p.FromApplicationUserId == currentUser.Id && + p.ToApplicationUserId == userId) + || + (p.FromApplicationUserId == userId && + p.ToApplicationUserId == currentUser.Id) + ) + .OrderByDescending(p => p.RowCreationDateTime) + .Select(p=>new UserMessageModel() + { + FromApplicationUserFullName = p.FromApplicationUser.UserName, + Message = p.Message, + ReadByDestinatary = p.ReadByDestinatary, + RowCreationDateTime = p.RowCreationDateTime, + ToApplicationUserFullName = p.ToApplicationUser.UserName, + ToApplicationUserId = p.ToApplicationUserId + }).ToArrayAsync(cancellationToken); + } + + public async Task SendMessageAsync(UserMessageModel? userMessageModel, + CancellationToken cancellationToken) + { + var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); + await dbContext.UserMessage1.AddAsync( + new DataAccess.Models.FairPlayTubeSchema.UserMessage1() + { + FromApplicationUserId = userProviderService.GetCurrentUserId(), + ToApplicationUserId = userMessageModel!.ToApplicationUserId, + Message = userMessageModel.Message, + }, cancellationToken); + await dbContext.SaveChangesAsync(cancellationToken); + } + } +} diff --git a/src/FairPlayCombinedSln/FairPlayCombinedDb/FairPlayCombinedDb.sqlproj b/src/FairPlayCombinedSln/FairPlayCombinedDb/FairPlayCombinedDb.sqlproj index 05fd295e..2fcb6774 100644 --- a/src/FairPlayCombinedSln/FairPlayCombinedDb/FairPlayCombinedDb.sqlproj +++ b/src/FairPlayCombinedSln/FairPlayCombinedDb/FairPlayCombinedDb.sqlproj @@ -160,6 +160,7 @@ + diff --git a/src/FairPlayCombinedSln/FairPlayCombinedDb/FairPlayTube/Tables/UserMessage.sql b/src/FairPlayCombinedSln/FairPlayCombinedDb/FairPlayTube/Tables/UserMessage.sql new file mode 100644 index 00000000..4d64b273 --- /dev/null +++ b/src/FairPlayCombinedSln/FairPlayCombinedDb/FairPlayTube/Tables/UserMessage.sql @@ -0,0 +1,12 @@ +CREATE TABLE [FairPlayTube].[UserMessage] +( + [UserMessageId] BIGINT NOT NULL CONSTRAINT PK_UserMessage PRIMARY KEY IDENTITY, + [FromApplicationUserId] NVARCHAR(450) NOT NULL CONSTRAINT FK_FromApplicationUserId_AspNetUsers FOREIGN KEY REFERENCES [dbo].[AspNetUsers]([Id]), + [ToApplicationUserId] NVARCHAR(450) NOT NULL CONSTRAINT FK_ToApplicationUserId_AspNetUsers FOREIGN KEY REFERENCES [dbo].[AspNetUsers]([Id]), + [Message] NVARCHAR(MAX) NOT NULL, + [SourceApplication] NVARCHAR(250) NOT NULL, + [OriginatorIpaddress] NVARCHAR(100) NOT NULL, + [RowCreationDateTime] DATETIMEOFFSET NOT NULL, + [RowCreationUser] NVARCHAR(256) NOT NULL, + [ReadByDestinatary] BIT NOT NULL DEFAULT 0 +) \ No newline at end of file diff --git a/src/FairPlayCombinedSln/FairPlayTube/Components/Pages/Creator/MyVideoViewers.razor b/src/FairPlayCombinedSln/FairPlayTube/Components/Pages/Creator/MyVideoViewers.razor index c5d95a10..581ed1ba 100644 --- a/src/FairPlayCombinedSln/FairPlayTube/Components/Pages/Creator/MyVideoViewers.razor +++ b/src/FairPlayCombinedSln/FairPlayTube/Components/Pages/Creator/MyVideoViewers.razor @@ -18,7 +18,26 @@
- + + @if (String.IsNullOrWhiteSpace(context.Username)) + { + Anonymous + } + else + { + @context.Username + } + + + @if (!String.IsNullOrWhiteSpace(context.Username)) + { + + + + } + @TimeSpan.FromSeconds(context.TotalTimeWatched) @@ -52,7 +71,7 @@ }; var items = await videoViewerService .GetPaginatedVideoViewerInfoForUserIdAsync(paginationRequest, - videoId:this.VideoId!, + videoId: this.VideoId!, userId: this.userProviderService!.GetCurrentUserId()!, this.cancellationTokenSource.Token); StateHasChanged(); diff --git a/src/FairPlayCombinedSln/FairPlayTube/Components/Pages/User/MyConversations.razor b/src/FairPlayCombinedSln/FairPlayTube/Components/Pages/User/MyConversations.razor new file mode 100644 index 00000000..591e30cf --- /dev/null +++ b/src/FairPlayCombinedSln/FairPlayTube/Components/Pages/User/MyConversations.razor @@ -0,0 +1,156 @@ +@page "/User/MyConversations" +@using FairPlayCombined.Models.FairPlayTube.Conversation +@using FairPlayCombined.Models.FairPlayTube.UserMessage +@using FairPlayCombined.Services.FairPlayTube +@using FairPlayTube.Components.Spinners + +@attribute [Authorize] +@attribute [StreamRendering(enabled: true)] + +@rendermode RenderMode.InteractiveServer + +@inject UserMessageService userMessageService +@inject IToastService toastService + +MyConversations + + + MyConversations + + + + +@if (this.AllMyConversationsUsers?.Length > 0) +{ +
+
+
+
+ @foreach (var singleUser in this.AllMyConversationsUsers) + { + + } +
+
+
+ @if (this.AllMyConversationsWithSelectedUser != null) + { + @foreach (var singleConversation in this.AllMyConversationsWithSelectedUser) + { +
+
+ + + + @singleConversation.FromApplicationUserFullName + + + + @singleConversation.Message + + + @singleConversation.RowCreationDateTime + + +
+
+ } + } +
+
+
+
+ +
+ + +
+
+ + + +
+
+
+
+
+} + +@code { + private ConversationsUserModel[]? AllMyConversationsUsers; + private bool IsBusy { get; set; } + public ConversationsUserModel? SelectedUser { get; private set; } + private UserMessageModel[]? AllMyConversationsWithSelectedUser { get; set; } + private UserMessageModel? MessageToSend { get; set; } = new UserMessageModel(); + private CancellationTokenSource cancellationTokenSource = new(); + + protected override async Task OnInitializedAsync() + { + + try + { + IsBusy = true; + this.AllMyConversationsUsers = + await this.userMessageService + .GetMyConversationsUsersAsync(this.cancellationTokenSource.Token); + if (this.AllMyConversationsUsers?.Length > 0) + { + this.SelectedUser = this.AllMyConversationsUsers[0]; + this.AllMyConversationsWithSelectedUser = await + this.userMessageService + .GetMyConversationsWithUserAsync(this.SelectedUser.ApplicationUserId, + this.cancellationTokenSource.Token); + this.MessageToSend!.ToApplicationUserId = this.SelectedUser.ApplicationUserId; + } + } + catch (Exception ex) + { + toastService.ShowError(ex.Message); + } + finally + { + IsBusy = false; + } + } + + private async Task SendMessageAsync() + { + try + { + IsBusy = true; + + this.MessageToSend!.ToApplicationUserId = this.SelectedUser!.ApplicationUserId; + await this.userMessageService.SendMessageAsync(this.MessageToSend, + this.cancellationTokenSource.Token); + await LoadMyConversationsWithSelectedUser(); + this.MessageToSend.Message = String.Empty; + } + catch (Exception ex) + { + toastService.ShowError(ex.Message); + } + finally + { + IsBusy = false; + } + } + + private async Task LoadMyConversationsWithSelectedUser() + { + this.AllMyConversationsWithSelectedUser = + await this.userMessageService + .GetMyConversationsWithUserAsync(this.SelectedUser!.ApplicationUserId, + this.cancellationTokenSource.Token); + } + + private async Task SelectUserAsync(ConversationsUserModel conversationsUserModel) + { + this.SelectedUser = conversationsUserModel; + await LoadMyConversationsWithSelectedUser(); + StateHasChanged(); + } +} diff --git a/src/FairPlayCombinedSln/FairPlayTube/Program.cs b/src/FairPlayCombinedSln/FairPlayTube/Program.cs index dee78086..1cd833a9 100644 --- a/src/FairPlayCombinedSln/FairPlayTube/Program.cs +++ b/src/FairPlayCombinedSln/FairPlayTube/Program.cs @@ -239,6 +239,7 @@ builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); +builder.Services.AddTransient(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen();