From e345935aacc98b787198ff87e3645751afc708c3 Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Sun, 29 Mar 2026 16:40:09 +0700 Subject: [PATCH] fix(reply modal): stop recentering on input --- .../__tests__/reply-modal.test.tsx | 30 +++++++++++++++++-- src/components/reply-modal/reply-modal.tsx | 13 +++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/components/reply-modal/__tests__/reply-modal.test.tsx b/src/components/reply-modal/__tests__/reply-modal.test.tsx index 7162b7ae..7f3f514b 100644 --- a/src/components/reply-modal/__tests__/reply-modal.test.tsx +++ b/src/components/reply-modal/__tests__/reply-modal.test.tsx @@ -39,6 +39,7 @@ const testState = vi.hoisted(() => ({ setAccountMock: vi.fn(), setPublishReplyOptionsMock: vi.fn(), springStartMock: vi.fn(), + useSpringMock: vi.fn(), communities: { 'music-posting.eth': { address: 'music-posting.eth', @@ -182,7 +183,7 @@ vi.mock('@react-spring/web', async () => { animated: { div: React.forwardRef(({ style, ...props }: any, ref) => React.createElement('div', { ...props, ref, style: normalizeStyle(style) })), }, - useSpring: () => [ + useSpring: testState.useSpringMock.mockImplementation(() => [ { left: { get: () => 120 }, top: { get: () => 80 }, @@ -190,7 +191,7 @@ vi.mock('@react-spring/web', async () => { { start: testState.springStartMock, }, - ], + ]), }; }); @@ -286,6 +287,16 @@ describe('ReplyModal', () => { testState.setAccountMock.mockReset(); testState.setPublishReplyOptionsMock.mockReset(); testState.springStartMock.mockReset(); + testState.useSpringMock.mockReset(); + testState.useSpringMock.mockImplementation(() => [ + { + left: { get: () => 120 }, + top: { get: () => 80 }, + }, + { + start: testState.springStartMock, + }, + ]); testState.communities = { 'music-posting.eth': { address: 'music-posting.eth', @@ -459,4 +470,19 @@ describe('ReplyModal', () => { expect(modal?.style.transform).toBe(''); expect(modal?.style.touchAction).toBe('none'); }); + + it('initializes the drag spring once so typing rerenders do not recenter the modal', async () => { + await renderReplyModal('/mu/thread/post-1'); + + expect(testState.useSpringMock).toHaveBeenCalledWith(expect.any(Function), []); + + const [configFactory, deps] = testState.useSpringMock.mock.calls[0] as [() => Record, unknown[]]; + expect(deps).toEqual([]); + expect(configFactory()).toEqual({ + from: { + left: expect.any(Number), + top: expect.any(Number), + }, + }); + }); }); diff --git a/src/components/reply-modal/reply-modal.tsx b/src/components/reply-modal/reply-modal.tsx index 7b53b982..165f4e2e 100644 --- a/src/components/reply-modal/reply-modal.tsx +++ b/src/components/reply-modal/reply-modal.tsx @@ -111,10 +111,15 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa const nodeRef = useRef(null); const isMobile = useIsMobile(); - const [{ left, top }, api] = useSpring(() => ({ - left: Math.round(window.innerWidth / 2 - 150), - top: Math.round(window.innerHeight / 2 - 200), - })); + const [{ left, top }, api] = useSpring( + () => ({ + from: { + left: Math.round(window.innerWidth / 2 - 150), + top: Math.round(window.innerHeight / 2 - 200), + }, + }), + [], + ); const bind = useDrag( ({ active, event, offset: [ox, oy] }) => {