empty states
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 64 64"><path fill="#111822" d="M55 32C55 19.298 44.703 9 32 9S9 19.298 9 32c0 3.463.765 6.745 2.134 9.688.358.769.479 1.658.267 2.525l-2.076 8.51a1.667 1.667 0 0 0 1.987 2.02l8.876-2.005a3.84 3.84 0 0 1 2.412.26A22.9 22.9 0 0 0 32 55c12.703 0 23-10.297 23-23Zm2 0c0 13.807-11.193 25-25 25-3.638 0-7.098-.778-10.219-2.177a1.84 1.84 0 0 0-1.152-.133l-8.877 2.004c-2.655.6-5.015-1.802-4.37-4.446l2.077-8.51a1.84 1.84 0 0 0-.139-1.207A24.9 24.9 0 0 1 7 32C7 18.193 18.193 7 32 7s25 11.193 25 25Z"/><path fill="#111822" d="M17.667 32a2.333 2.333 0 1 0 4.667 0 2.333 2.333 0 0 0-4.667 0Zm24 0a2.334 2.334 0 1 0 4.667 0 2.334 2.334 0 0 0-4.667 0Z"/><path fill="#000" d="M35.137 37.215a1 1 0 0 1 1.414 1.414 6.143 6.143 0 0 1-8.687 0 1 1 0 0 1 1.415-1.414 4.14 4.14 0 0 0 5.858 0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 847 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 64 64"><path fill="#111822" d="M57.666 33.416q-.001-.21-.022-.419L57.6 33H44a4.33 4.33 0 0 0-3.466 1.734l-.8 1.066a6.34 6.34 0 0 1-5.068 2.533h-5.333a6.33 6.33 0 0 1-5.066-2.533l-.8-1.066A4.33 4.33 0 0 0 20 33H6.4l-.045-.003a4 4 0 0 0-.022.42v11.916a4.333 4.333 0 0 0 4.333 4.333h42.667a4.333 4.333 0 0 0 4.333-4.333V33.416Zm-38.57-19.083a4.33 4.33 0 0 0-3.764 2.184L7.057 31H20c1.994 0 3.87.939 5.067 2.533l.8 1.067a4.33 4.33 0 0 0 3.466 1.733h5.333a4.33 4.33 0 0 0 3.467-1.733l.8-1.067A6.34 6.34 0 0 1 44 31h12.944l-8.277-14.483a4.33 4.33 0 0 0-3.762-2.184h-25.81Zm40.57 31a6.333 6.333 0 0 1-6.333 6.333H10.666a6.333 6.333 0 0 1-6.333-6.333V33.416c0-1.102.288-2.185.835-3.142l8.428-14.75a6.33 6.33 0 0 1 5.5-3.19h25.809a6.33 6.33 0 0 1 5.499 3.19l8.427 14.75h.001a6.33 6.33 0 0 1 .834 3.142v11.917Z"/></svg>
|
||||
|
After Width: | Height: | Size: 875 B |
@@ -811,14 +811,15 @@ export function ButtonIcon({
|
||||
* also so that we can calculate transforms.
|
||||
*/
|
||||
const iconSize = {
|
||||
'2xs': 8,
|
||||
xs: 12,
|
||||
sm: 16,
|
||||
md: 18,
|
||||
lg: 24,
|
||||
xl: 28,
|
||||
'2xs': 8,
|
||||
'2xl': 32,
|
||||
'3xl': 40,
|
||||
'4xl': 48,
|
||||
}[iconSizeShorthand]
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
import {createMultiPathSVG, createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const BubbleQuestion_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M5.002 17.036V5h14v12.036h-3.986a1 1 0 0 0-.639.23l-2.375 1.968-2.344-1.965a1 1 0 0 0-.643-.233H5.002ZM20.002 3h-16a1 1 0 0 0-1 1v14.036a1 1 0 0 0 1 1h4.65l2.704 2.266a1 1 0 0 0 1.28.004l2.74-2.27h4.626a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1Zm-7.878 3.663c-1.39 0-2.5 1.135-2.5 2.515a1 1 0 0 0 2 0c0-.294.232-.515.5-.515a.507.507 0 0 1 .489.6.174.174 0 0 1-.027.048 1.1 1.1 0 0 1-.267.226c-.508.345-1.128.923-1.286 1.978a1 1 0 1 0 1.978.297.762.762 0 0 1 .14-.359c.063-.086.155-.169.293-.262.436-.297 1.18-.885 1.18-2.013 0-1.38-1.11-2.515-2.5-2.515ZM12 15.75a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5Z',
|
||||
@@ -15,3 +15,12 @@ export const Bubble_Stroke2_Corner3_Rounded = createSinglePathSVG({
|
||||
export const Bubbles_Stroke2_Corner2_Rounded = createSinglePathSVG({
|
||||
path: 'M6.002 6a3 3 0 0 1 3-3h10a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3h-1v1a3 3 0 0 1-3 3h-4.24l-4.274 2.374a1 1 0 0 1-1.486-.874V19a3 3 0 0 1-3-3v-6a3 3 0 0 1 3-3h1V6Zm-1 3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h1a1 1 0 0 1 1 1v.8l3.015-1.674a1 1 0 0 1 .485-.126h4.5a1 1 0 0 0 1-1v-1.933a1 1 0 0 1 0-.134V10a1 1 0 0 0-1-1h-10Zm13 4v-3a3 3 0 0 0-3-3h-7V6a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-1Z',
|
||||
})
|
||||
|
||||
export const BubbleSmile_Stroke2_Corner2_Rounded_Large = createMultiPathSVG({
|
||||
viewBox: '0 0 64 64',
|
||||
paths: [
|
||||
'M55 32C55 19.298 44.703 9 32 9S9 19.298 9 32c0 3.463.765 6.745 2.134 9.688.358.769.479 1.658.267 2.525l-2.076 8.51a1.667 1.667 0 0 0 1.987 2.02l8.876-2.005a3.84 3.84 0 0 1 2.412.26A22.9 22.9 0 0 0 32 55c12.703 0 23-10.297 23-23Zm2 0c0 13.807-11.193 25-25 25-3.638 0-7.098-.778-10.219-2.177a1.84 1.84 0 0 0-1.152-.133l-8.877 2.004c-2.655.6-5.015-1.802-4.37-4.446l2.077-8.51c.094-.384.046-.809-.139-1.207A24.9 24.9 0 0 1 7 32C7 18.193 18.193 7 32 7s25 11.193 25 25Z',
|
||||
'M17.667 32a2.333 2.333 0 1 0 4.667 0 2.333 2.333 0 0 0-4.667 0Zm24 0a2.334 2.334 0 1 0 4.667 0 2.334 2.334 0 0 0-4.667 0Z',
|
||||
'M35.137 37.215a1 1 0 0 1 1.414 1.414 6.143 6.143 0 0 1-8.687 0 1 1 0 0 1 1.415-1.414 4.14 4.14 0 0 0 5.858 0Z',
|
||||
],
|
||||
})
|
||||
|
||||
@@ -3,3 +3,8 @@ import {createSinglePathSVG} from './TEMPLATE'
|
||||
export const Inbox_Stroke2_Corner2_Rounded = createSinglePathSVG({
|
||||
path: 'M19 14h-2.417a5 5 0 0 1-9.166 0H5v4a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-4Zm0-8a1 1 0 0 0-.898-.995L18 5H6a1 1 0 0 0-1 1v6h3.126a1 1 0 0 1 .969.751 3.001 3.001 0 0 0 5.81 0l.056-.16a1 1 0 0 1 .913-.591H19V6Zm2 12a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h12l.154.004A3 3 0 0 1 21 6v12Z',
|
||||
})
|
||||
|
||||
export const Inbox_Stroke2_Corner2_Rounded_Large = createSinglePathSVG({
|
||||
viewBox: '0 0 64 64',
|
||||
path: 'M57.666 33.416q-.001-.21-.022-.419L57.6 33H44a4.33 4.33 0 0 0-3.466 1.734l-.8 1.066a6.34 6.34 0 0 1-5.068 2.533h-5.333a6.33 6.33 0 0 1-5.066-2.533l-.8-1.066A4.33 4.33 0 0 0 20 33H6.4l-.045-.003a4 4 0 0 0-.022.42v11.916a4.333 4.333 0 0 0 4.333 4.333h42.667a4.333 4.333 0 0 0 4.333-4.333V33.416Zm-38.57-19.083a4.33 4.33 0 0 0-3.764 2.184L7.057 31H20c1.994 0 3.87.939 5.067 2.533l.8 1.067a4.33 4.33 0 0 0 3.466 1.733h5.333a4.33 4.33 0 0 0 3.467-1.733l.8-1.067A6.34 6.34 0 0 1 44 31h12.944l-8.277-14.483a4.33 4.33 0 0 0-3.762-2.184h-25.81Zm40.57 31a6.333 6.333 0 0 1-6.333 6.333H10.666a6.333 6.333 0 0 1-6.333-6.333V33.416c0-1.102.288-2.185.835-3.142l8.428-14.75a6.33 6.33 0 0 1 5.5-3.19h25.809a6.33 6.33 0 0 1 5.499 3.19l8.427 14.75h.001c.547.957.834 2.04.834 3.142v11.917Z',
|
||||
})
|
||||
|
||||
@@ -51,7 +51,7 @@ export function createSinglePathSVG({
|
||||
fill="none"
|
||||
{...rest}
|
||||
ref={ref}
|
||||
viewBox={viewBox || '0 0 24 24'}
|
||||
viewBox={viewBox ?? '0 0 24 24'}
|
||||
width={size}
|
||||
height={size}
|
||||
style={[style]}>
|
||||
@@ -71,7 +71,13 @@ export function createSinglePathSVG({
|
||||
})
|
||||
}
|
||||
|
||||
export function createSinglePathSVG2({path}: {path: string}) {
|
||||
export function createMultiPathSVG({
|
||||
paths,
|
||||
viewBox,
|
||||
}: {
|
||||
paths: string[]
|
||||
viewBox?: string
|
||||
}) {
|
||||
return forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
@@ -80,27 +86,7 @@ export function createSinglePathSVG2({path}: {path: string}) {
|
||||
fill="none"
|
||||
{...rest}
|
||||
ref={ref}
|
||||
viewBox="0 0 24 24"
|
||||
width={size}
|
||||
height={size}
|
||||
style={style}>
|
||||
{gradient}
|
||||
<Path fill={fill} fillRule="evenodd" clipRule="evenodd" d={path} />
|
||||
</Svg>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export function createMultiPathSVG({paths}: {paths: string[]}) {
|
||||
return forwardRef<Svg, Props>(function LogoImpl(props, ref) {
|
||||
const {fill, size, style, gradient, ...rest} = useCommonSVGProps(props)
|
||||
|
||||
return (
|
||||
<Svg
|
||||
fill="none"
|
||||
{...rest}
|
||||
ref={ref}
|
||||
viewBox="0 0 24 24"
|
||||
viewBox={viewBox ?? '0 0 24 24'}
|
||||
width={size}
|
||||
height={size}
|
||||
style={[style]}>
|
||||
|
||||
@@ -21,6 +21,7 @@ export const sizes = {
|
||||
xl: 28,
|
||||
'2xl': 32,
|
||||
'3xl': 48,
|
||||
'4xl': 64,
|
||||
} as const
|
||||
|
||||
export function useCommonSVGProps(props: Props) {
|
||||
|
||||
@@ -28,9 +28,11 @@ import {type DialogControlProps, useDialogControl} from '#/components/Dialog'
|
||||
import {NewChat} from '#/components/dms/dialogs/NewChatDialog'
|
||||
import {useRefreshOnFocus} from '#/components/hooks/useRefreshOnFocus'
|
||||
import {ArrowRotateCounterClockwise_Stroke2_Corner0_Rounded as RetryIcon} from '#/components/icons/ArrowRotate'
|
||||
import {BubbleSmile_Stroke2_Corner2_Rounded_Large as BubbleSmileIcon} from '#/components/icons/Bubble'
|
||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfoIcon} from '#/components/icons/CircleInfo'
|
||||
import {Inbox_Stroke2_Corner2_Rounded_Large as InboxLargeIcon} from '#/components/icons/Inbox'
|
||||
import {
|
||||
Message_Stroke2_Corner0_Rounded as MessageIcon,
|
||||
MessagePlus_Stroke2_Corner0_Rounded as MessagePlusIcon,
|
||||
MessagePlus_Stroke2_Corner0_Rounded as NewChatIcon,
|
||||
} from '#/components/icons/Message'
|
||||
import {SettingsGear2_Stroke2_Corner0_Rounded as SettingsIcon} from '#/components/icons/SettingsGear2'
|
||||
@@ -90,6 +92,7 @@ export function MessagesScreen(props: Props) {
|
||||
export function MessagesScreenInner({navigation, route}: Props) {
|
||||
const {isWithinSplitView} = useIsWithinSplitView()
|
||||
const {t: l} = useLingui()
|
||||
const t = useTheme()
|
||||
const newChatControl = useDialogControl()
|
||||
const pushToConversation = route.params?.pushToConversation
|
||||
|
||||
@@ -132,15 +135,18 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
||||
return (
|
||||
<>
|
||||
<EmptyState
|
||||
message={l`Start a conversation`}
|
||||
icon={MessageIcon}
|
||||
iconSize="3xl"
|
||||
message={l`Say hi to someone`}
|
||||
icon={BubbleSmileIcon}
|
||||
textStyle={t.atoms.text}
|
||||
iconColor={t.atoms.text.color}
|
||||
iconSize="4xl"
|
||||
button={{
|
||||
label: l`New chat`,
|
||||
text: l`New chat`,
|
||||
onPress: newChatControl.open,
|
||||
size: 'small',
|
||||
color: 'secondary_inverted',
|
||||
color: 'primary',
|
||||
icon: MessagePlusIcon,
|
||||
}}
|
||||
style={[a.h_full, a.justify_center, a.pb_5xl]}
|
||||
/>
|
||||
@@ -152,18 +158,37 @@ export function MessagesScreenInner({navigation, route}: Props) {
|
||||
return (
|
||||
<Layout.Screen testID="messagesScreen">
|
||||
<Header newChatControl={newChatControl} />
|
||||
<ChatList />
|
||||
<ChatList newChatControl={newChatControl} />
|
||||
<NewChat onNewChat={onNewChat} control={newChatControl} />
|
||||
</Layout.Screen>
|
||||
)
|
||||
}
|
||||
|
||||
export function ChatList({selectedChat}: {selectedChat?: string}) {
|
||||
export function ChatList({
|
||||
selectedChat,
|
||||
newChatControl,
|
||||
}: {
|
||||
selectedChat?: string
|
||||
newChatControl: DialogControlProps
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {t: l} = useLingui()
|
||||
const scrollElRef: ListRef = useAnimatedRef()
|
||||
const {isWithinSplitView} = useIsWithinSplitView()
|
||||
|
||||
const openChatControl = useCallback(() => {
|
||||
newChatControl.open()
|
||||
}, [newChatControl])
|
||||
|
||||
const requireEmailVerification = useRequireEmailVerification()
|
||||
const wrappedOpenChatControl = requireEmailVerification(openChatControl, {
|
||||
instructions: [
|
||||
<Trans key="new-chat">
|
||||
Before you can message another user, you must first verify your email.
|
||||
</Trans>,
|
||||
],
|
||||
})
|
||||
|
||||
const initialNumToRender = useInitialNumToRender({minItemHeight: 80})
|
||||
const [isPTRing, setIsPTRing] = useState(false)
|
||||
|
||||
@@ -289,14 +314,27 @@ export function ChatList({selectedChat}: {selectedChat?: string}) {
|
||||
</>
|
||||
) : isWithinSplitView ? (
|
||||
<EmptyState
|
||||
message={l`Your conversations will appear here`}
|
||||
icon={MessageIcon}
|
||||
message={l`Inbox empty`}
|
||||
icon={InboxLargeIcon}
|
||||
iconSize="4xl"
|
||||
textStyle={t.atoms.text}
|
||||
iconColor={t.atoms.text.color}
|
||||
/>
|
||||
) : (
|
||||
<EmptyState
|
||||
message={l`No chats yet`}
|
||||
icon={MessageIcon}
|
||||
iconSize="3xl"
|
||||
icon={InboxLargeIcon}
|
||||
iconSize="4xl"
|
||||
textStyle={t.atoms.text}
|
||||
iconColor={t.atoms.text.color}
|
||||
button={{
|
||||
label: l`New chat`,
|
||||
text: l`New chat`,
|
||||
onPress: wrappedOpenChatControl,
|
||||
size: 'small',
|
||||
color: 'primary',
|
||||
icon: MessagePlusIcon,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -94,7 +94,10 @@ function MessagesSplitViewLayout({children, navigation, route}: LayoutProps) {
|
||||
{width: centerColumnOffset ? 300 : 350},
|
||||
]}>
|
||||
<ChatListHeader newChatControl={newChatControl} />
|
||||
<ChatList selectedChat={selectedChat} />
|
||||
<ChatList
|
||||
newChatControl={newChatControl}
|
||||
selectedChat={selectedChat}
|
||||
/>
|
||||
<NewChat onNewChat={onNewChat} control={newChatControl} />
|
||||
</View>
|
||||
</SplitViewProvider>
|
||||
|
||||
@@ -197,11 +197,12 @@ export function ItemIcon({
|
||||
'2xs': 8,
|
||||
xs: 12,
|
||||
sm: 16,
|
||||
md: 20,
|
||||
md: 18,
|
||||
lg: 24,
|
||||
xl: 28,
|
||||
'2xl': 32,
|
||||
'3xl': 40,
|
||||
'4xl': 48,
|
||||
}[size]
|
||||
|
||||
const color =
|
||||
|
||||
@@ -3,19 +3,26 @@ import {type StyleProp, type TextStyle, type ViewStyle} from 'react-native'
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, type ButtonProps, ButtonText} from '#/components/Button'
|
||||
import {
|
||||
Button,
|
||||
ButtonIcon,
|
||||
type ButtonProps,
|
||||
ButtonText,
|
||||
} from '#/components/Button'
|
||||
import {EditBig_Stroke1_Corner0_Rounded as EditIcon} from '#/components/icons/EditBig'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export type EmptyStateButtonProps = Omit<ButtonProps, 'children' | 'label'> & {
|
||||
label: string
|
||||
text: string
|
||||
icon?: React.ComponentProps<typeof ButtonIcon>['icon']
|
||||
}
|
||||
|
||||
export function EmptyState({
|
||||
testID,
|
||||
icon,
|
||||
iconSize = '3xl',
|
||||
iconColor,
|
||||
message,
|
||||
style,
|
||||
textStyle,
|
||||
@@ -23,7 +30,8 @@ export function EmptyState({
|
||||
}: {
|
||||
testID?: string
|
||||
icon?: React.ComponentType<any> | React.ReactElement | null
|
||||
iconSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl'
|
||||
iconSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'
|
||||
iconColor?: string
|
||||
message: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
textStyle?: StyleProp<TextStyle>
|
||||
@@ -57,8 +65,7 @@ export function EmptyState({
|
||||
return (
|
||||
<IconComponent
|
||||
size={iconSize}
|
||||
fill={t.atoms.text_contrast_medium.color}
|
||||
style={{color: t.atoms.text_contrast_low.color}}
|
||||
style={{color: iconColor ?? t.atoms.text_contrast_low.color}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -99,8 +106,9 @@ export function EmptyState({
|
||||
{message}
|
||||
</Text>
|
||||
{button && (
|
||||
<View style={[a.flex_shrink, a.mt_xl, a.self_center, a.mb_5xl]}>
|
||||
<View style={[a.flex_shrink, a.mt_md, a.self_center, a.mb_5xl]}>
|
||||
<Button {...button}>
|
||||
{button.icon && <ButtonIcon icon={button.icon} />}
|
||||
<ButtonText>{button.text}</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user