Add IsPinned and IsDeleted properties to Note entity with corresponding database configurations and migrations
This commit enhances the Note entity by introducing two new properties: IsPinned and IsDeleted, allowing for better management of note visibility and status. The NoteConfiguration class has been updated to include indexes for these properties, improving query performance. Additionally, new migrations have been created to reflect these changes in the database schema. The UI components have been updated to support pinning and restoring notes, enhancing user interaction and functionality within the note management system.
This commit is contained in:
@@ -30,11 +30,20 @@ public class NotesService : INotesService
|
||||
return user.FindFirstValue(ClaimTypes.Email) ?? user.Identity?.Name;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Note>> GetNotesAsync()
|
||||
public async Task<IEnumerable<Note>> GetNotesAsync(bool includeDeleted = false)
|
||||
{
|
||||
return await _context.Notes
|
||||
var query = _context.Notes
|
||||
.AsNoTracking()
|
||||
.OrderByDescending(n => n.UpdatedAt)
|
||||
.AsQueryable();
|
||||
|
||||
if (!includeDeleted)
|
||||
{
|
||||
query = query.Where(n => !n.IsDeleted);
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderBy(n => n.Title.StartsWith("@") ? 1 : 0) // Non-page notes first (0), page notes last (1)
|
||||
.ThenByDescending(n => n.UpdatedAt) // Within each group, order by most recently updated
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
@@ -45,6 +54,15 @@ public class NotesService : INotesService
|
||||
.FirstOrDefaultAsync(n => n.Id == id);
|
||||
}
|
||||
|
||||
public async Task<Note?> GetPageNoteAsync(string pageIdentifier)
|
||||
{
|
||||
var pageNoteTitle = $"@{pageIdentifier}";
|
||||
return await _context.Notes
|
||||
.AsNoTracking()
|
||||
.Where(n => n.Title == pageNoteTitle && !n.IsDeleted)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NoteHistory>> GetNoteHistoryAsync(int noteId)
|
||||
{
|
||||
return await _context.NoteHistories
|
||||
@@ -147,16 +165,134 @@ public class NotesService : INotesService
|
||||
Content = note.Content,
|
||||
ModifiedBy = userEmail,
|
||||
ModifiedAt = now,
|
||||
ChangeType = "Deleted"
|
||||
ChangeType = "Soft Deleted"
|
||||
};
|
||||
|
||||
_context.NoteHistories.Add(history);
|
||||
|
||||
// Delete the note (cascade will handle history, but we want to keep history)
|
||||
_context.Notes.Remove(note);
|
||||
// Soft delete - set IsDeleted flag instead of removing
|
||||
note.IsDeleted = true;
|
||||
note.UpdatedAt = now;
|
||||
note.LastModifiedBy = userEmail;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Note deleted: {NoteId} by {User}", id, userEmail);
|
||||
_logger.LogInformation("Note soft deleted: {NoteId} by {User}", id, userEmail);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Note>> GetPinnedNotesAsync()
|
||||
{
|
||||
return await _context.Notes
|
||||
.AsNoTracking()
|
||||
.Where(n => n.IsPinned && !n.Title.StartsWith("@") && !n.IsDeleted)
|
||||
.OrderByDescending(n => n.UpdatedAt)
|
||||
.Take(3)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task TogglePinNoteAsync(int noteId)
|
||||
{
|
||||
var note = await _context.Notes
|
||||
.FirstOrDefaultAsync(n => n.Id == noteId);
|
||||
|
||||
if (note == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Note with ID {noteId} not found.");
|
||||
}
|
||||
|
||||
// Prevent pinning page notes
|
||||
if (note.Title.StartsWith("@"))
|
||||
{
|
||||
throw new InvalidOperationException("Page notes cannot be pinned.");
|
||||
}
|
||||
|
||||
var userEmail = GetCurrentUserEmail();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// If pinning and already 3 pinned notes exist (excluding this note if it's already pinned)
|
||||
if (!note.IsPinned)
|
||||
{
|
||||
var pinnedCount = await _context.Notes
|
||||
.CountAsync(n => n.IsPinned && !n.Title.StartsWith("@") && !n.IsDeleted);
|
||||
|
||||
if (pinnedCount >= 3)
|
||||
{
|
||||
// Unpin the oldest pinned note
|
||||
var oldestPinned = await _context.Notes
|
||||
.Where(n => n.IsPinned && !n.Title.StartsWith("@") && !n.IsDeleted)
|
||||
.OrderBy(n => n.UpdatedAt)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (oldestPinned != null)
|
||||
{
|
||||
oldestPinned.IsPinned = false;
|
||||
oldestPinned.UpdatedAt = now;
|
||||
oldestPinned.LastModifiedBy = userEmail;
|
||||
_logger.LogInformation("Auto-unpinned note {NoteId} (oldest) when pinning note {NewNoteId} by {User}",
|
||||
oldestPinned.Id, noteId, userEmail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle the pin status
|
||||
note.IsPinned = !note.IsPinned;
|
||||
note.UpdatedAt = now;
|
||||
note.LastModifiedBy = userEmail;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Note {NoteId} {Action} by {User}", noteId,
|
||||
note.IsPinned ? "pinned" : "unpinned", userEmail);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Note>> GetDeletedNotesAsync()
|
||||
{
|
||||
return await _context.Notes
|
||||
.AsNoTracking()
|
||||
.Where(n => n.IsDeleted)
|
||||
.OrderBy(n => n.Title.StartsWith("@") ? 1 : 0) // Non-page notes first (0), page notes last (1)
|
||||
.ThenByDescending(n => n.UpdatedAt) // Within each group, order by most recently updated
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task RestoreNoteAsync(int noteId)
|
||||
{
|
||||
var note = await _context.Notes
|
||||
.FirstOrDefaultAsync(n => n.Id == noteId);
|
||||
|
||||
if (note == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Note with ID {noteId} not found.");
|
||||
}
|
||||
|
||||
if (!note.IsDeleted)
|
||||
{
|
||||
throw new InvalidOperationException($"Note with ID {noteId} is not deleted.");
|
||||
}
|
||||
|
||||
var userEmail = GetCurrentUserEmail();
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Create restore history entry
|
||||
var history = new NoteHistory
|
||||
{
|
||||
NoteId = note.Id,
|
||||
Title = note.Title,
|
||||
Content = note.Content,
|
||||
ModifiedBy = userEmail,
|
||||
ModifiedAt = now,
|
||||
ChangeType = "Restored"
|
||||
};
|
||||
|
||||
_context.NoteHistories.Add(history);
|
||||
|
||||
// Restore the note
|
||||
note.IsDeleted = false;
|
||||
note.UpdatedAt = now;
|
||||
note.LastModifiedBy = userEmail;
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("Note restored: {NoteId} by {User}", noteId, userEmail);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user