feat: post reshare enhancements

This commit is contained in:
Krispire Admin
2026-06-03 14:41:15 +05:30
parent 3f6b7b6636
commit 7f5b0fee11
9 changed files with 397 additions and 190 deletions
@@ -0,0 +1 @@
DROP TABLE IF EXISTS post_reshare_images;
@@ -0,0 +1,35 @@
-- Per-reshare background image overrides.
--
-- Previously every viewer of a reshare saw the canonical post_images row —
-- so when either the original author or any resharer "Updated background"
-- the change leaked across all timelines that referenced the post. This
-- table separates "the post's background" from "this user's reshare of
-- the post's background": authoring stays in post_images, resharing
-- stores its own snapshot here.
--
-- At reshare time we copy the post's current post_images into this table
-- so the resharer's feed renders identically to the author's. After that
-- moment the two are independent — the resharer's background lives only
-- here, the original author's lives only in post_images, and changes to
-- one never touch the other.
--
-- Cascade: the FK targets the (post_id, user_id) primary key on
-- post_reshares. Every path that removes a reshare relationship (the
-- author deletes the post, the resharer un-reshares, either user is
-- deleted) drops the post_reshares row, which cascades the overrides
-- away. The user's stated rule — "should not be in sync except deletion"
-- — is preserved end-to-end without any extra application code.
CREATE TABLE IF NOT EXISTS post_reshare_images (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
post_id UUID NOT NULL,
user_id UUID NOT NULL,
url TEXT NOT NULL,
position INT NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
FOREIGN KEY (post_id, user_id)
REFERENCES post_reshares (post_id, user_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS idx_post_reshare_images_lookup
ON post_reshare_images (post_id, user_id, position);
@@ -0,0 +1,12 @@
-- Reverting this requires materialising the new reshared posts back into
-- the post_reshares + post_reshare_images join model and then dropping
-- the column. Documented but not automated — run manually if a rollback
-- is ever actually needed.
--
-- Step 1: recreate the old tables.
-- Step 2: insert one row per reshare back into post_reshares.
-- Step 3: copy each reshare's post_images back into post_reshare_images.
-- Step 4: delete the materialised reshare rows from `posts`.
-- Step 5: drop the column.
ALTER TABLE posts DROP COLUMN IF EXISTS reshared_from_post_id;
@@ -0,0 +1,80 @@
-- Reshares become real `posts` rows.
--
-- Previously a reshare was a join-table relationship between (post, user)
-- that pointed at the original author's canonical row. Likes, comments,
-- and (most recently) background overrides all had to be plumbed through
-- a parallel "per-resharer" path to avoid leaking across timelines. The
-- next set of social features (likes, comments) would multiply that
-- complexity again.
--
-- This migration collapses that model: every reshare is its own row in
-- `posts`, owned by the resharer, with a `reshared_from_post_id` pointer
-- back to the canonical original. Likes and comments will naturally hang
-- off post.id and diverge for free; backgrounds are already per-row in
-- post_images so the per-reshare override table goes away.
--
-- Deletion still cascades: the FK uses ON DELETE CASCADE on
-- reshared_from_post_id, so an author deleting their post nukes every
-- reshare of it (including their post_images via the existing FK).
-- 1. New self-referential column. Nullable: NULL on canonical posts,
-- set on reshares.
ALTER TABLE posts
ADD COLUMN IF NOT EXISTS reshared_from_post_id UUID
REFERENCES posts(id) ON DELETE CASCADE;
CREATE INDEX IF NOT EXISTS idx_posts_reshared_from
ON posts (reshared_from_post_id)
WHERE reshared_from_post_id IS NOT NULL;
-- Composite index used by ReshareStatesFor — given a viewer + a batch of
-- canonical post ids, "which of these has the viewer reshared?".
CREATE INDEX IF NOT EXISTS idx_posts_user_reshared_from
ON posts (user_id, reshared_from_post_id)
WHERE reshared_from_post_id IS NOT NULL;
-- 2. Backfill: each row in post_reshares becomes a real `posts` row,
-- copying the canonical post's textual fields and the timestamp the
-- reshare originally happened. The new row is owned by the resharer.
--
-- Skipped if post_reshares doesn't exist (clean install) — the
-- DO block uses to_regclass so a fresh database doesn't error.
DO $$
BEGIN
IF to_regclass('public.post_reshares') IS NOT NULL THEN
INSERT INTO posts (
id, user_id, caption, source_url, source_title,
created_at, updated_at, reshared_from_post_id
)
SELECT
gen_random_uuid(),
r.user_id,
p.caption,
p.source_url,
p.source_title,
r.created_at,
r.created_at,
p.id
FROM post_reshares r
JOIN posts p ON p.id = r.post_id;
END IF;
END$$;
-- 3. Carry the per-resharer image overrides over. Each override row maps
-- to the newly-inserted post via (reshared_from_post_id, user_id).
DO $$
BEGIN
IF to_regclass('public.post_reshare_images') IS NOT NULL THEN
INSERT INTO post_images (post_id, url, position)
SELECT np.id, ri.url, ri.position
FROM post_reshare_images ri
JOIN posts np
ON np.reshared_from_post_id = ri.post_id
AND np.user_id = ri.user_id;
END IF;
END$$;
-- 4. Old tables are no longer referenced anywhere — drop them so the
-- schema mirrors the new model.
DROP TABLE IF EXISTS post_reshare_images;
DROP TABLE IF EXISTS post_reshares;