Skip to content

Commit bc0d5a6

Browse files
Use short Concierge placeholder when long one would wrap
1 parent 04062e9 commit bc0d5a6

1 file changed

Lines changed: 41 additions & 16 deletions

File tree

src/pages/home/ForYouSection/ConciergePromptBox.tsx

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ import useConciergeAttachmentPicker from './useConciergeAttachmentPicker';
3939
// Max number of lines before the input starts scrolling internally.
4040
const MAX_INPUT_LINES = 5;
4141

42+
// A single line of placeholder text is one lineHeightXLarge tall. Anything meaningfully taller has wrapped.
43+
const SINGLE_LINE_PLACEHOLDER_MAX_HEIGHT = variables.lineHeightXLarge * 1.5;
44+
4245
type ConciergePromptBoxProps = {
4346
/**
4447
* Visibility of the "+" actions menu is owned by HomePage (above the narrow/wide layout branch) so it survives the
@@ -65,6 +68,7 @@ function ConciergePromptBox({isMenuVisible, setIsMenuVisible}: ConciergePromptBo
6568
// shouldCalculateCaretPosition), otherwise every value update re-renders it with the caret at the start.
6669
const [selection, setSelection] = useState({start: 0, end: 0});
6770
const [isFocused, setIsFocused] = useState(false);
71+
const [longPlaceholderHeight, setLongPlaceholderHeight] = useState<number | null>(null);
6872
const [popoverAnchorPosition, setPopoverAnchorPosition] = useState<AnchorPosition | null>(null);
6973
const actionButtonRef = useRef<View | HTMLDivElement | null>(null);
7074

@@ -96,7 +100,13 @@ function ConciergePromptBox({isMenuVisible, setIsMenuVisible}: ConciergePromptBo
96100
const localNow = getLocalDateFromDatetime();
97101
const dateLabel = DateUtils.formatToLongDateWithWeekdayWithoutYear(localNow, dateFnsLocale);
98102
const greeting = translate(`homePage.conciergePrompt.${DateUtils.getTimeOfDayGreetingKey(localNow)}`, {name: firstName});
99-
const placeholder = translate(shouldUseNarrowLayout ? 'homePage.conciergePrompt.inputPlaceholderMobile' : 'homePage.conciergePrompt.inputPlaceholder');
103+
const longPlaceholder = translate('homePage.conciergePrompt.inputPlaceholder');
104+
const shortPlaceholder = translate('homePage.conciergePrompt.inputPlaceholderMobile');
105+
106+
// On the wide layout, fall back to the short placeholder when the long one would wrap past one line
107+
// (until it is measured, optimistically assume it fits to avoid a flash of the short copy).
108+
const longPlaceholderFitsOneLine = longPlaceholderHeight === null || longPlaceholderHeight <= SINGLE_LINE_PLACEHOLDER_MAX_HEIGHT;
109+
const placeholder = shouldUseNarrowLayout || !longPlaceholderFitsOneLine ? shortPlaceholder : longPlaceholder;
100110
const canSubmit = shouldShowAskConcierge && value.trim().length > 0;
101111

102112
const submit = () => {
@@ -200,21 +210,36 @@ function ConciergePromptBox({isMenuVisible, setIsMenuVisible}: ConciergePromptBo
200210
</View>
201211
<View style={styles.conciergePromptBoxDivider} />
202212
</View>
203-
<Composer
204-
style={[styles.textNormal, styles.noOutline, styles.flex1, styles.conciergePromptBoxInput]}
205-
value={value}
206-
onChangeText={setValue}
207-
selection={selection}
208-
onSelectionChange={(event) => setSelection(event.nativeEvent.selection)}
209-
shouldCalculateCaretPosition
210-
onFocus={() => setIsFocused(true)}
211-
onBlur={() => setIsFocused(false)}
212-
onKeyPress={handleKeyPress}
213-
maxLines={MAX_INPUT_LINES}
214-
multiline
215-
placeholder={placeholder}
216-
accessibilityLabel={placeholder}
217-
/>
213+
<View style={[styles.flex1, styles.flexRow, styles.pRelative]}>
214+
<Composer
215+
style={[styles.textNormal, styles.noOutline, styles.flex1, styles.conciergePromptBoxInput]}
216+
value={value}
217+
onChangeText={setValue}
218+
selection={selection}
219+
onSelectionChange={(event) => setSelection(event.nativeEvent.selection)}
220+
shouldCalculateCaretPosition
221+
onFocus={() => setIsFocused(true)}
222+
onBlur={() => setIsFocused(false)}
223+
onKeyPress={handleKeyPress}
224+
maxLines={MAX_INPUT_LINES}
225+
multiline
226+
placeholder={placeholder}
227+
accessibilityLabel={placeholder}
228+
/>
229+
{/* Hidden probe stretched to the input's width. Its height reveals whether the long placeholder wraps past one line. */}
230+
<View
231+
pointerEvents="none"
232+
style={[styles.pAbsolute, styles.t0, styles.l0, styles.r0, styles.opacity0]}
233+
onLayout={(event) => setLongPlaceholderHeight(event.nativeEvent.layout.height)}
234+
>
235+
<Text
236+
accessible={false}
237+
style={[styles.textNormal, styles.conciergePromptBoxInput]}
238+
>
239+
{longPlaceholder}
240+
</Text>
241+
</View>
242+
</View>
218243
<Tooltip text={translate('common.send')}>
219244
<PressableWithFeedback
220245
accessibilityLabel={translate('common.send')}

0 commit comments

Comments
 (0)