Uh oh!
There was an error while loading. Please reload this page.
feat(composer): edit shared posts and share-to-squad in the new composer - #6422
Open
tsahimatsliah wants to merge 37 commits into
Open
feat(composer): edit shared posts and share-to-squad in the new composer#6422tsahimatsliah wants to merge 37 commits into
tsahimatsliah wants to merge 37 commits into
Conversation
The reported bug: on mobile a long comment overflowed with nowhere to scroll, so the text ended up hidden behind the virtual keyboard. Root cause was two-fold. `CommentMarkdownInput` drove `RichTextInput` with the default top toolbar, which renders no scroll container and no max-height, so the editor just grew. `CommentModal` then force-sized the form to the visual viewport by writing `style.height` during render — producing the literal "autopx" whenever the maths went negative. The composer is now the same inline box as the watercooler create-post composer, minus title, cover, post type and audience: bottom toolbar, one action bar, and a height capped against `visualViewport` (the part that survives the keyboard) with the body scrolling inside it. Same component on every viewport, so `CommentModal` and the mobile/desktop fork are gone. The mobile floating bar now opens the in-page composer through a window event instead of mounting its own copy inside the fixed footer. Several fixes landed in shared primitives and so reach every composer, including create post: - RichTextInput: rich and markdown modes share one tree, so switching no longer remounts the avatar (which refetched the image and blinked) or the action bar. Heights now match between modes, and the scroll offset and caret are preserved across the toggle. - RichTextToolbar: the overflow budget ignored dividers and row gaps, and the overflow button lived inside the `overflow-hidden` group it exists to protect, so it got sliced. Also swapped to the three-dots icon and kept it beside the formatting items. - Drawer: closing on any click whose target was outside the panel also caught portaled dropdowns, whose synthetic clicks bubble up the React tree. Picking a post type, audience, poll duration or schedule tore the drawer down instead of running the action. Now only a backdrop hit closes it. - Switch: the label could not shrink, so long copy ran off the right edge on mobile. - ProseMirror no longer adds a second min-height inside the editor's own padding. - AudienceChip and the poll option input could not shrink either. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Swapping the rich editor for the textarea briefly shrinks the scroll container, so the browser clamps its offset to 0. Restoring from a later animation frame was too late in one direction: markdown to rich held, but rich to markdown always landed back at the top. Restore right after the textarea gets its real height, before paint, guarded by a flag so normal typing does not fight the user's own scrolling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Engineering review pass over the composer. No behaviour or visual change. - Stories move under stories/components/comments/, next to the existing CommentContainer story, and are retitled Components/Comments/*. The storybook AGENTS.md groups stories by atomic design, and a top-level `comments/` group also sorted outside preview.tsx's storySort order. Added the `autodocs` tag the same guide asks for. - The header label goes through Typography rather than a raw span with typo-* classes, per the components guide. - The composer form carries an aria-label, so it exposes role="form" to assistive tech. The spec queries that instead of a data-testid, which the components guide reserves for when no accessible query exists. - Drop three props left dead by removing CommentModal: `style`, `showUserAvatar` and `CommentClassName.markdownContainer`. Nothing passed them any more, and the comment composer always wants its avatar. - Flatten CommentInput's `className.input` nesting to `CommentClassName`. The wrapper object only existed to sit beside the `modal` key that went with the deleted modal branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The changed-file guard reports all strict errors in a touched file, not just newly introduced ones, so editing these files made their existing violations block CI. Fixed rather than suppressed; no behaviour change. - isSourcePublicSquad accepts an optional Source, which is what its body already assumes via optional chaining. - PostEngagements: useRef<NewCommentRef>(null) for a LegacyRef-compatible ref, and `false | value` unions replaced with undefined branches. - Drawer: container ref nullable, forwardRef render fn typed with ForwardedRef. The debounced close now forwards the event that triggered it instead of dropping it and calling onClose with undefined. - PostComments.modalParentSelector may return null, matching getCompanionWrapper; the call site already null-coalesced it. - CompanionDiscussion returns ReactElement | null, which it always did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mobile round of feedback on the composer surfaces: - On mobile the comment composer opens as a full-screen Drawer, the same way creating a post does — an inline box wedged into the page is too small to write in. It autofocuses so the keyboard opens ready to type. A new `fills` mode makes the composer fill the drawer instead of capping against the viewport, dropping the inline card treatment. - Full-screen drawers size themselves to the visual viewport (height and offsetTop), so bottom action bars sit above the virtual keyboard instead of underneath it. useVisualViewport now reports offsetTop and listens to viewport scroll for the iOS keyboard-pan case. - The create-post kind picker moves to its own row above the action bar on mobile (`stackToolbarLeading`). The bar's overflow menu only relocates formatting items, so the picker plus the icon actions overflowed the clipped group and rendered sliced. - Expand/collapse is hidden on mobile — the composer is already full-screen there, so the control was a no-op. - Drawer wrapper overrides use `!px-0 !pt-0` (matching NavDrawer): plain `p-0` lost to the drawer's default padding, so the composer paid padding twice, and killing padding-bottom outright would also have dropped the safe-area inset that `drawer-padding` provides. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Animated/transformed ancestors (the animate-composer-in wrapper) create a containing block for position: fixed, pinning the drawer mid-page with the page showing through. appendOnRoot portals it to the document root so it actually covers the screen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…er on mobile - The stacked leading row rendered in addition to the inline slot rather than instead of it, so mobile showed two kind pickers. The inline slot now yields when stackLeading is set. - On mobile the schedule calendar moves up beside the header's scheduled- posts clock, where scheduling already lives; the bottom bar keeps just the Post button. Desktop keeps both beside Post. Specs for this mobile round: toolbar leading-slot dedupe, the comment drawer (full-screen, portaled, autofocus, fills, padding override), keyboard-safe drawer sizing, backdrop-only close, header expand/schedule placement per breakpoint, and the composer fills mode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…loating bar on redesign - Queue an early imperative focus until the TipTap editor exists, so reply composers autofocus on desktop (the mount-time focus raced editor creation) - Move the More-formatting tooltip onto the trigger's tooltip prop; the Tooltip wrapper blurred the trigger on mouseup, insta-dismissing the menu - Extract useOpenPostCommentRequest and register it in PostDiscussionPanel too, so the mobile floating bar opens the composer on the redesigned post page, not only the classic one - Cover the full PR surface with tests: useVisualViewport, postComment event bridge, Switch wrap fix, RichTextInput avatar/mode/focus, FooterWrapper dispatch, PostPage integration, toolbar overflow menu Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scrolling inside the mobile composer drawer chained to the post page behind it, which visibly jumped and shifted. Apply the same body lock react-modal uses (hidden-scrollbar, ref-counted for stacked drawers) and contain overscroll on the drawer's own scroller; the composer's internal scrolling is untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
<html> is the page's actual scroll container, so the body-level lock never reached the viewport and the page behind the drawer kept scrolling. Verified against the live preview. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The drawer's safe-area bottom padding stacked with the action bar's own pb-5, reading as double spacing under the actions. The bar now absorbs the safe area itself (max(1.25rem, safe-area-inset-bottom)) and the drawer adds none, so the composer sits 20px from every edge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The header and avatar hugged the comment list's 16px guideline while the action bar carried 20px, so the drawer's edges did not match. In the drawer there is no list to line up with, so both join the 20px frame the action bar and create-post composer already use; inline keeps 16px. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Drawer: lock the page only for full-screen drawers, so context menus and pickers keep the page scrollable exactly as before; capture and restore the inline html overflow instead of deleting whatever another lock set - useVisualViewport: take an `enabled` flag and subscribe only when asked; non-full-screen drawers no longer re-render on every iOS keyboard scroll - RichTextInput: fill the editor by flexing rather than `height: 100%`, which resolved to auto where the box height comes from `minHeightClassName` and let the editable collapse away from clicks below the first line - CommentMarkdownInput: drop the inert `rows: 7` Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MyFeedPage › should request user feed timed out in test_webapp. Known flaky async feed race — passes locally on repeat and this branch does not touch the feed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Editing a shared link still opened the old /posts/[id]/edit page: the menu only routed freeform and welcome posts to SmartComposerModal, and the modal forced kind to 'text' whenever editPost was set, so it could not represent a share at all. - Derive the kind from editPost.type, prefill the commentary the way the old page did (a share with no commentary carries the shared post's own title, which is not the author's text), and seed the preview from the post rather than refetching it - Lock the URL while editing: only the commentary is editable, matching what the API accepts - Add the missing edit branch to submitLink, including the moderation path, which completes without a toast of its own - Route share posts to the modal; moderation items keep the page, which edits a pending submission the composer cannot represent - Delete CreateLiveRoomForm and CreatePoll, referenced by nothing Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Squad share triggers (ShareBar, DiscussionShareRow, Share modal) now open SmartComposerModal seeded with the post as the link preview and the squad preselected, replacing CreateSharedPostModal. The composer's link flow already covers internal shares (preview.id -> addPostToSquad, moderation, multi-squad via sharedPostId), so the old modal is deleted along with its lazy-modal registration. A new onPosted callback carries the ShareToSquad analytics event that the old modal's onSharedSuccessfully used to fire. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A composer opened pre-filled (share-to-squad, ?share= deep links) counted its own seed as unsaved changes, so closing it untouched asked to discard a draft the user never wrote. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comments explaining what the code already says, narrating fixed bugs, or restating test names are removed; the ones that survive state constraints the code cannot show (platform quirks, cross-file contracts, do-not-readd traps). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comments explaining what the code already says, narrating fixed bugs, or restating test names are removed; the ones that survive state constraints the code cannot show (platform quirks, cross-file contracts, do-not-readd traps). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-post-new-composer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- share-edit save now shows its in-flight state (isUpdatePostLoading joins isPosting) - the mobile composer drawer portals to the root, so opening it from the Share modal's drawer no longer traps it in a transformed ancestor - editing always targets the post's own source instead of falling back to the personal audience when the squad is no longer postable - a moderated share edit is no longer logged as a post creation - the share-edit dirty check trims like the seed comparison does - a locked link with no surviving shared post renders no empty preview card - CommentMarkdownInput skips the visualViewport subscription when it fills its container (the value was discarded) - the locked-link no-refetch spec now actually exercises the guard Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Live verification found two defects: a freeform post with no body (title + cover) could never save a title edit because submit demanded a body, and the desktop 'Add a comment' composer opened unfocused — the by-id focus helper races the lazy chunk and TipTap's async editor, so the composer now owns its focus through the queued autofocus path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CommentMarkdownInput passes enabled=!fills to useVisualViewport, so the mobile drawer stops re-rendering on iOS keyboard scroll for a value it discards - the toolbar overflow budget measures the rendered divider and the row's column-gap instead of hand-synced constants (jsdom keeps fallbacks) - the open comment composer owns its focus through the queued autofocus: the by-id helper races the lazy chunk and TipTap's async editor - a title-only freeform edit no longer demands a body to save Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-post-new-composer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-post-new-composer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t-post-new-composer
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 freeto 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.
Changes
Two remaining old-composer surfaces move to
SmartComposerModal: editing a shared post and sharing a post to a squad.Editing a share in the composer
Editing a shared link still opened the old
/posts/[id]/editpage, with the old top-toolbar editor, while every other composer surface had moved toSmartComposerModal.Two reasons, not one:
PostOptionButtononly routed Freeform and Welcome posts to the modal; everything else fell through torouter.push('<permalink>/edit').kind = 'text'whenevereditPostwas set — it had no way to represent a share.What changed:
editPost.typeinstead of hard-coded totext.editPost.sharedPostinstead of being refetched — there is no URL being typed for it to resolve.LinkForm isUrlLocked): only the commentary is editable, which is allupdateSquadPostaccepts. The URL field and the preview's remove button are dropped, and focus starts in the commentary.submitLinkgained the edit branch it never had — onlysubmitTexthandlededitPostId. It routes throughonUpdateSharePost, including the moderation path, which completes without a toast of its own (the plain update path already toasts fromusePostToSquad, so adding one there would double it).Share to squad through the composer
Clicking a squad avatar in
ShareBar,DiscussionShareRow, or the Share modal openedCreateSharedPostModal— the old editor. All three now openSmartComposerModalseeded for the share:preview={post},initialUrl={post.permalink}), so nothing is refetched and the composer's existing internal-share path applies:preview.id → addPostToSquad, moderation when the squad requires it, and multi-squad sharing viasharedPostId— a capability the old modal never had.initialSquadId, but the audience stays editable.onPostedcallback onSmartComposerModalcarries theShareToSquadanalytics event the old modal fired fromonSharedSuccessfully.CreateSharedPostModalis deleted along with its spec and itsLazyModalregistration;SocialSharerenders the composer inline for the same dep-cycle reason it rendered the old modal inline.What deliberately did not move
/posts/[id]/edit. That page edits a pending submission (useSourcePostModerationById), which is a different entity from a post — the composer cannot represent it.LiveRoomChatPanelstays as it is. It is a chat input, not a post composer: one line, send-on-enter, in a sidebar. A full-screen drawer and a bottom action bar would make it worse.ControlledMarkdownInput(profile bio) is a plain markdown field, not a composer.Dead code removed
CreateSharedPostModal,CreateLiveRoomForm, andCreatePollwere each left referenced by nothing (or only their own specs), so they are deleted rather than carried forward.Events
No new tracking events —
StartShareToSquad/ShareToSquadkeep firing from the same triggers.Experiment
No new experiments.
Preview domain
https://claude-edit-post-new-composer.preview.app.daily.dev