mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-27 17:56:25 +02:00
feat: add archived column to notifications and implement archiving functionality
- Introduced an archived boolean column in the notifications table to allow users to archive inbox items without deletion. - Updated Notification model to include the archived field with default value. - Added ArchiveRequest and ArchiveResponse models for handling archive/unarchive operations. - Implemented API endpoint to archive or unarchive notifications, ensuring real-time updates with Electric SQL. - Enhanced InboxSidebar to filter and display archived notifications appropriately.
This commit is contained in:
parent
93aa1dcf3c
commit
22b2d6e400
8 changed files with 178 additions and 39 deletions
|
|
@ -0,0 +1,51 @@
|
|||
"""Add archived column to notifications table
|
||||
|
||||
Revision ID: 73
|
||||
Revises: 72
|
||||
|
||||
Adds an archived boolean column to the notifications table to allow users
|
||||
to archive inbox items without deleting them.
|
||||
|
||||
NOTE: Electric SQL automatically picks up schema changes when REPLICA IDENTITY FULL
|
||||
is set (which was done in migration 66). We re-affirm it here to ensure replication
|
||||
continues to work after adding the new column.
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "73"
|
||||
down_revision: str | None = "72"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Add archived column to notifications table."""
|
||||
# Add the archived column with a default value
|
||||
op.execute(
|
||||
"""
|
||||
ALTER TABLE notifications
|
||||
ADD COLUMN IF NOT EXISTS archived BOOLEAN NOT NULL DEFAULT FALSE;
|
||||
"""
|
||||
)
|
||||
|
||||
# Create index for archived column
|
||||
op.execute(
|
||||
"CREATE INDEX IF NOT EXISTS ix_notifications_archived ON notifications (archived);"
|
||||
)
|
||||
|
||||
# Re-affirm REPLICA IDENTITY FULL for Electric SQL after schema change
|
||||
# This ensures Electric SQL continues to replicate all columns including the new one
|
||||
op.execute("ALTER TABLE notifications REPLICA IDENTITY FULL;")
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Remove archived column from notifications table."""
|
||||
op.execute("DROP INDEX IF EXISTS ix_notifications_archived;")
|
||||
op.execute("ALTER TABLE notifications DROP COLUMN IF EXISTS archived;")
|
||||
# Re-affirm REPLICA IDENTITY FULL after removing the column
|
||||
op.execute("ALTER TABLE notifications REPLICA IDENTITY FULL;")
|
||||
|
||||
|
|
@ -784,6 +784,9 @@ class Notification(BaseModel, TimestampMixin):
|
|||
read = Column(
|
||||
Boolean, nullable=False, default=False, server_default=text("false"), index=True
|
||||
)
|
||||
archived = Column(
|
||||
Boolean, nullable=False, default=False, server_default=text("false"), index=True
|
||||
)
|
||||
notification_metadata = Column("metadata", JSONB, nullable=True, default={})
|
||||
updated_at = Column(
|
||||
TIMESTAMP(timezone=True),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,19 @@ class MarkAllReadResponse(BaseModel):
|
|||
updated_count: int
|
||||
|
||||
|
||||
class ArchiveRequest(BaseModel):
|
||||
"""Request body for archive/unarchive operations."""
|
||||
|
||||
archived: bool
|
||||
|
||||
|
||||
class ArchiveResponse(BaseModel):
|
||||
"""Response for archive operations."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
|
||||
|
||||
@router.patch("/{notification_id}/read", response_model=MarkReadResponse)
|
||||
async def mark_notification_as_read(
|
||||
notification_id: int,
|
||||
|
|
@ -100,3 +113,41 @@ async def mark_all_notifications_as_read(
|
|||
message=f"Marked {updated_count} notification(s) as read",
|
||||
updated_count=updated_count,
|
||||
)
|
||||
|
||||
|
||||
@router.patch("/{notification_id}/archive", response_model=ArchiveResponse)
|
||||
async def archive_notification(
|
||||
notification_id: int,
|
||||
request: ArchiveRequest,
|
||||
user: User = Depends(current_active_user),
|
||||
session: AsyncSession = Depends(get_async_session),
|
||||
) -> ArchiveResponse:
|
||||
"""
|
||||
Archive or unarchive a notification.
|
||||
|
||||
Electric SQL will automatically sync this change to all connected clients.
|
||||
"""
|
||||
# Verify the notification belongs to the user
|
||||
result = await session.execute(
|
||||
select(Notification).where(
|
||||
Notification.id == notification_id,
|
||||
Notification.user_id == user.id,
|
||||
)
|
||||
)
|
||||
notification = result.scalar_one_or_none()
|
||||
|
||||
if not notification:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Notification not found",
|
||||
)
|
||||
|
||||
# Update the notification
|
||||
notification.archived = request.archived
|
||||
await session.commit()
|
||||
|
||||
action = "archived" if request.archived else "unarchived"
|
||||
return ArchiveResponse(
|
||||
success=True,
|
||||
message=f"Notification {action}",
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue