Skip to content

feat: prototype editor asset library (NES-1614)#9102

Draft
edmonday wants to merge 6 commits into
mainfrom
04-28-ES-feat-editor-asset-library-personal
Draft

feat: prototype editor asset library (NES-1614)#9102
edmonday wants to merge 6 commits into
mainfrom
04-28-ES-feat-editor-asset-library-personal

Conversation

@edmonday
Copy link
Copy Markdown
Contributor

@edmonday edmonday commented Apr 29, 2026

Closes NES-1614.

Summary

Surfaces a user's previously uploaded and AI-generated images directly inside the editor's image picker, and previously uploaded Mux videos inside the editor's video picker, so creators can re-pick assets they've already produced instead of re-uploading or re-prompting.

Three grids appear in the existing tabs:

  • Image picker → Custom tab — "Your uploads" grid below the file/URL inputs.
  • Image picker → AI tab — "Your generations" grid below the prompt input.
  • Video picker → Upload tab — "Your uploads" grid below the Mux upload dropzone.

Unsplash and the YouTube / shared video library are unaffected. There is no standalone "Library" tab.

Approach

The original plan's Block-table-as-source-of-truth approach was abandoned during prototyping after discovering Block.src is overwritten in place by imageBlockUpdate (so it can't carry history). Pivot documented in the plan file.

The implemented approach uses the existing per-asset tables (already independent of Block, persisting across replacements) as the backing data:

Images (CloudflareImage)

  • New nullable isAi boolean column on CloudflareImage. Populated by mutations going forward; existing 38k rows stay NULL (no backfill, no assumptions). Custom tab queries isAi: false, AI tab queries isAi: true.
  • getMyCloudflareImages refactored from offset/limit flat-array into a Pothos prismaConnection with (first, after, isAi) args — Relay cursor pagination.
  • Frontend grid uses fetchMore with a "Load More" button (PAGE_SIZE = 9). Apollo cache uses relayStylePagination(['isAi']).
  • Refetch fires on upload/URL paste/AI generation via refetchQueries: ['GetMyCloudflareImages']. With relayStylePagination's default merge, refetch resets the cache to the first page so the new image appears at the top.

Videos (MuxVideo)

  • getMyMuxVideos refactored from offset/limit flat-array into a Pothos prismaConnection with (first, after) args — Relay cursor pagination. Filtered to user-owned videos that are readyToStream: true with non-null playbackId, so only selectable thumbnails appear.
  • Frontend grid uses fetchMore with a "Load More" button (PAGE_SIZE = 9). Apollo cache uses relayStylePagination().
  • Refetch fires when Mux finishes processing — MuxVideoUploadProvider calls client.refetchQueries({ include: ['GetMyMuxVideos'] }) from the polling-completion path, so a freshly uploaded video appears at the top of the grid as soon as it's playable.

Behavior trade-offs (intentional, prototype-grade)

  • Refetch resets the paginated tail. If the user clicked Load More then uploads, they're back to a 3×3 with Load More. The new asset is at the top; older paginated rows can be re-fetched. Documented in conversation; can swap to a custom merge function later if real usage warrants it.
  • Existing CloudflareImage rows are NULL-tagged and invisible in both grids. Clean slate from now on. No backfill since we can't reliably guess which were AI-generated.
  • Mux videos still processing don't appear in the grid. The upload UI shows in-flight state in the dropzone; the new video surfaces in the grid via the polling-completion refetch once it's readyToStream.
  • Pagination tied to mutation usage. Cursor approach prevents the "+1 shift" bug that offset would have on inserts.

Files of interest

  • apis/api-media/src/schema/cloudflare/image/image.ts — connection resolver, isAi set in upload/URL/AI mutations.
  • apis/api-media/src/schema/mux/video/video.tsgetMyMuxVideos connection resolver.
  • libs/prisma/media/db/schema.prismaCloudflareImage.isAi column.
  • apps/journeys-admin/src/components/Editor/Slider/Settings/Drawer/ImageBlockEditor/MyCloudflareImagesGrid/ — image grid component.
  • apps/journeys-admin/src/components/Editor/Slider/Settings/Drawer/VideoLibrary/VideoFromMux/MyMuxVideosGrid/ — Mux video grid component.
  • apps/journeys-admin/src/components/MuxVideoUploadProvider/MuxVideoUploadProvider.tsx — refetches GetMyMuxVideos after polling completes.
  • apps/journeys-admin/src/libs/apolloClient/cache.tsrelayStylePagination policies.
  • docs/plans/2026-04-28-001-feat-editor-asset-library-plan.md — plan with the approach pivot at top.

Testing

  • apis/api-media/src/schema/cloudflare/image/image.spec.ts updated to cover the image connection shape and isAi filter.
  • apis/api-media/src/schema/mux/video/video.spec.ts updated to cover the Mux connection shape and the user / readyToStream / playbackId filter.
  • TypeScript compile clean across api-media and journeys-admin.
  • Manual smoke: upload via Custom → tile appears in image "Your uploads"; generate via AI → tile appears in "Your generations"; upload via Video → Upload → tile appears in video "Your uploads" once Mux finishes processing; Load More paginates; tabs render independent lists.

⚠️ Pre-merge review

getMyCloudflareImages and getMyMuxVideos both changed return type from [T!]! to a Connection. Internal grep shows no callers in this monorepo (only the resolvers, their specs, and auto-generated types — confirmed across JesusFilm/core and JesusFilm/forge for getMyCloudflareImages), but both fields are federated through api-gateway and could have external consumers (mobile, partner integrations) that aren't visible here.

Please request review from @MichaelAllisonJF (original author of getMyCloudflareImages, PR #1301) before merging to confirm zero external blast radius. Same caution applies to getMyMuxVideos.

Post-Deploy Monitoring & Validation

  • Watch for GraphQL errors on getMyCloudflareImages or getMyMuxVideos queries with offset / limit args (would indicate an external consumer was relying on the old shape).
  • Healthy signal — editor's image picker shows "Your uploads" / "Your generations" and the video picker's Upload tab shows "Your uploads" with no console errors.
  • Failure trigger — spike in GraphQL validation errors for either field after deploy → roll back or restore the legacy field shape via deprecation.
  • Validation window — 24h post-deploy.

Out of scope

  • Distinguishing AI vs upload for existing 38k NULL CloudflareImage rows (not done; flagged for future).
  • Unsplash "previously selected" history (would need new tracking table).
  • YouTube / shared video library "previously selected" history.
  • Pothos nx generate-graphql api-media is broken pre-existing (circular import); api-media schema regen worked via direct invocation. Worth a separate ticket.

🤖 Generated with Claude Code

@linear
Copy link
Copy Markdown

linear Bot commented Apr 29, 2026

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 66172e38-99f3-4364-b5d1-4c19a5773b54

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 04-28-ES-feat-editor-asset-library-personal

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

Fails
🚫 Please request a reviewer for this PR.
Warnings
⚠️ ❗ Big PR (1568 changes)

(change count - 1568): Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.

Generated by 🚫 dangerJS against 07d4d09

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Apr 29, 2026

View your CI Pipeline Execution ↗ for commit 07d4d09

Command Status Duration Result
nx run-many --target=vercel-alias --projects=jo... ✅ Succeeded 2s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 10s View ↗
nx run-many --target=deploy --projects=journeys... ✅ Succeeded 2m 57s View ↗
nx run-many --target=vercel-alias --projects=watch ✅ Succeeded 2s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 4s View ↗
nx run-many --target=deploy --projects=watch ✅ Succeeded 2m 12s View ↗
nx run-many --target=vercel-alias --projects=re... ✅ Succeeded 2s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 6s View ↗
Additional runs (13) ✅ Succeeded ... View ↗

☁️ Nx Cloud last updated this comment at 2026-04-30 05:22:41 UTC

@github-actions github-actions Bot temporarily deployed to Preview - watch April 29, 2026 03:50 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - resources April 29, 2026 03:50 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - videos-admin April 29, 2026 03:50 Inactive
@github-actions github-actions Bot had a problem deploying to Preview - journeys April 29, 2026 03:50 Failure
@github-actions github-actions Bot temporarily deployed to Preview - player April 29, 2026 03:50 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - short-links April 29, 2026 03:50 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin April 29, 2026 03:50 Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
player ✅ Ready player preview Thu Apr 30 17:20:27 NZST 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
short-links ✅ Ready short-links preview Thu Apr 30 17:20:25 NZST 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
videos-admin ✅ Ready videos-admin preview Thu Apr 30 17:21:12 NZST 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
resources ✅ Ready resources preview Thu Apr 30 17:21:39 NZST 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
watch ✅ Ready watch preview Thu Apr 30 17:21:53 NZST 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 29, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys-admin ✅ Ready journeys-admin preview Thu Apr 30 17:22:41 NZST 2026

@edmonday edmonday self-assigned this Apr 29, 2026
edmonday and others added 3 commits April 30, 2026 01:03
…e picker

Surface CloudflareImage history per source tab so previously uploaded and
AI-generated images can be re-picked. Backed by the existing getMyCloudflareImages
query in api-media. Sidesteps the Block.src overwrite issue documented in the
plan pivot.

Also: ignore .worktrees in git and nx project graph.
Adds a nullable isAi boolean to CloudflareImage so the editor's image picker
can split user uploads from AI generations into separate grids.

- New isAi column on CloudflareImage (nullable). Existing 38k rows stay NULL
  and are excluded from both grids. Upload and URL mutations set isAi: false;
  the AI-prompt mutation sets isAi: true. No backfill — we can't reliably
  guess which existing rows were AI-generated.
- getMyCloudflareImages takes an optional isAi: Boolean arg to filter. The
  resolver shape (offset, limit) and return type ([CloudflareImage!]!) are
  unchanged — additive, non-breaking.
- Editor: MyCloudflareImagesGrid renders a 3x3 grid with Load More.
  Custom tab shows isAi: false; AI tab shows isAi: true. Refetch on
  upload/AI generation surfaces new images at the top.
…m-to-select preview

Surfaces a user's previously uploaded Mux videos in the editor's video upload
tab so creators can re-pick a previous upload instead of re-uploading. Clicking
a tile opens VideoDetails with a Select button to confirm — the in-flight
upload is no longer silently cancelled by an accidental gallery click.

- New MyMuxVideosGrid component (offset/limit pagination, Load More via
  length-equals-PAGE_SIZE heuristic). Mounted under AddByFile in VideoFromMux.
- getMyMuxVideos resolver: keeps original [MuxVideo!]! shape and offset/limit
  args, adds deterministic orderBy [createdAt desc, id desc] required for
  reliable offset pagination. Filtering for ready-to-stream videos lives on
  the frontend, keeping the API generic.
- MuxDetails: accepts optional id + playbackId, renders a Select button only
  when activeVideoBlock is null (gallery mode). Threading playbackId through
  VideoDetails avoids a redundant fetch since the gallery already has it.
  Cleanup also calls dispose() on the video.js player so a later preview can
  attach to the same playbackId without DOM-race errors.
- VideoLibrary.onSelect closes the outer drawer when shouldCloseDrawer is true,
  so picking the same video as the active block still navigates back to
  Properties.
- MuxVideoUploadProvider refetches GetMyMuxVideos after polling completes so
  freshly-ready uploads appear in the gallery.
- getMyCloudflareImages: revert from Connection to keep [CloudflareImage!]!
  with offset/limit/isAi args (additive only, non-breaking). Apollo cache uses
  offsetLimitPagination(['isAi']).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@edmonday edmonday force-pushed the 04-28-ES-feat-editor-asset-library-personal branch from b090e65 to 07d4d09 Compare April 30, 2026 05:16
@github-actions github-actions Bot temporarily deployed to Preview - journeys April 30, 2026 05:18 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - short-links April 30, 2026 05:18 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - player April 30, 2026 05:18 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - videos-admin April 30, 2026 05:18 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - journeys-admin April 30, 2026 05:18 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - resources April 30, 2026 05:18 Inactive
@github-actions github-actions Bot temporarily deployed to Preview - watch April 30, 2026 05:18 Inactive
@github-actions
Copy link
Copy Markdown
Contributor

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys ✅ Ready journeys preview Thu Apr 30 17:20:52 NZST 2026

@blacksmith-sh
Copy link
Copy Markdown
Contributor

blacksmith-sh Bot commented Apr 30, 2026

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
segmind › createImageBySegmindPrompt/should return cloudflare image View Logs

Fix in Cursor

edmonday and others added 3 commits May 1, 2026 00:55
Adds a 2026-05-01 dated section documenting what shipped in PR #9102
(per-tab history grids for Custom uploads, AI generations, and Mux
uploads — personal scope only) and laying out v1.1 for team-shared
visibility.

v1.1 highlights:
- Additive optional teamId arg on getMyMuxVideos and getMyCloudflareImages.
- Direct Prisma cross-domain lookup (api-media imports prisma-journeys)
  for membership precheck and teammate userId list. Same precedent already
  used by the user-delete flow.
- No schema/migration changes to MuxVideo or CloudflareImage. Team
  affiliation is computed dynamically from current UserTeam rows.
- Account deletion verified: preserves media assets, deleted users
  auto-vanish from team galleries via the userId IN (...) filter.

Also flags the rejected Connection-style refactor (prototyped + reverted)
so it doesn't get re-introduced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Frame the plan as chronologically layered; mark Enhancement
  Summary onward as historical context for the rejected
  Relay-connection / imageBlocksConnection design.
- Drop quick-start (Template Customization) flow from scope per
  Lyuba's feedback; revisit later if needed.
- Add Production rollout section: gate the three grid mount points
  (Custom tab, AI tab, VideoFromMux) behind the mediaLibrary
  LaunchDarkly flag. isAi writes and orderBy correctness changes
  remain ungated.
- Note that v1.1 team-shared grids inherit the same flag.
Revise the v1.1 team-shared visibility section to persist a home
team on each CloudflareImage / MuxVideo at upload time (derived
from journey.teamId), instead of computing visibility from current
UserTeam membership.

Why: assets now stay with their team after the uploader leaves,
and don't follow the uploader into a new team.

Changes:
- Add nullable teamId column + index to both models.
- Upload mutations gain a journeyId arg and write teamId from
  journey.teamId after the existing journey-edit auth check.
- Read path simplifies: 'Team uploads' filters on assets.teamId;
  drops the cross-domain teammate-list fetch.
- Existing 38k+ rows stay NULL teamId — invisible to all team
  grids, visible only to original uploader's personal grid.
- Membership precheck retained for the read path.
- Tests, edge cases, performance, and risk sections updated to
  match.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant