"""note_user_state: a recipient's own pin, archive and place for a shared note Revision ID: 0036 Revises: 0035 Create Date: 2026-10-07 Pin, archive and board order are personal organization, and until now they were columns on `notes`, so on a shared note they could only ever mean the owner's (#5176). This table holds them for everyone else: one row per (note, person it is shared with) who has pinned, archived or moved it. The owner keeps the note's own columns; a recipient with no row sees the note unpinned, unarchived, in the owner's order. `sync_revision` is stamped by the same `ts_set_sync_revision()` trigger the notes and labels use (0015), so a recipient's change reaches their own devices on the one cursor they already page by, and never moves the note on anyone else's. ## Downgrade Drops the table. Recipients lose their own pins and archives; the notes are untouched. """ import sqlalchemy as sa from alembic import op from sqlalchemy.dialects.postgresql import UUID revision = "0036" down_revision = "0035" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "note_user_state", sa.Column("note_id", UUID(as_uuid=True), sa.ForeignKey("notes.id", ondelete="CASCADE"), nullable=False), sa.Column("user_id", UUID(as_uuid=True), sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=False), sa.Column("pinned", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("archived", sa.Boolean(), nullable=False, server_default=sa.false()), sa.Column("position", sa.Integer(), nullable=True), sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()), sa.Column("sync_revision", sa.BigInteger(), nullable=True), sa.PrimaryKeyConstraint("note_id", "user_id"), ) op.create_index("ix_note_user_state_user_revision", "note_user_state", ["user_id", "sync_revision"]) op.execute( "CREATE TRIGGER trg_note_user_state_sync_revision BEFORE INSERT OR UPDATE ON note_user_state " "FOR EACH ROW EXECUTE PROCEDURE ts_set_sync_revision()" ) def downgrade() -> None: op.execute("DROP TRIGGER IF EXISTS trg_note_user_state_sync_revision ON note_user_state") op.drop_index("ix_note_user_state_user_revision", table_name="note_user_state") op.drop_table("note_user_state")