Repository navigation
Conversation
…ntract
- regenerate packages/components/schema.graphql against the deployed ported backend
(image/avatar/bannerImage fields are now String scalars, not { url } objects)
- update fragments to select image fields as scalars; drop { url } subselections
- update components/utils to consume image fields as strings (coalesce null -> undefined
where passed to string|undefined props)
- regenerate Relay __generated__ artifacts
- rebased onto origin/master
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XyuGcRJryi9hQiWRVXU7Js
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd # Conflicts: # packages/components/modules/profiles/web/ProfileMembers/MemberItem/index.tsx # packages/components/schema.graphql
Master's InviteMemberDialog queried image { url }; on this branch Profile.image
is a String scalar, so select it directly and use it as avatarUrl.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
README claimed a FileUploadProvider JSX wrapper; the store is a module-level zustand singleton, so no provider exists. Documented the shared-state implication for multiple targets and fixed the edges.map any cast. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
- Record ETags per chunk index as chunks complete (new recordChunkEtag store action); completedChunks was chunkIndex+1, which under 3-way parallel uploads mis-counted on out-of-order completion, and etags were only stored at the very end — a paused upload resumed with wrong slices and empty parts. - Resume now uploads exactly the chunks with missing ETags (non-contiguous safe) against their original presigned URLs. - Track initiatedAt/expiresIn from the initiate response; resuming past expiry aborts the stale upload (best effort) and transparently re-initiates. - Extract shared startUpload used by upload/retry/expired-resume. - Drop parentContentType/parentObjectId from initiate payloads — the API accepts parent_id only, so the fields were silently ignored; attaching is done via fileAttachToTarget. - Select id on FilesList file nodes (was masked by the removed any cast); remove leftover merge-conflict marker in ProfileMembers MemberItem. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
…paths chunkFile slicing, uploadChunks (out-of-order ETag indexing, retry/backoff, abort short-circuit), the upload store (recordChunkEtag sparse counting, pause/resume/retry transitions), and useChunkedUpload (initiate/complete payloads, sparse resume, expired-URL re-initiation, failed-state handling). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
…ume works Two bugs made in-session pause/resume unreachable from the UI (the jest tests exercised resumeUpload directly on a hand-crafted PAUSED state, masking both): - The catch in upload/resume/retry set status=FAILED on any thrown error, including the AbortController rejection a pause triggers — clobbering the PAUSED the store just set and firing onUploadError for a deliberate pause. A shared handleUploadError now leaves PAUSED/ABORTED untouched. - UploadingFileItem.handleResume called the store's resumeFile (PAUSED→PENDING) before resumeUpload, whose guard requires PAUSED — so resume no-op'd and the file stranded in PENDING. Removed the redundant call; the hook flips it. - resumeUpload now computes pending chunks before treating URL expiry as fatal: if every chunk already uploaded, it only re-issues the (URL-free) complete call instead of discarding the whole upload and re-uploading from byte zero. Added regression tests for both fixed behaviors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
…App design) Decouple the three concerns so the pieces can be mounted to match the Figma comment design while keeping today's behaviour as defaults: - useFileSelect: headless hidden-input select (accept/size/count validation), so the trigger is pluggable. - FileUploadTrigger: owns the upload logic and renders as a dropzone (default) or a compact paperclip button (as="button"). FileUpload stays as a back-compat dropzone wrapper. - FileThumbnail + FileChip: presentational 40x40 thumbnail (image or type-icon tile) and the compact bordered chip from the design. - UploadingFileItem / AttachedFileItem gain variant='chip'; AttachedFileItem gains editable (× remove on files the user can change). - FileUploadList gains variant (cards|chips), layout (stack|horizontal), and editable — cards/stack remain the defaults so nothing changes for existing callers. Wire comments to the design: CommentItem renders the chip list (editable while editing); CommentUpdate renders the paperclip trigger button. Tests: useFileSelect unit coverage; full components suite green (37). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
…ion bar
- AttachedFileItem and FileUploadTrigger now use the design-system IconButton
(which renders a spinner via isLoading) instead of raw MUI: the remove/delete
action shows isLoading={isDeletingFile}, the attach button isLoading while
attaching.
- Wire the comment editor's attach icon: SocialInput forwards a new optional
SocialUpsertActionsProps to its SocialUpsertActions slot, and CommentUpdate
passes a module-level CommentFilesUpsertActions (target-bound) whose
AttachmentIcon is the real FileUploadTrigger. Removes the standalone attach
button that used to sit below the input.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
- The attached-file preview used file.thumbnail, which the backend leaves null when easy_thumbnails can't generate one, and downloads used the raw `file` field (a host-less /media path the frontend origin can't serve). Query the absolute `url` field and use it: preview falls back to thumbnail || url so images always render, and download opens `url`. - FileChip max-width 264 -> 232px to match the design. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
Previously the uploading item decoded the source File (via createObjectURL) into an image preview. Even for a 40px tile the browser decodes the full-res bitmap, which is wasteful for large images. Show a generic type icon during upload; the real thumbnail arrives when FileAttachToTarget prependEdges the committed File node. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
Adds file attachment to CommentCreate, mirroring CommentUpdate. Because the comment does not exist at selection time, files upload immediately and their relay ids are held by a new useDeferredFileAttachments hook; on successful create they are attached to the new comment in one FileAttachToTarget mutation (the comment's FilesList_files connection already exists via the CommentItem response, so the prependEdge lands). The attach icon lives in a create-specific SocialUpsertActions bar and shows a loading state while uploads are in flight. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
The upload store is a global singleton, so every FileUploadList (one per comment) was rendering all in-flight uploads — a file attached during comment creation appeared on existing comments. Tag each upload with a scope: the auto-attach path scopes to its target id; the deferred (create) path scopes to a per-composer id. FileUploadList now shows only uploads matching its target, and CommentCreate renders a new target-less UploadingFilesList for its own scope. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
In a composer (e.g. new comment) an uploaded-but-not-yet-attached file kept showing its progress bar and a pause control, with no way to undo it. Once COMPLETED the chip now shows the file size instead of the bar (card hides FileProgress) and offers a remove control. Removing works correctly because the deferred hook now reads the completed uploads for its scope straight from the store at attach time (fileRelayId is persisted on completion), so a removed file is excluded from the attach; cleanup is scoped to the composer instead of clearing all completed uploads globally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0146oeiGsELS6QVpVUQHDQXd
Master refactored the comments module into a shared hook layer (silverlogic#392) and converted tailwind to MUI styled components (silverlogic#408), touching the same components this branch extends with file attachments. Resolved by taking master's structure and re-applying the attachment feature on top: - CommentCreate/CommentUpdate/CommentItem rebuilt on master's useCommentCreateForm / useCommentUpdateForm / useCommentItem hooks and styled components, keeping the file upsert actions, UploadingFilesList and FileUploadList. - useCommentCreateForm now surfaces the created comment id to onSuccess, which the deferred-attachment flow needs to attach uploads once the comment exists. This branch makes thumbnails resolve to a plain String, so master's `image/avatar { url }` selections and readers had to be adapted: AddContactToGroupItem's fragment plus three consumers new in master. Relay compiles and tsc is clean; the remaining lint output is pre-existing warnings only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013LkamMd6XMPeYNHbAV1DZ9
Conflict: master replaced the Cypress component tests with Playwright (silverlogic#427), deleting ProfileMembers/__tests__/Members.cy.tsx, which silverlogic#413 had edited for the string image contract. Kept the deletion; the mock updates follow separately.
silverlogic#413 moved image, bannerImage and avatar to plain String URLs, but mocks it didn't touch, or that master added after it branched (GroupChatEdit, Members.story), still returned { url } objects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019P1oyApi4BLS8iLfMEGm8b
silverlogic#413 was written before master moved to Vitest. The jest = vi shim covers jest.fn(), but Vitest only hoists vi.mock(), so the jest.mock() calls never replaced axios or uploadChunks and 14 tests failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019P1oyApi4BLS8iLfMEGm8b
2 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merges silverlogic#413 (
filesmodule: chunked multipart upload and attaching files toFilesInterfaceobjects) into the fork. It pairs with queplanta/baseapp-backend#1, which merges the backend side (silverlogic/baseapp-backend#442). Both upstream PRs are still open. whatplant needs them now to store and show iNaturalist observation photos.Commits
--no-ffmerge ofupstream/feature/baseapp_filesonto forkmaster(d650d09d). One conflict:masterreplaced the Cypress component tests with Playwright (Feature/playwright tests silverlogic/baseapp-frontend#427) and deletedProfileMembers/__tests__/Members.cy.tsx, which feat(files): multipart upload module + attach files to comments silverlogic/baseapp-frontend#413 had edited. The deletion is kept;Members.pw.tsreplaces that file.test: mock GraphQL image fields as URL strings. feat(files): multipart upload module + attach files to comments silverlogic/baseapp-frontend#413 changesimage,bannerImageandavatarto plainStringURLs. Mocks it didn't convert, or thatmasteradded after it branched (GroupChatEdit,Members.story), still returned{ url }objects. REST user mocks (avatar: { small }) and React Native styles are unchanged.test(files): port the files module tests from Jest to Vitest. feat(files): multipart upload module + attach files to comments silverlogic/baseapp-frontend#413 predates the Vitest switch. Vitest only hoistsvi.mock(), so itsjest.mock()calls never took effect and 14 tests failed.Test plan
relay-compilerinpackages/components: compiles against the merged schema.vitest runinpackages/components: 17 files, 102 tests passed (was 14 failed before commit 3).tsc --noEmitclean formodules/files. ESLint: no errors, plus 2 pre-existingno-consolewarnings in feat(files): multipart upload module + attach files to comments silverlogic/baseapp-frontend#413'suseFileDeleteLogic/useFileDownloadLogic.🤖 Generated with Claude Code
https://claude.ai/code/session_019P1oyApi4BLS8iLfMEGm8b