Assorted clipclop fixes (#3853)
* empty state for new chat dialog * use terniary * dark mode pending state * copy message text option * fix service url input (scrollview ftw) * whoops, fix equality * slightly reduce horizontal message paddingzio/stable
parent
feff55a14a
commit
c223bcdaf7
|
@ -62,19 +62,23 @@ export let MessageItem = ({
|
||||||
lastInGroupRef.current = isLastInGroup
|
lastInGroupRef.current = isLastInGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pendingColor =
|
||||||
|
t.name === 'light' ? t.palette.primary_200 : t.palette.primary_800
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ActionsWrapper isFromSelf={isFromSelf} message={item}>
|
<ActionsWrapper isFromSelf={isFromSelf} message={item}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.py_sm,
|
a.py_sm,
|
||||||
a.px_lg,
|
|
||||||
a.my_2xs,
|
a.my_2xs,
|
||||||
a.rounded_md,
|
a.rounded_md,
|
||||||
{
|
{
|
||||||
|
paddingLeft: 14,
|
||||||
|
paddingRight: 14,
|
||||||
backgroundColor: isFromSelf
|
backgroundColor: isFromSelf
|
||||||
? pending
|
? pending
|
||||||
? t.palette.primary_200
|
? pendingColor
|
||||||
: t.palette.primary_500
|
: t.palette.primary_500
|
||||||
: t.palette.contrast_50,
|
: t.palette.contrast_50,
|
||||||
borderRadius: 17,
|
borderRadius: 17,
|
||||||
|
@ -88,6 +92,7 @@ export let MessageItem = ({
|
||||||
a.text_md,
|
a.text_md,
|
||||||
a.leading_snug,
|
a.leading_snug,
|
||||||
isFromSelf && {color: t.palette.white},
|
isFromSelf && {color: t.palette.white},
|
||||||
|
pending && t.name !== 'light' && {color: t.palette.primary_300},
|
||||||
]}>
|
]}>
|
||||||
{item.text}
|
{item.text}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Pressable, View} from 'react-native'
|
import {Pressable, View} from 'react-native'
|
||||||
|
import * as Clipboard from 'expo-clipboard'
|
||||||
import {ChatBskyConvoDefs} from '@atproto-labs/api'
|
import {ChatBskyConvoDefs} from '@atproto-labs/api'
|
||||||
import {msg} from '@lingui/macro'
|
import {msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {useSession} from 'state/session'
|
import {useSession} from 'state/session'
|
||||||
|
import * as Toast from '#/view/com/util/Toast'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
|
import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid'
|
||||||
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash'
|
||||||
|
@ -12,6 +14,7 @@ import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/War
|
||||||
import * as Menu from '#/components/Menu'
|
import * as Menu from '#/components/Menu'
|
||||||
import * as Prompt from '#/components/Prompt'
|
import * as Prompt from '#/components/Prompt'
|
||||||
import {usePromptControl} from '#/components/Prompt'
|
import {usePromptControl} from '#/components/Prompt'
|
||||||
|
import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '../icons/Clipboard'
|
||||||
|
|
||||||
export let MessageMenu = ({
|
export let MessageMenu = ({
|
||||||
message,
|
message,
|
||||||
|
@ -32,6 +35,14 @@ export let MessageMenu = ({
|
||||||
|
|
||||||
const isFromSelf = message.sender?.did === currentAccount?.did
|
const isFromSelf = message.sender?.did === currentAccount?.did
|
||||||
|
|
||||||
|
const onCopyPostText = React.useCallback(() => {
|
||||||
|
// use when we have rich text
|
||||||
|
// const str = richTextToString(richText, true)
|
||||||
|
|
||||||
|
Clipboard.setStringAsync(message.text)
|
||||||
|
Toast.show(_(msg`Copied to clipboard`))
|
||||||
|
}, [_, message.text])
|
||||||
|
|
||||||
const onDelete = React.useCallback(() => {
|
const onDelete = React.useCallback(() => {
|
||||||
// TODO delete the message
|
// TODO delete the message
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -62,12 +73,22 @@ export let MessageMenu = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Menu.Outer>
|
<Menu.Outer>
|
||||||
|
<Menu.Group>
|
||||||
|
<Menu.Item
|
||||||
|
testID="messageDropdownCopyBtn"
|
||||||
|
label={_(msg`Copy message text`)}
|
||||||
|
onPress={onCopyPostText}>
|
||||||
|
<Menu.ItemText>{_(msg`Copy message text`)}</Menu.ItemText>
|
||||||
|
<Menu.ItemIcon icon={ClipboardIcon} position="right" />
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.Group>
|
||||||
|
<Menu.Divider />
|
||||||
<Menu.Group>
|
<Menu.Group>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
testID="messageDropdownDeleteBtn"
|
testID="messageDropdownDeleteBtn"
|
||||||
label={_(msg`Delete message`)}
|
label={_(msg`Delete message for me`)}
|
||||||
onPress={deleteControl.open}>
|
onPress={deleteControl.open}>
|
||||||
<Menu.ItemText>{_(msg`Delete`)}</Menu.ItemText>
|
<Menu.ItemText>{_(msg`Delete for me`)}</Menu.ItemText>
|
||||||
<Menu.ItemIcon icon={Trash} position="right" />
|
<Menu.ItemIcon icon={Trash} position="right" />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
{!isFromSelf && (
|
{!isFromSelf && (
|
||||||
|
|
|
@ -20,6 +20,7 @@ import * as TextField from '#/components/forms/TextField'
|
||||||
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2'
|
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2'
|
||||||
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
|
||||||
import {Button} from '../Button'
|
import {Button} from '../Button'
|
||||||
|
import {Envelope_Stroke2_Corner0_Rounded as Envelope} from '../icons/Envelope'
|
||||||
import {ListMaybePlaceholder} from '../Lists'
|
import {ListMaybePlaceholder} from '../Lists'
|
||||||
import {Text} from '../Typography'
|
import {Text} from '../Typography'
|
||||||
|
|
||||||
|
@ -178,7 +179,7 @@ function SearchablePeopleList({
|
||||||
</Text>
|
</Text>
|
||||||
<TextField.Root>
|
<TextField.Root>
|
||||||
<TextField.Icon icon={Search} />
|
<TextField.Icon icon={Search} />
|
||||||
<TextField.Input
|
<Dialog.Input
|
||||||
label={_(msg`Search profiles`)}
|
label={_(msg`Search profiles`)}
|
||||||
placeholder={_(msg`Search`)}
|
placeholder={_(msg`Search`)}
|
||||||
value={searchText}
|
value={searchText}
|
||||||
|
@ -197,6 +198,7 @@ function SearchablePeopleList({
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
</View>
|
</View>
|
||||||
|
@ -211,20 +213,35 @@ function SearchablePeopleList({
|
||||||
ListHeaderComponent={
|
ListHeaderComponent={
|
||||||
<>
|
<>
|
||||||
{listHeader}
|
{listHeader}
|
||||||
{searchText.length > 0 && !actorAutocompleteData?.length && (
|
{searchText.length === 0 ? (
|
||||||
<ListMaybePlaceholder
|
<View style={[a.pt_4xl, a.align_center, a.px_lg]}>
|
||||||
isLoading={isFetching}
|
<Envelope width={64} fill={t.palette.contrast_200} />
|
||||||
isError={isError}
|
<Text
|
||||||
onRetry={refetch}
|
style={[
|
||||||
hideBackButton={true}
|
a.text_lg,
|
||||||
emptyType="results"
|
a.text_center,
|
||||||
sideBorders={false}
|
a.mt_md,
|
||||||
emptyMessage={
|
t.atoms.text_contrast_low,
|
||||||
isError
|
]}>
|
||||||
? _(msg`No search results found for "${searchText}".`)
|
<Trans>Search for someone to start a conversation with.</Trans>
|
||||||
: _(msg`Could not load profiles. Please try again later.`)
|
</Text>
|
||||||
}
|
</View>
|
||||||
/>
|
) : (
|
||||||
|
!actorAutocompleteData?.length && (
|
||||||
|
<ListMaybePlaceholder
|
||||||
|
isLoading={isFetching}
|
||||||
|
isError={isError}
|
||||||
|
onRetry={refetch}
|
||||||
|
hideBackButton={true}
|
||||||
|
emptyType="results"
|
||||||
|
sideBorders={false}
|
||||||
|
emptyMessage={
|
||||||
|
isError
|
||||||
|
? _(msg`No search results found for "${searchText}".`)
|
||||||
|
: _(msg`Could not load profiles. Please try again later.`)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {TimeElapsed} from '#/view/com/util/TimeElapsed'
|
||||||
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar'
|
||||||
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
||||||
import {CenteredView} from '#/view/com/util/Views'
|
import {CenteredView} from '#/view/com/util/Views'
|
||||||
|
import {ScrollView} from '#/view/com/util/Views'
|
||||||
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {DialogControlProps, useDialogControl} from '#/components/Dialog'
|
import {DialogControlProps, useDialogControl} from '#/components/Dialog'
|
||||||
|
@ -130,7 +131,7 @@ export function MessagesScreen({navigation}: Props) {
|
||||||
|
|
||||||
if (!hasValidServiceUrl) {
|
if (!hasValidServiceUrl) {
|
||||||
return (
|
return (
|
||||||
<CenteredView sideBorders style={[a.flex_1, a.p_md]}>
|
<ScrollView contentContainerStyle={a.p_lg}>
|
||||||
<View>
|
<View>
|
||||||
<TextField.LabelText>Service URL</TextField.LabelText>
|
<TextField.LabelText>Service URL</TextField.LabelText>
|
||||||
<TextField.Root>
|
<TextField.Root>
|
||||||
|
@ -143,7 +144,7 @@ export function MessagesScreen({navigation}: Props) {
|
||||||
/>
|
/>
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
</View>
|
</View>
|
||||||
</CenteredView>
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, {memo} from 'react'
|
import React, {memo} from 'react'
|
||||||
import {Pressable, PressableProps, StyleProp, ViewStyle} from 'react-native'
|
import {Pressable, PressableProps, StyleProp, ViewStyle} from 'react-native'
|
||||||
import {setStringAsync} from 'expo-clipboard'
|
import * as Clipboard from 'expo-clipboard'
|
||||||
import {
|
import {
|
||||||
AppBskyActorDefs,
|
AppBskyActorDefs,
|
||||||
AppBskyFeedPost,
|
AppBskyFeedPost,
|
||||||
|
@ -160,7 +160,7 @@ let PostDropdownBtn = ({
|
||||||
const onCopyPostText = React.useCallback(() => {
|
const onCopyPostText = React.useCallback(() => {
|
||||||
const str = richTextToString(richText, true)
|
const str = richTextToString(richText, true)
|
||||||
|
|
||||||
setStringAsync(str)
|
Clipboard.setStringAsync(str)
|
||||||
Toast.show(_(msg`Copied to clipboard`))
|
Toast.show(_(msg`Copied to clipboard`))
|
||||||
}, [_, richText])
|
}, [_, richText])
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue