diff --git a/CouchLog/Components/Account/Pages/ExternalLogin.razor b/CouchLog/Components/Account/Pages/ExternalLogin.razor index e803dd1..bf24ccd 100644 --- a/CouchLog/Components/Account/Pages/ExternalLogin.razor +++ b/CouchLog/Components/Account/Pages/ExternalLogin.razor @@ -54,7 +54,7 @@ private HttpContext HttpContext { get; set; } = default!; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; [SupplyParameterFromQuery] private string? RemoteError { get; set; } @@ -94,6 +94,8 @@ // the login page if we get here some other way. RedirectManager.RedirectTo("Account/Login"); } + + Input ??= new InputModel(); } private async Task OnLoginCallbackAsync() diff --git a/CouchLog/Components/Account/Pages/ForgotPassword.razor b/CouchLog/Components/Account/Pages/ForgotPassword.razor index 8cbe591..29cf4ad 100644 --- a/CouchLog/Components/Account/Pages/ForgotPassword.razor +++ b/CouchLog/Components/Account/Pages/ForgotPassword.razor @@ -35,7 +35,7 @@ @code { [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; private async Task OnValidSubmitAsync() { diff --git a/CouchLog/Components/Account/Pages/Login.razor b/CouchLog/Components/Account/Pages/Login.razor index b02b6e7..303f8d1 100644 --- a/CouchLog/Components/Account/Pages/Login.razor +++ b/CouchLog/Components/Account/Pages/Login.razor @@ -31,7 +31,7 @@ @* MudTextFields zwingend mit Name-Attribut für SSR *@ Logger +@inject ISnackbar Snackbar Change password -

Change password

- -
-
- - - -
- - - -
-
- - - -
-
- - - -
- -
-
-
+Change password + + + + + + + + + + Change password + @code { - private string? message; - private ApplicationUser user = default!; - private bool hasPassword; + private ApplicationUser _user = null!; + private bool _hasPassword; [CascadingParameter] - private HttpContext HttpContext { get; set; } = default!; + private HttpContext HttpContext { get; set; } = null!; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; protected override async Task OnInitializedAsync() { - user = await UserAccessor.GetRequiredUserAsync(HttpContext); - hasPassword = await UserManager.HasPasswordAsync(user); - if (!hasPassword) + _user = await UserAccessor.GetRequiredUserAsync(HttpContext); + _hasPassword = await UserManager.HasPasswordAsync(_user); + if (!_hasPassword) { RedirectManager.RedirectTo("Account/Manage/SetPassword"); } + Input ??= new InputModel(); } private async Task OnValidSubmitAsync() { - var changePasswordResult = await UserManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + var changePasswordResult = await UserManager.ChangePasswordAsync(_user, Input.OldPassword, Input.NewPassword); if (!changePasswordResult.Succeeded) { - message = $"Error: {string.Join(",", changePasswordResult.Errors.Select(error => error.Description))}"; + return; } - await SignInManager.RefreshSignInAsync(user); + await SignInManager.RefreshSignInAsync(_user); Logger.LogInformation("User changed their password successfully."); RedirectManager.RedirectToCurrentPageWithStatus("Your password has been changed", HttpContext); diff --git a/CouchLog/Components/Account/Pages/Manage/DeletePersonalData.razor b/CouchLog/Components/Account/Pages/Manage/DeletePersonalData.razor index 8fb6331..438b8d9 100644 --- a/CouchLog/Components/Account/Pages/Manage/DeletePersonalData.razor +++ b/CouchLog/Components/Account/Pages/Manage/DeletePersonalData.razor @@ -47,13 +47,15 @@ private HttpContext HttpContext { get; set; } = default!; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; protected override async Task OnInitializedAsync() { Input ??= new(); user = await UserAccessor.GetRequiredUserAsync(HttpContext); requirePassword = await UserManager.HasPasswordAsync(user); + + Input ??= new InputModel(); } private async Task OnValidSubmitAsync() diff --git a/CouchLog/Components/Account/Pages/Manage/Email.razor b/CouchLog/Components/Account/Pages/Manage/Email.razor index 2115d77..b64a040 100644 --- a/CouchLog/Components/Account/Pages/Manage/Email.razor +++ b/CouchLog/Components/Account/Pages/Manage/Email.razor @@ -63,7 +63,7 @@ private HttpContext HttpContext { get; set; } = default!; [SupplyParameterFromForm(FormName = "change-email")] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; protected override async Task OnInitializedAsync() { @@ -72,6 +72,7 @@ isEmailConfirmed = await UserManager.IsEmailConfirmedAsync(user); Input.NewEmail ??= email; + Input ??= new InputModel(); } private async Task OnValidSubmitAsync() diff --git a/CouchLog/Components/Account/Pages/Manage/EnableAuthenticator.razor b/CouchLog/Components/Account/Pages/Manage/EnableAuthenticator.razor index 6004d0d..ad5f478 100644 --- a/CouchLog/Components/Account/Pages/Manage/EnableAuthenticator.razor +++ b/CouchLog/Components/Account/Pages/Manage/EnableAuthenticator.razor @@ -79,7 +79,7 @@ else private HttpContext HttpContext { get; set; } = default!; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; protected override async Task OnInitializedAsync() { diff --git a/CouchLog/Components/Account/Pages/Manage/Index.razor b/CouchLog/Components/Account/Pages/Manage/Index.razor index 54edfd9..50c4c6f 100644 --- a/CouchLog/Components/Account/Pages/Manage/Index.razor +++ b/CouchLog/Components/Account/Pages/Manage/Index.razor @@ -11,27 +11,14 @@ Profile -

Profile

+Profile -
-
- - - -
- - -
- -
-
-
+ + + + + @code { private ApplicationUser user = default!; @@ -42,7 +29,7 @@ private HttpContext HttpContext { get; set; } = default!; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; protected override async Task OnInitializedAsync() { diff --git a/CouchLog/Components/Account/Pages/Manage/SetPassword.razor b/CouchLog/Components/Account/Pages/Manage/SetPassword.razor index 5ccd8ba..5087f38 100644 --- a/CouchLog/Components/Account/Pages/Manage/SetPassword.razor +++ b/CouchLog/Components/Account/Pages/Manage/SetPassword.razor @@ -45,7 +45,7 @@ private HttpContext HttpContext { get; set; } = default!; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; protected override async Task OnInitializedAsync() { diff --git a/CouchLog/Components/Account/Pages/Register.razor b/CouchLog/Components/Account/Pages/Register.razor index 8ca9662..20154fe 100644 --- a/CouchLog/Components/Account/Pages/Register.razor +++ b/CouchLog/Components/Account/Pages/Register.razor @@ -59,7 +59,7 @@ private IEnumerable? identityErrors; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; [SupplyParameterFromQuery] private string? ReturnUrl { get; set; } diff --git a/CouchLog/Components/Account/Pages/ResendEmailConfirmation.razor b/CouchLog/Components/Account/Pages/ResendEmailConfirmation.razor index 69a5d36..83999e6 100644 --- a/CouchLog/Components/Account/Pages/ResendEmailConfirmation.razor +++ b/CouchLog/Components/Account/Pages/ResendEmailConfirmation.razor @@ -37,7 +37,7 @@ private string? message; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; private async Task OnValidSubmitAsync() { diff --git a/CouchLog/Components/Account/Pages/ResetPassword.razor b/CouchLog/Components/Account/Pages/ResetPassword.razor index e1754f9..b83d72d 100644 --- a/CouchLog/Components/Account/Pages/ResetPassword.razor +++ b/CouchLog/Components/Account/Pages/ResetPassword.razor @@ -46,7 +46,7 @@ private IEnumerable? identityErrors; [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + private InputModel Input { get; set; } = null!; [SupplyParameterFromQuery] private string? Code { get; set; } diff --git a/CouchLog/Components/Account/Shared/ManageLayout.razor b/CouchLog/Components/Account/Shared/ManageLayout.razor index bdd73a8..dfca18c 100644 --- a/CouchLog/Components/Account/Shared/ManageLayout.razor +++ b/CouchLog/Components/Account/Shared/ManageLayout.razor @@ -1,17 +1,18 @@ @inherits LayoutComponentBase @layout CouchLog.Components.Layout.MainLayout -

Manage your account

+Manage your account -
-

Change your account settings

-
-
-
- -
-
+ + Change your account settings +
+ + + + + + @Body -
-
-
+ + + \ No newline at end of file diff --git a/CouchLog/Components/Account/Shared/ManageNavMenu.razor b/CouchLog/Components/Account/Shared/ManageNavMenu.razor index e6c1413..8f01db2 100644 --- a/CouchLog/Components/Account/Shared/ManageNavMenu.razor +++ b/CouchLog/Components/Account/Shared/ManageNavMenu.razor @@ -3,33 +3,41 @@ @inject SignInManager SignInManager - + + @code { private bool hasExternalLogins; diff --git a/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor b/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor index 4fb8717..d7e35bd 100644 --- a/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor +++ b/CouchLog/Components/AdminSettings/Pages/CouchLogSettings.razor @@ -17,11 +17,11 @@ Color="Color.Primary" /> -Version: @_version +@*Version: @_version*@ @code { - private AccountsSettings? _accountsSettings; - private readonly string _version = Assembly.GetEntryAssembly().GetCustomAttribute()?.InformationalVersion?.Split('+')[0]; + private AccountsSettings _accountsSettings = null!; + //private readonly string _version = Assembly.GetEntryAssembly().GetCustomAttribute()?.InformationalVersion?.Split('+')[0]; protected override async Task OnInitializedAsync() { diff --git a/CouchLog/Components/Pages/GlobalList/Components/GlobalEntityCard.razor b/CouchLog/Components/Pages/GlobalList/Components/GlobalEntityCard.razor index 09d3e19..5bcd010 100644 --- a/CouchLog/Components/Pages/GlobalList/Components/GlobalEntityCard.razor +++ b/CouchLog/Components/Pages/GlobalList/Components/GlobalEntityCard.razor @@ -1,11 +1,16 @@ @using CouchLog.Data - - + + + + + - @GlobalEntity.Title + + @GlobalEntity.Title + @@ -34,4 +39,7 @@ [Parameter] public EventCallback OnAddToPrivate { get; set; } [Parameter] public EventCallback OnAddToShared { get; set; } + [Parameter] public EventCallback OnEdit { get; set; } + + private bool _showEditOverlay; } diff --git a/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor b/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor index 57feb7f..edadd95 100644 --- a/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor +++ b/CouchLog/Components/Pages/GlobalList/Dialogs/AddNewGlobalEntityDialog.razor @@ -1,10 +1,9 @@ @using CouchLog.Data @using Microsoft.EntityFrameworkCore -@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Identity @using System.ComponentModel.DataAnnotations -@inject ApplicationDbContext CouchLogDB +@inject ApplicationDbContext CouchLogDb @inject UserManager UserManager @inject AuthenticationStateProvider AuthenticationStateProvider @inject NavigationManager NavigationManager @@ -23,10 +22,10 @@ - + - + @if (!string.IsNullOrWhiteSpace(_imagePreview)) @@ -44,13 +43,12 @@ Accept="image/*" InputClass="null" Class="mud-file-upload-dragarea"> - + } - - + - - @if (_isSaving) - { - - Saving... - } - else - { - Create Global Entity - } - @@ -97,7 +84,18 @@ - Cancel + Cancel + + @if (_isSaving) + { + + Saving... + } + else + { + Create Global Entity + } + @@ -120,7 +118,7 @@ //Picture private IBrowserFile? _pictureFile; - private string _pictureBase64; + private string _pictureBase64 = null!; private string? _pictureContentType; private string? _pictureExtension; @@ -145,8 +143,8 @@ return; } - _mediaTypes = await CouchLogDB.MediaType.OrderBy(t => t.Id).ToListAsync(); - _genres = await CouchLogDB.Genres.OrderBy(t => t.Id).ToListAsync(); + _mediaTypes = await CouchLogDb.MediaType.OrderBy(t => t.Id).ToListAsync(); + _genres = await CouchLogDb.Genres.OrderBy(t => t.Id).ToListAsync(); if (_mediaTypes.Count > 0) _selectedMediaTypeId = _mediaTypes.First().Id; @@ -212,19 +210,19 @@ IsPrivate = _isPrivate }; - CouchLogDB.Add(entity); - await CouchLogDB.SaveChangesAsync(); + CouchLogDb.Add(entity); + await CouchLogDb.SaveChangesAsync(); foreach (int genreId in _selectedGenreIds) { - CouchLogDB.Add(new LinkTableGlobalGenre + CouchLogDb.Add(new LinkTableGlobalGenre { GenreId = genreId, GlobalEntityId = entity.Id }); } - await CouchLogDB.SaveChangesAsync(); + await CouchLogDb.SaveChangesAsync(); Snackbar.Add("Global Entity created!", Severity.Success); MudDialog.Close(DialogResult.Ok(entity)); diff --git a/CouchLog/Components/Pages/GlobalList/Dialogs/EditGlobalEntityDialog.razor b/CouchLog/Components/Pages/GlobalList/Dialogs/EditGlobalEntityDialog.razor new file mode 100644 index 0000000..2f0566b --- /dev/null +++ b/CouchLog/Components/Pages/GlobalList/Dialogs/EditGlobalEntityDialog.razor @@ -0,0 +1,340 @@ +@using CouchLog.Data +@using Microsoft.EntityFrameworkCore + +@inject ApplicationDbContext CouchLogDb +@inject ISnackbar Snackbar + + + + Edit Global Entity + + + + + + @if (!string.IsNullOrWhiteSpace(_imagePreview)) + { + + + + + + + } + else + { + + + + + + } + + + + + + @foreach (var type in _mediaTypes) + { + @type.Name + } + + + + @foreach (var genre in _genres) + { + @genre.Name + } + + + @if (AppUser.Id == _editedGlobalEntity.CreatorId) + { + + Delete + + } + else + { + + + + Delete + + + + } + + + + + + + + + Cancel + Save Changes + + + +@code +{ + [CascadingParameter] private IMudDialogInstance Dialog { get; set; } = null!; + + [Parameter] public GlobalEntity GlobalEntity { get; set; } = null!; + [Parameter] public ApplicationUser AppUser { get; set; } = null!; + + [Inject] private IDialogService DialogService { get; set; } + + private GlobalEntity _editedGlobalEntity = null!; + private string? _imagePreview; + private IBrowserFile? _pictureFile; + private List _mediaTypes = new(); + private IReadOnlyCollection _selectedGenreIds = Array.Empty(); + private List _genres = new(); + private bool _isPrivate; + + private bool _showChangePictureOverlay; + + private string _oldImageBase64; + + private string _placeHolder = "data:image/png;base64,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"; + + protected override async Task OnInitializedAsync() + { + await CouchLogDb.GlobalEntities + .Include(x => x.MediaType) + .Include(x => x.LinkTableGlobalGenres) + .LoadAsync(); + + _editedGlobalEntity = new GlobalEntity + { + //Required + Id = GlobalEntity.Id, + CreatorId = GlobalEntity.CreatorId, + CreationTime = GlobalEntity.CreationTime, + TypeId = GlobalEntity.TypeId, + + //Editable + Title = GlobalEntity.Title, + MediaType = GlobalEntity.MediaType, + LinkTableGlobalGenres = GlobalEntity.LinkTableGlobalGenres, + IsPrivate = GlobalEntity.IsPrivate, + PicturePath = GlobalEntity.PicturePath, + }; + await GetImagePreview(); + _mediaTypes = await CouchLogDb.MediaType.OrderBy(t => t.Id).ToListAsync(); + _selectedGenreIds = GlobalEntity.LinkTableGlobalGenres.Select(x => x.GenreId).ToList(); + //var newGenreIds = new HashSet(_selectedGenreIds); + _genres = await CouchLogDb.Genres.OrderBy(t => t.Id).ToListAsync(); + _isPrivate = _editedGlobalEntity.IsPrivate; + } + + private async Task GetImagePreview() + { + if (_editedGlobalEntity.PicturePath == null) + { + _imagePreview = _placeHolder; + return; + } + string path = Path.Combine("wwwroot", _editedGlobalEntity.PicturePath); + + byte[] imageArray; + string base64Image = ""; + + try + { + imageArray = await File.ReadAllBytesAsync(path); + base64Image = Convert.ToBase64String(imageArray); + } + catch (Exception ex) + { + + } + + if (!String.IsNullOrWhiteSpace(base64Image)) + { + _imagePreview = $"data:image/jpeg;base64,{base64Image}"; + _oldImageBase64 = _imagePreview; + } + else + { + _imagePreview = _placeHolder; + } + } + + private async Task PrepareForNewImage() + { + if (_imagePreview != _placeHolder) + { + _imagePreview = null; + } + else + { + _imagePreview = null; + } + } + + private async Task OnPictureUploaded() + { + if (_pictureFile == null) + { + Snackbar.Add("No file selected!", Severity.Warning); + return; + } + if (_pictureFile.Size > 5242880) + { + Snackbar.Add("File size exceeds 5MB!", Severity.Error); + return; + } + await using var stream = _pictureFile.OpenReadStream(5242880); + using var ms = new MemoryStream(); + await stream.CopyToAsync(ms); + + var _pictureContentType = _pictureFile.ContentType; + var _pictureExtension = Path.GetExtension(_pictureFile.Name); + + var _pictureBase64 = Convert.ToBase64String(ms.ToArray()); + _imagePreview = $"data:{_pictureFile.ContentType};base64,{_pictureBase64}"; + } + + private async Task HandleDelete() + { + //TODO: Add Message Box + File.Delete(Path.Combine("wwwroot", _editedGlobalEntity.PicturePath)); + Dialog.Close(DialogResult.Ok((_editedGlobalEntity, true))); + } + + private bool CheckForChanges() + { + if (_editedGlobalEntity.Title != GlobalEntity.Title) + { + return true; + } + if(_editedGlobalEntity.TypeId != GlobalEntity.TypeId) + { + return true; + } + + var newGenresHashSet = new HashSet(_selectedGenreIds); + var oldGenereIds = GlobalEntity.LinkTableGlobalGenres.Select(g => g.GenreId); + if (!newGenresHashSet.SetEquals(oldGenereIds)) + { + Snackbar.Add("Selected genre id is", Severity.Warning); + return true; + } + + if (_editedGlobalEntity.IsPrivate != GlobalEntity.IsPrivate) + { + return true; + } + + if (_imagePreview != _oldImageBase64) + { + return true; + } + + return false; + } + + private void UpdateGenreLinks() + { + var currentGenreIds = GlobalEntity.LinkTableGlobalGenres.Select(g => g.GenreId).ToHashSet(); + var selectedGenreIds = _selectedGenreIds.ToHashSet(); + + var toRemove = GlobalEntity.LinkTableGlobalGenres + .Where(g => !selectedGenreIds.Contains(g.GenreId)) + .ToList(); + foreach (var link in toRemove) + { + GlobalEntity.LinkTableGlobalGenres.Remove(link); + } + + var toAdd = selectedGenreIds.Where(id => !currentGenreIds.Contains(id)); + foreach (var genreId in toAdd) + { + GlobalEntity.LinkTableGlobalGenres.Add(new LinkTableGlobalGenre + { + GlobalEntityId = GlobalEntity.Id, + GenreId = genreId + }); + } + } + + private async Task SaveImageToFile(string dataUrl, string targetPath) + { + if (string.IsNullOrWhiteSpace(dataUrl)) + { + return; + } + + var commaIndex = dataUrl.IndexOf(','); + if (commaIndex < 0) + { + return; + } + + string base64Data = dataUrl.Substring(commaIndex + 1); + byte[] imageBytes = Convert.FromBase64String(base64Data); + + await File.WriteAllBytesAsync(targetPath, imageBytes); + } + + private async Task SaveChanges() + { + if (CheckForChanges()) + { + bool? result = await DialogService.ShowMessageBoxAsync( + "Warning", + "Do you really want to save does changes?", + yesText:"Save", cancelText:"Cancel"); + + if (result == true) + { + GlobalEntity.Title = _editedGlobalEntity.Title; + GlobalEntity.TypeId = _editedGlobalEntity.TypeId; + GlobalEntity.MediaType = _editedGlobalEntity.MediaType; + UpdateGenreLinks(); + GlobalEntity.IsPrivate = _editedGlobalEntity.IsPrivate; + GlobalEntity.LastChange = DateTime.UtcNow; + + SaveImageToFile(_imagePreview, Path.Combine("wwwroot", _editedGlobalEntity.PicturePath)); + + await CouchLogDb.SaveChangesAsync(); + Dialog.Close(DialogResult.Ok((GlobalEntity, false))); + } + } + else + { + Snackbar.Add("You changed nothing so there is nothing to save!", Severity.Info); + Cancel(); + } + } + + private void Cancel() + { + Dialog.Cancel(); + } +} \ No newline at end of file diff --git a/CouchLog/Components/Pages/GlobalList/GlobalList.razor b/CouchLog/Components/Pages/GlobalList/GlobalList.razor index c1d54d4..88f77b7 100644 --- a/CouchLog/Components/Pages/GlobalList/GlobalList.razor +++ b/CouchLog/Components/Pages/GlobalList/GlobalList.razor @@ -78,11 +78,12 @@ @foreach (var globalEntity in VisibleGlobalEntities) { - + + OnAddToShared="AddGlobalEntityToSharedList" + OnEdit="OpenEditGlobalEntityDialog"/> } @@ -165,6 +166,44 @@ await OnNewEntity(entity); } } + + private async Task OpenEditGlobalEntityDialog(GlobalEntity globalEntity) + { + var parameters = new DialogParameters + { + { x => x.GlobalEntity, globalEntity }, + { x => x.AppUser, _appUser} + }; + + var dialog = await DialogService.ShowAsync("Edit Global Entity", parameters); + var result = await dialog.Result; + + if (!result!.Canceled && result.Data is ValueTuple data) + { + if (data.Item2) + { + CouchLogDb.GlobalEntities.Remove(data.Item1); + await CouchLogDb.SaveChangesAsync(); + + _globalEntities.RemoveAll(x => x.Id == data.Item1.Id); + + if (_currentPage > TotalPages) + _currentPage = Math.Max(1, TotalPages); + + StateHasChanged(); + } + + if (!data.Item2) + { + int index = _globalEntities.FindIndex(x => x.Id == data.Item1.Id); + if (index >= 0) + { + _globalEntities[index] = data.Item1; + } + StateHasChanged(); + } + } + } public async Task AddGlobalEntityToPrivateList(GlobalEntity globalEntity) { diff --git a/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor b/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor index f42e7d3..fadae32 100644 --- a/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor +++ b/CouchLog/Components/Pages/PrivateList/Components/PrivateEntityCard.razor @@ -17,7 +17,9 @@ - @PrivateEntity.GlobalEntity.Title + + @PrivateEntity.GlobalEntity.Title + Edit Remove diff --git a/CouchLog/Components/Pages/PrivateList/PrivateList.razor b/CouchLog/Components/Pages/PrivateList/PrivateList.razor index a69d974..07c2c51 100644 --- a/CouchLog/Components/Pages/PrivateList/PrivateList.razor +++ b/CouchLog/Components/Pages/PrivateList/PrivateList.razor @@ -132,7 +132,7 @@ //Filter private string? _searchString; private bool _showFilters; - private bool _showAlreadyAddedEntities = false; + //private bool _showAlreadyAddedEntities = false; private IEnumerable FiltertGlobalEntities => _privateEntities.Where(entity => (string.IsNullOrWhiteSpace(_searchString) || entity.GlobalEntity.Title.Contains(_searchString, StringComparison.OrdinalIgnoreCase)) //Search bar ); diff --git a/CouchLog/CouchLog.csproj b/CouchLog/CouchLog.csproj index 915973e..bcff7d6 100644 --- a/CouchLog/CouchLog.csproj +++ b/CouchLog/CouchLog.csproj @@ -9,6 +9,8 @@ True 0.1.0-nightly.10 Penry + false + false @@ -60,26 +62,23 @@ - - - - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/CouchLog/Data/DatabaseModels/Global/GlobalEntity.cs b/CouchLog/Data/DatabaseModels/Global/GlobalEntity.cs index 0abf9d8..cb3cc21 100644 --- a/CouchLog/Data/DatabaseModels/Global/GlobalEntity.cs +++ b/CouchLog/Data/DatabaseModels/Global/GlobalEntity.cs @@ -4,8 +4,7 @@ using System.Diagnostics.CodeAnalysis; namespace CouchLog.Data { - [method: SetsRequiredMembers] - public class GlobalEntity() + public class GlobalEntity { [Key] public int Id { get; set; } diff --git a/CouchLog/wwwroot/Pictures/Inception.jpg b/CouchLog/wwwroot/Pictures/Inception.jpg deleted file mode 100644 index b1884e3..0000000 Binary files a/CouchLog/wwwroot/Pictures/Inception.jpg and /dev/null differ