snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
This commit is contained in:
115
apps/dotcom/zero-cache/migrations/040_add_comments.sql
Normal file
115
apps/dotcom/zero-cache/migrations/040_add_comments.sql
Normal file
@@ -0,0 +1,115 @@
|
||||
-- Comments for app files, written by the file's Durable Object from the room's comment records
|
||||
-- (see TLComment / TLCommentThread in tlschema). Postgres is the sole durable store for comment
|
||||
-- records: the columns collectively carry every record field, so the Durable Object can rebuild
|
||||
-- its room's comment records from rows alone on cold start. `lastChangedClock` preserves each
|
||||
-- record's sync clock across reloads and guards upserts against no-op replays (an update that
|
||||
-- doesn't advance the clock is skipped, producing no WAL entry for Zero). `anchor`, `meta`,
|
||||
-- `resolvedBy`, `editedAt`, and `lastChangedClock` are persistence/rehydration columns not
|
||||
-- declared in the Zero schema, so they never reach clients.
|
||||
|
||||
CREATE TABLE comment_thread (
|
||||
"id" VARCHAR PRIMARY KEY,
|
||||
"fileId" VARCHAR NOT NULL,
|
||||
"pageId" VARCHAR NOT NULL,
|
||||
-- the thread's TLCommentAnchor (discriminated union) as JSON — canonical, used to rebuild the record
|
||||
"anchor" JSONB NOT NULL,
|
||||
-- denormalized from anchor for queries; only set for shape-anchored threads
|
||||
"shapeId" VARCHAR,
|
||||
"resolvedAt" BIGINT,
|
||||
"resolvedBy" VARCHAR,
|
||||
-- soft-deletion flag (see TLCommentThread.isDeleted): threads are never hard-deleted by
|
||||
-- clients, so the row (and its comments) stays for recovery while room loads and Zero
|
||||
-- queries filter it out
|
||||
"isDeleted" BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
-- no FK to "user": deleting a user must not cascade-delete threads (and thereby other
|
||||
-- authors' comments); the comment table's authorId FK handles per-author cleanup
|
||||
"createdBy" VARCHAR NOT NULL,
|
||||
"createdAt" BIGINT NOT NULL,
|
||||
"meta" JSONB NOT NULL,
|
||||
"lastChangedClock" BIGINT NOT NULL,
|
||||
CONSTRAINT comment_thread_file_id_fkey FOREIGN KEY ("fileId") REFERENCES public."file"("id") ON DELETE CASCADE,
|
||||
-- resolvedAt/resolvedBy encode one nullable `resolved` record field, so they must be set or
|
||||
-- null together; rehydration (rowToThreadRecord) relies on resolvedBy being non-null whenever
|
||||
-- resolvedAt is
|
||||
CONSTRAINT comment_thread_resolved_check CHECK (("resolvedAt" IS NULL) = ("resolvedBy" IS NULL))
|
||||
);
|
||||
|
||||
CREATE INDEX comment_thread_file_id_idx ON comment_thread("fileId");
|
||||
|
||||
CREATE TABLE comment (
|
||||
"id" VARCHAR PRIMARY KEY,
|
||||
"fileId" VARCHAR NOT NULL,
|
||||
"threadId" VARCHAR NOT NULL,
|
||||
"pageId" VARCHAR NOT NULL,
|
||||
"authorId" VARCHAR NOT NULL,
|
||||
-- author display fields, denormalized from the user table by the triggers below (same
|
||||
-- pattern as file."ownerName") — joining the user row would sync private fields to readers
|
||||
"authorName" VARCHAR DEFAULT '' NOT NULL,
|
||||
"authorColor" VARCHAR DEFAULT '' NOT NULL,
|
||||
"authorAvatar" VARCHAR DEFAULT '' NOT NULL,
|
||||
"body" JSONB NOT NULL,
|
||||
"createdAt" BIGINT NOT NULL,
|
||||
-- null until first edited (exact record field; updatedAt below is the derived sort key)
|
||||
"editedAt" BIGINT,
|
||||
-- soft-deletion flag (see TLComment.isDeleted): same model as the thread's — the row stays
|
||||
-- for recovery while room loads and Zero queries filter it out
|
||||
"isDeleted" BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
"updatedAt" BIGINT NOT NULL,
|
||||
"meta" JSONB NOT NULL,
|
||||
"lastChangedClock" BIGINT NOT NULL,
|
||||
CONSTRAINT comment_file_id_fkey FOREIGN KEY ("fileId") REFERENCES public."file"("id") ON DELETE CASCADE,
|
||||
CONSTRAINT comment_author_id_fkey FOREIGN KEY ("authorId") REFERENCES public."user"("id") ON DELETE CASCADE,
|
||||
CONSTRAINT comment_thread_id_fkey FOREIGN KEY ("threadId") REFERENCES public."comment_thread"("id") ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX comment_file_id_idx ON comment("fileId");
|
||||
CREATE INDEX comment_thread_id_idx ON comment("threadId");
|
||||
CREATE INDEX comment_author_id_idx ON comment("authorId");
|
||||
|
||||
-- Stamp author details on insert — the Durable Object only knows authorId. BEFORE INSERT
|
||||
-- keeps it a single write with no extra WAL entry for Zero.
|
||||
CREATE OR REPLACE FUNCTION set_comment_author_details() RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
author_name VARCHAR;
|
||||
author_color VARCHAR;
|
||||
author_avatar VARCHAR;
|
||||
BEGIN
|
||||
SELECT u."name", u."color", u."avatar" INTO author_name, author_color, author_avatar
|
||||
FROM public."user" u
|
||||
WHERE u."id" = NEW."authorId";
|
||||
-- a missing user row keeps the defaults, so the insert still fails its author FK check
|
||||
-- (see isCommentAuthorFkViolation) rather than erroring here
|
||||
IF FOUND THEN
|
||||
NEW."authorName" := author_name;
|
||||
NEW."authorColor" := author_color;
|
||||
NEW."authorAvatar" := author_avatar;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER "set_comment_author_details_trigger"
|
||||
BEFORE INSERT OR UPDATE OF "authorId" ON comment
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_comment_author_details();
|
||||
|
||||
-- Propagate user renames, recolors, and avatar changes to their existing comments.
|
||||
CREATE OR REPLACE FUNCTION update_comment_author_details() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
UPDATE comment
|
||||
SET "authorName" = NEW."name",
|
||||
"authorColor" = NEW."color",
|
||||
"authorAvatar" = NEW."avatar"
|
||||
WHERE "authorId" = NEW."id";
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER "update_comment_author_details_trigger"
|
||||
AFTER UPDATE OF "name", "color", "avatar" ON public."user"
|
||||
FOR EACH ROW
|
||||
WHEN (OLD."name" IS DISTINCT FROM NEW."name" OR OLD."color" IS DISTINCT FROM NEW."color" OR OLD."avatar" IS DISTINCT FROM NEW."avatar")
|
||||
EXECUTE FUNCTION update_comment_author_details();
|
||||
|
||||
ALTER PUBLICATION zero_data ADD TABLE public."comment_thread";
|
||||
ALTER PUBLICATION zero_data ADD TABLE public."comment";
|
||||
Reference in New Issue
Block a user