Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions apps/mobile/src/features/threads/ThreadComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
import { StackActions, useFocusEffect, useNavigation } from "@react-navigation/native";
import type { ReactNode } from "react";
import { useEffect, useEffectEvent, useRef, useState, type RefObject } from "react";
import { Keyboard, Platform, View, type ViewStyle } from "react-native";
import { I18nManager, Keyboard, Platform, View, type ViewStyle } from "react-native";
import { composerImageGallery } from "../../lib/composerImageGallery";
import { filePreviewGallery } from "../../lib/filePreviewGallery";
import { FilePreviewModal, type FilePreviewSource } from "../../components/FilePreviewModal";
Expand Down Expand Up @@ -97,6 +97,7 @@ import {
resolveComposerSendPresentation,
type ComposerSendPresentation,
} from "./composerSendPresentation";
import { compactEditorInsets } from "./compactEditorInsets";
import { ComposerCommandPopover } from "./ComposerCommandPopover";
import { ComposerPopoverAnchor } from "./ComposerPopoverAnchor";
import { ComposerQueuedEditAttachments } from "./ComposerQueuedEdit";
Expand Down Expand Up @@ -157,12 +158,19 @@ function useCompactComposerEditor(text: ScaledTextRole, value: string) {
(contentHeight > text.lineHeight * 1.5 ||
(compactWidth > 0 && contentSize.width > compactWidth));
const compactRowHeight = Math.max(44, minimum);
const isRTL = I18nManager.isRTL;
const editorStyle = useAnimatedStyle(() => ({
position: "absolute",
overflow: "hidden",
height,
left: isExpanded ? 14 : editorX.value + 4,
right: isExpanded ? 14 : rowWidth.value - editorX.value - editorWidth.value + 4,
...(isExpanded
? { left: 14, right: 14 }
: compactEditorInsets({
slotX: editorX.value,
slotWidth: editorWidth.value,
rowWidth: rowWidth.value,
isRTL,
})),
// The full-width editor must move above the controls in the same frame it widens.
bottom: isExpanded ? 52 : (compactRowHeight - minimum) / 2,
}));
Expand Down
17 changes: 17 additions & 0 deletions apps/mobile/src/features/threads/compactEditorInsets.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { describe, expect, it } from "vite-plus/test";

import { compactEditorInsets } from "./compactEditorInsets";

describe("compact editor insets", () => {
it("keeps the measured slot position in left-to-right layouts", () => {
expect(compactEditorInsets({ slotX: 48, slotWidth: 200, rowWidth: 360, isRTL: false })).toEqual(
{ left: 52, right: 116 },
);
});

it("swaps the insets in right-to-left layouts so the editor stays over its slot", () => {
expect(compactEditorInsets({ slotX: 112, slotWidth: 200, rowWidth: 360, isRTL: true })).toEqual(
{ left: 52, right: 116 },
);
});
});
17 changes: 17 additions & 0 deletions apps/mobile/src/features/threads/compactEditorInsets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const COMPACT_EDITOR_SLOT_INSET = 4;

/**
* Places the floating editor over its slot. Layout x is measured from the left,
* but React Native swaps left and right styles in RTL, so the insets trade places.
*/
export function compactEditorInsets(input: {
readonly slotX: number;
readonly slotWidth: number;
readonly rowWidth: number;
readonly isRTL: boolean;
}): { left: number; right: number } {
"worklet";
const fromLeft = input.slotX + COMPACT_EDITOR_SLOT_INSET;
const fromRight = input.rowWidth - input.slotX - input.slotWidth + COMPACT_EDITOR_SLOT_INSET;
return input.isRTL ? { left: fromRight, right: fromLeft } : { left: fromLeft, right: fromRight };
}
Loading