Add mobile navigation & Composer (#333)

* feat: add composer component

* feat: complete composer component and few minor fixes

* feat: add several improvements and new features

* fix: change icon button in composer
This commit is contained in:
marcelines 2023-01-20 14:06:26 +00:00 committed by GitHub
parent 2fab24781e
commit 5171209b5d
No known key found for this signature in database
GPG Key ID: 0EB8D75C775AB6F1
37 changed files with 884 additions and 392 deletions

View File

@ -2,27 +2,26 @@
/* eslint-disable import/namespace */
import 'expo-dev-client'
import { useState } from 'react'
import { useMemo, useState } from 'react'
import {
Code,
Heading,
Label,
Messages,
Paragraph,
Shape,
Sidebar,
} from '@status-im/components'
import { Stack, TamaguiProvider } from '@tamagui/core'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { Heading } from '@status-im/components'
import { Avatar } from '@status-im/components/src/avatar'
import { Stack as View, TamaguiProvider } from '@tamagui/core'
import { useFonts } from 'expo-font'
import { SafeAreaView, ScrollView, TouchableOpacity } from 'react-native'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { AnimatePresence } from 'tamagui'
import { NavigationProvider } from './navigation/provider'
import { ChannelScreen } from './screens/channel'
import { HomeScreen } from './screens/home'
import tamaguiConfig from './tamagui.config'
type ThemeVars = 'light' | 'dark'
const Stack = createNativeStackNavigator()
export default function App() {
const [theme, setTheme] = useState<ThemeVars>('light')
const [position, setPosition] = useState(0)
const [loaded] = useFonts({
Inter: require('@tamagui/font-inter/otf/Inter-Medium.otf'),
InterBold: require('@tamagui/font-inter/otf/Inter-Bold.otf'),
@ -30,56 +29,95 @@ export default function App() {
UbuntuMono: require('./assets/fonts/UbuntuMono.ttf'),
})
const onScroll = event => {
if (event.nativeEvent.contentOffset.y > 90) {
setPosition(event.nativeEvent.contentOffset.y)
} else {
setPosition(0)
}
}
const showMinimizedHeader = useMemo(() => {
return position > 90
}, [position])
if (!loaded) {
return null
}
return (
<TamaguiProvider config={tamaguiConfig} defaultTheme={theme}>
<SafeAreaView>
<ScrollView>
<Sidebar
name="Rarible"
description="Multichain community-centric NFT marketplace. Create, buy and sell your NFTs."
membersCount={123}
/>
<Stack
flexDirection="column"
justifyContent="center"
<TamaguiProvider config={tamaguiConfig} defaultTheme="light">
<NavigationProvider>
<SafeAreaProvider>
<Stack.Navigator>
<Stack.Screen
name="Home"
options={{
headerBlurEffect: 'systemUltraThinMaterialLight',
headerTransparent: true,
headerShadowVisible: true,
header: () => (
<View
height={100}
animation="fast"
backgroundColor={
showMinimizedHeader ? '$background' : 'transparent'
}
padding={16}
paddingTop={48}
flexDirection="row"
justifyContent="space-between"
alignItems="center"
paddingTop={20}
paddingHorizontal={12}
>
<AnimatePresence>
{showMinimizedHeader && (
<View
key="header"
animation={[
'fast',
{
opacity: {
overshootClamping: true,
},
},
]}
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
opacity={1}
flexDirection="row"
alignItems="center"
justifyContent="space-between"
width="100%"
backgroundColor="$background"
>
<Heading weight="semibold" marginBottom={12}>
Communities
</Heading>
<Heading heading="h2" marginBottom={12}>
This is an Heading 2
</Heading>
<Paragraph weight="semibold" marginBottom={12} uppercase>
Paragraph uppercased and bolded
</Paragraph>
<Paragraph marginBottom={12} uppercase>
This is a paragraph
</Paragraph>
<Label marginBottom={12}>This is a label</Label>
<Code marginBottom={12}>This is a code line</Code>
<Paragraph fontWeight="400">0x213abc190 ... 121ah4a9e</Paragraph>
<Shape marginVertical={20} />
<Paragraph>Theme selected - {theme}</Paragraph>
<TouchableOpacity
onPress={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
<Heading color="$textPrimary">Rarible</Heading>
<Avatar
withOutline
src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.seadn.io%2Fgae%2FFG0QJ00fN3c_FWuPeUr9-T__iQl63j9hn5d6svW8UqOmia5zp3lKHPkJuHcvhZ0f_Pd6P2COo9tt9zVUvdPxG_9BBw%3Fw%3D500%26auto%3Dformat&f=1&nofb=1&ipt=c177cd71d8d0114080cfc6efd3f9e098ddaeb1b347919bd3089bf0aacb003b3e&ipo=images"
size={48}
/>
</View>
)}
</AnimatePresence>
</View>
),
}}
>
<Paragraph>Toogle theme</Paragraph>
</TouchableOpacity>
<Messages />
</Stack>
</ScrollView>
</SafeAreaView>
{props => (
<HomeScreen
{...props}
onScroll={onScroll}
isMinimized={showMinimizedHeader}
/>
)}
</Stack.Screen>
<Stack.Screen
name="Channel"
component={ChannelScreen}
options={{ title: 'Messages' }}
/>
</Stack.Navigator>
</SafeAreaProvider>
</NavigationProvider>
</TamaguiProvider>
)
}

View File

@ -0,0 +1,9 @@
import { NavigationContainer } from '@react-navigation/native'
export function NavigationProvider({
children,
}: {
children: React.ReactNode
}) {
return <NavigationContainer>{children}</NavigationContainer>
}

View File

@ -10,6 +10,8 @@
},
"dependencies": {
"@babel/runtime": "^7.18.9",
"@react-navigation/native": "^6.1.2",
"@react-navigation/native-stack": "^6.9.8",
"@status-im/components": "*",
"expo": "^47.0.12",
"expo-constants": "^14.0.2",
@ -21,6 +23,8 @@
"react": "18.1.0",
"react-dom": "^18.1.0",
"react-native": "0.70.5",
"react-native-safe-area-context": "4.4.1",
"react-native-screens": "~3.18.0",
"react-native-web": "~0.18.7"
},
"devDependencies": {

View File

@ -0,0 +1,34 @@
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable import/namespace */
import { Composer, Messages } from '@status-im/components'
import { Stack, useTheme } from '@tamagui/core'
import {
Keyboard,
KeyboardAvoidingView,
Platform,
TouchableWithoutFeedback,
} from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { ScrollView } from 'tamagui'
export const ChannelScreen = () => {
const insets = useSafeAreaInsets()
const theme = useTheme()
return (
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={Platform.select({ ios: 60 })}
style={{ height: '100%', flex: 1, backgroundColor: theme.background.val }}
>
<ScrollView paddingHorizontal={12} width="100%">
<Messages py={20} />
</ScrollView>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<Stack pb={insets.bottom}>
<Composer />
</Stack>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
)
}

View File

@ -0,0 +1,34 @@
// eslint-disable-next-line eslint-comments/disable-enable-pair
/* eslint-disable import/namespace */
import { useState } from 'react'
import { Sidebar } from '@status-im/components'
import { Stack } from '@tamagui/core'
import { StatusBar } from 'expo-status-bar'
import { ScrollView } from 'tamagui'
export const HomeScreen = ({ navigation, onScroll, isMinimized }) => {
const [selectedChannel, setSelectedChannel] = useState<string>('welcome')
const onChannelPress = (id: string) => {
setSelectedChannel(id)
navigation.navigate('Channel')
}
return (
<Stack flex={1} backgroundColor="$background">
<StatusBar style={isMinimized ? 'dark' : 'light'} animated />
<ScrollView onScroll={onScroll} scrollEventThrottle={16} flex={1}>
<Stack pb={40}>
<Sidebar
name="Rarible"
description="Multichain community-centric NFT marketplace. Create, buy and sell your NFTs."
membersCount={123}
onChannelPress={onChannelPress}
selectedChannel={selectedChannel}
/>
</Stack>
</ScrollView>
</Stack>
)
}

View File

@ -3,6 +3,7 @@ import { useState } from 'react'
import {
Button,
Code,
Composer,
Heading,
Label,
Messages,
@ -10,17 +11,41 @@ import {
Shape,
Sidebar,
} from '@status-im/components'
import { Stack, TamaguiProvider } from '@tamagui/core'
import { Stack, styled, TamaguiProvider } from '@tamagui/core'
import { AnimatePresence } from 'tamagui'
import tamaguiConfig from '../tamagui.config'
import { Topbar } from './components/topbar'
type ThemeVars = 'light' | 'dark'
const AnimatableDrawer = styled(Stack, {
variants: {
fromRight: {
true: {
x: 500,
width: 0,
},
},
fromLeft: {
true: {
x: 500,
width: 250,
},
},
},
})
function App() {
const [theme, setTheme] = useState<ThemeVars>('light')
const [showMembers, setShowMembers] = useState(false)
const [selectedChannel, setSelectedChannel] = useState<string>('welcome')
const onChannelPress = (id: string) => {
setSelectedChannel(id)
}
return (
<TamaguiProvider config={tamaguiConfig} defaultTheme={theme}>
<div id="app">
@ -29,9 +54,10 @@ function App() {
name="Rarible"
description="Multichain community-centric NFT marketplace. Create, buy and sell your NFTs."
membersCount={123}
onChannelPress={onChannelPress}
selectedChannel={selectedChannel}
/>
</div>
<main id="main">
<Topbar
membersVisisble={showMembers}
@ -71,16 +97,30 @@ function App() {
</Stack>
</Stack>
</div>
<Stack
backgroundColor="$background"
justifyContent="center"
alignItems="center"
>
<Paragraph weight="semibold">Composer</Paragraph>
</Stack>
<Composer />
</main>
{showMembers && <div id="members">members</div>}
<AnimatePresence>
{showMembers && (
<AnimatableDrawer
id="members"
key="members"
animation={[
'fast',
{
opacity: {
overshootClamping: true,
},
},
]}
enterStyle={{ opacity: 0 }}
exitStyle={{ opacity: 0 }}
opacity={1}
>
members
</AnimatableDrawer>
)}
</AnimatePresence>
</div>
</TamaguiProvider>
)

View File

@ -1,5 +1,8 @@
import '../styles/reset.css'
import '../styles/app.css'
import '@tamagui/core/reset.css'
import '@tamagui/font-inter/css/400.css'
import '@tamagui/font-inter/css/700.css'
import React from 'react'

View File

@ -15,13 +15,9 @@ body,
display: grid;
grid-template-rows: 56px 1fr 100px;
height: 100vh;
}
#main,
#sidebar,
#members,
#main > div {
overflow: hidden;
border: 1px solid rgba(0, 0, 0, 0.1);
border-width: 0 1px;
}
#sidebar {

View File

@ -1,9 +1,9 @@
import React from 'react'
import { ChevronIcon } from '@status-im/icons'
import { Stack } from '@tamagui/core'
import { AnimatePresence } from 'tamagui'
import { Chevron } from '../icon'
import { Label, Paragraph } from '../typography'
import type { GetProps } from '@tamagui/core'
@ -48,9 +48,17 @@ const Accordion = ({
<Stack flexDirection="row" alignItems="center">
<Stack
animation="fast"
transform={[{ rotateZ: isExpanded ? '90deg' : '0deg' }]}
justifyContent="center"
transform={[
{
rotateZ: isExpanded ? '90deg' : '0deg',
},
{
translateY: isExpanded ? -4 : 0,
},
]}
>
<Chevron color="$neutral-50" size={16} />
<ChevronIcon color="$neutral-50" size={16} />
</Stack>
<Paragraph
marginLeft={4}

View File

@ -1,6 +1,6 @@
import { MutedIcon } from '@status-im/icons'
import { Stack } from '@tamagui/core'
import { Muted } from '../icon'
import { Label, Paragraph } from '../typography'
import type { GetProps } from '@tamagui/core'
@ -42,7 +42,10 @@ const AccordionItem = ({
},
},
]}
backgroundColor={isSelected ? '$turquoise-50-opa-10' : 'transparent'}
backgroundColor={isSelected ? '$primary-50-opa-10' : 'transparent'}
hoverStyle={{
backgroundColor: '$primary-50-opa-5',
}}
borderRadius="$4"
padding={8}
width="100%"
@ -105,7 +108,9 @@ const AccordionItem = ({
/>
</Stack>
)}
{channelStatus === 'muted' && <Muted size={20} color="$neutral-40" />}
{channelStatus === 'muted' && (
<MutedIcon size={20} color="$neutral-40" />
)}
</Stack>
)}
</Stack>

View File

@ -24,6 +24,11 @@ const Base = styled(Stack, {
variants: {
size: {
80: {
width: 80,
height: 80,
borderRadius: 80 / 2,
},
56: {
width: 56,
height: 56,
@ -80,10 +85,15 @@ const Indicator = styled(Stack, {
variants: {
size: {
80: {
width: 10,
height: 10,
borderRadius: '50%',
},
56: {
width: 10,
height: 10,
borderRadius: 10 / 2,
borderRadius: '50%',
},
// FIXME: use catch all variant
52: {

View File

@ -24,6 +24,7 @@ const ChatMessage = (props: Props) => {
space={10}
position="relative"
alignItems="flex-start"
justifyContent="center"
paddingHorizontal={8}
paddingVertical={12}
borderRadius={16}
@ -50,7 +51,7 @@ const ChatMessage = (props: Props) => {
<Paragraph weight="semibold" color="$neutral-100">
Alisher Yakupov
</Paragraph>
<Paragraph fontFamily="$mono" color="$neutral-50" fontSize={11}>
<Paragraph color="$neutral-50" fontSize={11}>
zQ3...9d4Gs0
</Paragraph>
<Paragraph color="$neutral-50" variant={11}>

View File

@ -3,7 +3,7 @@ import { Stack } from 'tamagui'
import { Paragraph } from '../../typography'
interface Props {
onClick: VoidFunction
onClick?: VoidFunction
}
export const Actions = (_props: Props) => {

View File

@ -1,10 +1,16 @@
import { Stack } from '@tamagui/core'
import { ChatMessage } from './chat-message'
import type { GetProps } from '@tamagui/core'
export * from './chat-message'
export const Messages = () => {
type BaseProps = GetProps<typeof Stack>
export const Messages = (props: BaseProps) => {
return (
<>
<Stack {...props}>
<ChatMessage text="fsdjkf kasldjf ksdlfjksdlfj asdklfj sdkljf" />
<ChatMessage text="fsdjkf kasldjf ksdlfjksdlfj asdklfj sdkljf" />
<ChatMessage text="fsdjkf kasldjf ksdlfjksdlfj asdklfj sdkljf" />
@ -21,6 +27,6 @@ export const Messages = () => {
<ChatMessage text="fsdjkf kasldjf ksdlfjksdlfj asdklfj sdkljf" />
<ChatMessage text="fsdjkf kasldjf ksdlfjksdlfj asdklfj sdkljf" />
<ChatMessage text="fsdjkf kasldjf ksdlfjksdlfj asdklfj sdkljf" />
</>
</Stack>
)
}

View File

@ -0,0 +1,52 @@
import {
AudioIcon,
FormatIcon,
ImageIcon,
ReactionIcon,
} from '@status-im/icons'
import { Stack, XStack, YStack } from 'tamagui'
import { IconButton } from '../icon-button'
import { Input } from '../input'
import type { GetProps } from '@tamagui/core'
type BaseProps = GetProps<typeof YStack>
const Composer = (props: BaseProps) => {
return (
<YStack
backgroundColor="$background"
shadowColor="rgba(9, 16, 28, 0.08)"
shadowOffset={{ width: 0, height: -4 }}
shadowRadius={20}
borderTopLeftRadius={20}
borderTopRightRadius={20}
elevation={0}
px={16}
pt={8}
pb={12}
width="100%"
{...props}
>
<YStack>
<Input
elevation={10}
placeholder="Type something..."
borderWidth={0}
px={0}
/>
</YStack>
<XStack alignItems="center" justifyContent="space-between" pt={8}>
<Stack space={12} flexDirection="row">
<IconButton noBackground icon={<ImageIcon />} />
<IconButton noBackground icon={<ReactionIcon />} />
<IconButton noBackground icon={<FormatIcon />} />
</Stack>
<IconButton noBackground icon={<AudioIcon />} />
</XStack>
</YStack>
)
}
export { Composer }

View File

@ -0,0 +1 @@
export { Composer } from './composer'

View File

@ -13,9 +13,10 @@ const Base = styled(Stack, {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
animation: 'fast',
width: 31,
height: 31,
width: 30,
height: 30,
borderWidth: 1,
backgroundColor: '$neutral-10',
borderColor: '$neutral-10',
@ -24,7 +25,27 @@ const Base = styled(Stack, {
backgroundColor: '$neutral-20',
},
pressStyle: {
backgroundColor: '$neutral-20',
},
variants: {
noBackground: {
true: {
backgroundColor: 'transparent',
borderColor: '$neutral-20',
hoverStyle: {
backgroundColor: 'transparent',
borderColor: '$neutral-30',
},
pressStyle: {
backgroundColor: 'transparent',
borderColor: '$neutral-30',
},
},
},
selected: {
true: {
backgroundColor: '$neutral-30',
@ -56,14 +77,17 @@ interface Props {
icon: React.ReactElement
onPress?: () => void
selected?: boolean
noBackground?: boolean
}
const IconButton = (props: Props) => {
const { icon, selected, onPress } = props
const { icon, noBackground, selected, onPress } = props
return (
<Base selected={selected} onPress={onPress}>
<Icon selected={selected}>{icon}</Icon>
<Base selected={selected} onPress={onPress} noBackground={noBackground}>
<Icon selected={selected} pointerEvents="none">
{icon}
</Icon>
</Base>
)
}

View File

@ -1,18 +0,0 @@
import type {
SizeTokens,
StyleObject,
ThemeParsed,
Tokens,
} from '@tamagui/core'
import type { SvgProps } from 'react-native-svg'
type GetTokenString<A> = A extends string ? `$${A}` : `$${string}`
export type ColorTokens =
| GetTokenString<keyof Tokens['color']>
| GetTokenString<keyof ThemeParsed>
export type IconProps = SvgProps & {
size?: number | SizeTokens
color?: ColorTokens
style?: StyleObject
}

View File

@ -1,3 +0,0 @@
export { Chevron } from './icons/chevron'
export { Group } from './icons/group'
export { Muted } from './icons/muted'

View File

@ -1,6 +1,8 @@
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/ban-ts-comment */
import * as icons from '@status-im/icons'
import { Text } from '../typography'
import { Paragraph } from '../typography'
import type { IconProps } from '@status-im/icons'
import type { Meta, StoryObj } from '@storybook/react'
@ -35,8 +37,8 @@ export const All: Story = {
key={name}
style={{ display: 'flex', flexDirection: 'column' }}
>
<Icon color="black" />
<Text>{unpascal(name)}</Text>
<Icon color="$background" />
<Paragraph>{unpascal(name)}</Paragraph>
</div>
)
})}

View File

@ -1,5 +1,6 @@
export * from './button'
export * from './chat-message'
export * from './composer'
export * from './divider'
export * from './icon-button'
export * from './image'

View File

@ -0,0 +1,177 @@
import { Basketball, Collaboration, Fire, Peach, Play, Unicorn } from '../emoji'
interface Channels {
id: string
title: string
icon?: React.ReactNode
channelStatus?: 'muted' | 'normal' | 'withMessages' | 'withMentions'
numberOfMessages?: number
}
export interface CommunityProps {
id: string
title: string
numberOfNewMessages?: number
channels: Channels[]
}
// MOCK DATA
export const COMMUNITIES: CommunityProps[] = [
{
id: 'welcome',
title: 'Welcome',
numberOfNewMessages: 3,
channels: [
{
id: 'welcome',
title: '# welcome',
icon: <Collaboration hasBackground />,
},
{
id: 'general-welcome',
title: '# general',
icon: <Play hasBackground />,
},
{
id: 'random',
title: '# random',
icon: <Fire hasBackground />,
},
{
id: 'onboarding',
title: '# onboarding',
icon: <Unicorn hasBackground />,
channelStatus: 'withMentions',
numberOfMessages: 3,
},
],
},
{
id: 'community',
title: 'Community',
numberOfNewMessages: 5,
channels: [
{
id: 'announcements',
title: '# announcements',
icon: <Peach hasBackground />,
},
{
id: 'jobs',
title: '# jobs',
channelStatus: 'withMentions',
numberOfMessages: 3,
icon: <Play hasBackground />,
},
{
id: 'events',
title: '# events',
channelStatus: 'withMentions',
numberOfMessages: 2,
icon: <Fire hasBackground />,
},
{
id: 'meetups',
title: '# meetups',
icon: <Unicorn hasBackground />,
},
],
},
{
id: 'design',
title: 'Design',
channels: [
{
id: 'design',
title: '# design',
icon: <Collaboration hasBackground />,
},
{
id: 'ux',
title: '# ux',
icon: <Play hasBackground />,
},
{
id: 'ui',
title: '# ui',
icon: <Fire hasBackground />,
},
{
id: 'figma',
title: '# figma',
icon: <Unicorn hasBackground />,
},
],
},
{
id: 'General',
title: 'General',
channels: [
{
id: 'general',
title: '# general',
icon: <Collaboration hasBackground />,
},
{
id: 'people-ops',
title: '# people-ops',
icon: <Basketball hasBackground />,
},
],
},
{
id: 'Frontend',
title: 'Frontend',
channels: [
{
id: 'react',
title: '# react',
icon: <Collaboration hasBackground />,
channelStatus: 'withMessages',
},
{
id: 'vue',
title: '# vue',
icon: <Play hasBackground />,
},
{
id: 'angular',
title: '# angular',
channelStatus: 'muted',
icon: <Fire hasBackground />,
},
{
id: 'svelte',
title: '# svelte',
icon: <Unicorn hasBackground />,
},
],
},
{
id: 'Backend',
title: 'Backend',
channels: [
{
id: 'node',
title: '# node',
icon: <Collaboration hasBackground />,
},
{
id: 'python',
title: '# python',
icon: <Play hasBackground />,
},
{
id: 'ruby',
title: '# ruby',
icon: <Fire hasBackground />,
},
{
id: 'php',
title: '# php',
channelStatus: 'muted',
icon: <Unicorn hasBackground />,
},
],
},
]

View File

@ -1,213 +1,53 @@
import { useState } from 'react'
import { GroupIcon } from '@status-im/icons'
import { Stack } from '@tamagui/core'
import { Accordion } from '../accordion/accordion'
import { AccordionItem } from '../accordion/accordionItem'
import { Avatar } from '../avatar'
import { Button } from '../button'
// import { Button } from '../button'
import { Basketball, Collaboration, Fire, Peach, Play, Unicorn } from '../emoji'
import { Group } from '../icon'
import { Image } from '../image'
import { Heading, Paragraph } from '../typography'
import { COMMUNITIES } from './mock-data'
interface Props {
import type { CommunityProps } from './mock-data'
import type { GetProps } from '@tamagui/core'
type BaseProps = GetProps<typeof Stack>
type Props = {
name: string
description: string
membersCount: number
}
selectedChannel?: string
communities?: CommunityProps[]
onChannelPress: (channelId: string) => void
} & BaseProps
interface Channels {
id: string
title: string
icon?: React.ReactNode
channelStatus?: 'muted' | 'normal' | 'withMessages' | 'withMentions'
numberOfMessages?: number
}
interface CommunitiesProps {
id: string
title: string
numberOfNewMessages?: number
channels: Channels[]
}
const Sidebar = (props: Props) => {
const {
name,
description,
membersCount,
communities = COMMUNITIES,
selectedChannel,
onChannelPress,
} = props
// MOCK DATA
const COMMUNITIES: CommunitiesProps[] = [
{
id: 'welcome',
title: 'Welcome',
numberOfNewMessages: 3,
channels: [
{
id: 'welcome',
title: '# welcome',
icon: <Collaboration hasBackground />,
},
{
id: 'general-welcome',
title: '# general',
icon: <Play hasBackground />,
},
{
id: 'random',
title: '# random',
icon: <Fire hasBackground />,
},
{
id: 'onboarding',
title: '# onboarding',
icon: <Unicorn hasBackground />,
channelStatus: 'withMentions',
numberOfMessages: 3,
},
],
},
{
id: 'community',
title: 'Community',
numberOfNewMessages: 5,
channels: [
{
id: 'announcements',
title: '# announcements',
icon: <Peach hasBackground />,
},
{
id: 'jobs',
title: '# jobs',
channelStatus: 'withMentions',
numberOfMessages: 3,
icon: <Play hasBackground />,
},
{
id: 'events',
title: '# events',
channelStatus: 'withMentions',
numberOfMessages: 2,
icon: <Fire hasBackground />,
},
{
id: 'meetups',
title: '# meetups',
icon: <Unicorn hasBackground />,
},
],
},
{
id: 'Design',
title: 'Design',
channels: [
{
id: 'design',
title: '# design',
icon: <Collaboration hasBackground />,
},
{
id: 'ux',
title: '# ux',
icon: <Play hasBackground />,
},
{
id: 'ui',
title: '# ui',
icon: <Fire hasBackground />,
},
{
id: 'figma',
title: '# figma',
icon: <Unicorn hasBackground />,
},
],
},
{
id: 'General',
title: 'General',
channels: [
{
id: 'general',
title: '# general',
icon: <Collaboration hasBackground />,
},
{
id: 'people-ops',
title: '# people-ops',
icon: <Basketball hasBackground />,
},
],
},
{
id: 'Frontend',
title: 'Frontend',
channels: [
{
id: 'react',
title: '# react',
icon: <Collaboration hasBackground />,
channelStatus: 'withMessages',
},
{
id: 'vue',
title: '# vue',
icon: <Play hasBackground />,
},
{
id: 'angular',
title: '# angular',
channelStatus: 'muted',
icon: <Fire hasBackground />,
},
{
id: 'svelte',
title: '# svelte',
icon: <Unicorn hasBackground />,
},
],
},
{
id: 'Backend',
title: 'Backend',
channels: [
{
id: 'node',
title: '# node',
icon: <Collaboration hasBackground />,
},
{
id: 'python',
title: '# python',
icon: <Play hasBackground />,
},
{
id: 'ruby',
title: '# ruby',
icon: <Fire hasBackground />,
},
{
id: 'php',
title: '# php',
channelStatus: 'muted',
icon: <Unicorn hasBackground />,
},
],
},
]
const COMMUNITIES_EXPAND_CONTROL = COMMUNITIES.reduce(
const communitiesExpandControl = communities.reduce(
(o, key) => ({ ...o, [key.id]: false }),
{} as Record<string, boolean>[]
)
)
const Sidebar = (props: Props) => {
const { name, description, membersCount } = props
const [isExpanded, setIsExpanded] = useState({
...COMMUNITIES_EXPAND_CONTROL,
...communitiesExpandControl,
welcome: true,
community: true,
design: true,
})
const [selectedChannel, setSelectedChannel] = useState('welcome')
const handleToggle = (id: string) => {
setIsExpanded(prev => ({
...prev,
@ -231,23 +71,23 @@ const Sidebar = (props: Props) => {
zIndex={10}
>
<Stack paddingHorizontal={16} paddingBottom={16}>
<Stack marginTop={-32} marginBottom={12}>
<Stack marginTop={-40} marginBottom={12}>
<Avatar
withOutline
src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.seadn.io%2Fgae%2FFG0QJ00fN3c_FWuPeUr9-T__iQl63j9hn5d6svW8UqOmia5zp3lKHPkJuHcvhZ0f_Pd6P2COo9tt9zVUvdPxG_9BBw%3Fw%3D500%26auto%3Dformat&f=1&nofb=1&ipt=c177cd71d8d0114080cfc6efd3f9e098ddaeb1b347919bd3089bf0aacb003b3e&ipo=images"
size={56}
size={80}
/>
</Stack>
<Heading marginBottom={16}>{name}</Heading>
<Paragraph marginBottom={12}>{description}</Paragraph>
<Stack flexDirection="row" alignItems="center" mb={12}>
<Group color="$neutral-100" size={16} />
<GroupIcon color="$neutral-100" size={16} />
<Paragraph ml={8}>{membersCount}</Paragraph>
</Stack>
<Button>Join community</Button>
</Stack>
{COMMUNITIES.map(community => (
{communities.map(community => (
<Accordion
key={community.id}
isExpanded={!!isExpanded[community.id as keyof typeof isExpanded]}
@ -258,7 +98,10 @@ const Sidebar = (props: Props) => {
!isExpanded[community.id as keyof typeof isExpanded]
}
>
{community.channels.map(channel => (
{community.channels.map((channel, index) => {
const isLastChannelOfTheList =
index === community.channels.length - 1
return (
<AccordionItem
key={channel.id}
icon={channel.icon}
@ -266,9 +109,11 @@ const Sidebar = (props: Props) => {
channelStatus={channel.channelStatus}
numberOfMessages={channel.numberOfMessages}
isSelected={selectedChannel === channel.id}
onPress={() => setSelectedChannel(channel.id)}
onPress={() => onChannelPress(channel.id)}
mb={isLastChannelOfTheList ? 8 : 0}
/>
))}
)
})}
</Accordion>
))}
<Stack borderBottomColor="$neutral-10" borderBottomWidth={1} />

View File

@ -0,0 +1,30 @@
import { memo } from 'react'
import { Path, Svg } from 'react-native-svg'
import { themed } from './themed'
import type { IconProps } from './types'
function Icon(props: IconProps) {
const { color, size = 20, ...rest } = props
return (
<Svg
width={size}
height={size}
viewBox={`0 0 ${size} ${size}`}
fill="none"
{...rest}
>
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M10 2.35A3.65 3.65 0 0 0 6.35 6v3a3.65 3.65 0 0 0 7.3 0V6A3.65 3.65 0 0 0 10 2.35ZM7.65 6a2.35 2.35 0 1 1 4.7 0v3a2.35 2.35 0 0 1-4.7 0V6Zm-2.5 1.5V9a4.85 4.85 0 1 0 9.7 0V7.5h1.3V9a6.15 6.15 0 0 1-5.5 6.116v1.234H12v1.3H8v-1.3h1.35v-1.234A6.15 6.15 0 0 1 3.85 9V7.5h1.3Z"
fill={color}
/>
</Svg>
)
}
export const AudioIcon = memo<IconProps>(themed(Icon))

View File

@ -1,12 +1,11 @@
import { memo } from 'react'
import { themed } from '@status-im/icons/src/themed'
import { Path, Svg } from 'react-native-svg'
import { themed } from '../themed'
import type { IconProps } from './types'
import type { IconProps } from '../IconProps'
const Icon = (props: IconProps) => {
function Icon(props: IconProps) {
const { color, size = 16, ...otherProps } = props
return (
@ -22,6 +21,4 @@ const Icon = (props: IconProps) => {
)
}
Icon.displayName = 'Chevron'
export const Chevron = memo<IconProps>(themed(Icon))
export const ChevronIcon = memo<IconProps>(themed(Icon))

View File

@ -0,0 +1,32 @@
import { memo } from 'react'
import { ClipPath, Defs, G, Path, Svg } from 'react-native-svg'
import { themed } from './themed'
import type { IconProps } from './types'
function Icon(props: IconProps) {
const { color, size = 20, ...rest } = props
return (
<Svg
width={size}
height={size}
viewBox={`0 0 ${size} ${size}`}
fill="none"
{...rest}
>
<G clipPath="url(#format-icon-a)" fill={color}>
<Path d="M4.343 14.047a.858.858 0 1 1-1.622-.558l2.575-7.314a1.493 1.493 0 0 1 2.817 0l2.578 7.314a.858.858 0 1 1-1.621.559L6.753 7.15a.051.051 0 0 0-.098 0l-2.312 6.896Zm-.137-2.434c0-.38.308-.688.688-.688h3.61a.688.688 0 0 1 0 1.376h-3.61a.688.688 0 0 1-.688-.688ZM13.82 14.823c-.449 0-.857-.085-1.223-.254a2.1 2.1 0 0 1-.872-.744c-.216-.326-.324-.72-.324-1.181 0-.407.08-.736.24-.988.16-.256.374-.456.642-.6.268-.145.563-.253.886-.324.327-.073.654-.132.984-.175.43-.055.78-.097 1.048-.125.27-.03.467-.081.59-.152.127-.07.19-.194.19-.37v-.036c0-.456-.125-.81-.374-1.062-.246-.252-.62-.379-1.122-.379-.52 0-.928.114-1.223.342-.13.1-.244.204-.34.311-.193.213-.49.344-.76.248-.303-.108-.457-.453-.275-.717.133-.192.285-.355.456-.488.311-.243.65-.413 1.016-.508.37-.099.732-.148 1.09-.148.227 0 .489.028.784.083.299.053.586.162.863.328.28.166.513.417.697.752.185.336.277.785.277 1.349v4.127c0 .3-.244.544-.544.544h-.065a.48.48 0 0 1-.48-.48v-.446a.034.034 0 0 0-.034-.034.035.035 0 0 0-.03.02 1.96 1.96 0 0 1-.36.474 2.131 2.131 0 0 1-.689.448c-.286.123-.635.185-1.048.185Zm.167-.98c.43 0 .794-.084 1.09-.253a1.729 1.729 0 0 0 .904-1.5v-.653c0-.117-.193-.225-.305-.192a5.124 5.124 0 0 1-.535.115 24.109 24.109 0 0 1-1.044.139 4.158 4.158 0 0 0-.794.18c-.243.08-.44.201-.59.364-.148.16-.222.379-.222.656 0 .379.14.665.42.859.283.19.642.286 1.076.286Z" />
</G>
<Defs>
<ClipPath id="format-icon-a">
<Path d="M0 0h20v20H0z" />
</ClipPath>
</Defs>
</Svg>
)
}
export const FormatIcon = memo<IconProps>(themed(Icon))

View File

@ -1,12 +1,11 @@
import { memo } from 'react'
import { themed } from '@status-im/icons/src/themed'
import { Path, Svg } from 'react-native-svg'
import { themed } from '../themed'
import type { IconProps } from './types'
import type { IconProps } from '../IconProps'
const Icon = (props: IconProps) => {
function Icon(props: IconProps) {
const { color, size = 16, ...otherProps } = props
return (
@ -24,16 +23,14 @@ const Icon = (props: IconProps) => {
fillRule="evenodd"
clipRule="evenodd"
d="M4.6 5a1.9 1.9 0 1 1 3.8 0 1.9 1.9 0 0 1-3.8 0Zm1.9-3.1a3.1 3.1 0 1 0 0 6.2 3.1 3.1 0 0 0 0-6.2ZM5 8.9a3.6 3.6 0 0 0-3.6 3.6A1.6 1.6 0 0 0 3 14.1h6a1.6 1.6 0 0 0 1.6-1.6A3.6 3.6 0 0 0 7 8.9H5Zm-2.4 3.6A2.4 2.4 0 0 1 5 10.1h2a2.4 2.4 0 0 1 2.4 2.4.4.4 0 0 1-.4.4H3a.4.4 0 0 1-.4-.4Z"
fill={`${color}`}
fill={color}
/>
<Path
d="M10 3.1a1.9 1.9 0 0 1 0 3.8v1.2a3.1 3.1 0 0 0 0-6.2v1.2ZM11 10.1h.5a2.4 2.4 0 0 1 2.4 2.4.4.4 0 0 1-.4.4h-2v1.2h2a1.6 1.6 0 0 0 1.6-1.6 3.6 3.6 0 0 0-3.6-3.6H11v1.2Z"
fill={`${color}`}
fill={color}
/>
</Svg>
)
}
Icon.displayName = 'Group'
export const Group = memo<IconProps>(themed(Icon))
export const GroupIcon = memo<IconProps>(themed(Icon))

View File

@ -0,0 +1,24 @@
import { memo } from 'react'
import { Path, Svg } from 'react-native-svg'
import { themed } from './themed'
import type { IconProps } from './types'
function Icon(props: IconProps) {
const { color, size = 20, ...rest } = props
return (
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} {...rest}>
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M9.9 2.85h-.029c-1.096 0-1.958 0-2.651.057-.707.057-1.296.177-1.831.45a4.65 4.65 0 0 0-2.032 2.032c-.273.535-.393 1.124-.45 1.83-.057.694-.057 1.556-.057 2.652v.258c0 1.096 0 1.958.057 2.651.057.707.177 1.296.45 1.831a4.65 4.65 0 0 0 2.032 2.032c.535.273 1.124.393 1.83.45.694.057 1.556.057 2.652.057h.258c1.096 0 1.958 0 2.651-.057.707-.057 1.296-.177 1.831-.45a4.65 4.65 0 0 0 2.032-2.032c.273-.535.393-1.124.45-1.83.057-.694.057-1.556.057-2.652V9.87c0-1.096 0-1.958-.057-2.651-.057-.707-.177-1.296-.45-1.831a4.65 4.65 0 0 0-2.032-2.032c-.535-.273-1.124-.393-1.83-.45-.694-.057-1.556-.057-2.652-.057H9.9ZM5.98 4.515c.32-.163.719-.261 1.346-.313.634-.051 1.443-.052 2.574-.052h.2c1.13 0 1.94 0 2.574.052.627.052 1.026.15 1.347.313a3.35 3.35 0 0 1 1.464 1.464c.163.321.261.72.313 1.347.052.634.052 1.443.052 2.574v.2c0 1.13 0 1.94-.052 2.574a8.05 8.05 0 0 1-.024.243 5.089 5.089 0 0 0-2.754-.549 5.197 5.197 0 0 0-2.284.763 8.646 8.646 0 0 0-6.58-4.703c.007-.424.02-.785.046-1.102.052-.627.15-1.026.313-1.347A3.35 3.35 0 0 1 5.98 4.515Zm5.256 9.841c.155.483.268.98.336 1.487.424-.006.785-.02 1.102-.045.627-.052 1.026-.15 1.347-.313a3.35 3.35 0 0 0 1.372-1.296 3.818 3.818 0 0 0-2.263-.525 3.936 3.936 0 0 0-1.894.692Zm-3.039-2.553a7.35 7.35 0 0 1 2.063 4.047H9.9c-1.13 0-1.94 0-2.574-.052-.627-.052-1.026-.15-1.347-.313a3.35 3.35 0 0 1-1.464-1.464c-.163-.321-.261-.72-.313-1.347-.051-.634-.052-1.443-.052-2.574v-.36a7.35 7.35 0 0 1 4.047 2.063ZM11.15 8a.85.85 0 1 1 1.7 0 .85.85 0 0 1-1.7 0ZM12 5.85a2.15 2.15 0 1 0 0 4.3 2.15 2.15 0 0 0 0-4.3Z"
fill={color}
/>
</Svg>
)
}
export const ImageIcon = memo<IconProps>(themed(Icon))

View File

@ -1,4 +1,11 @@
export { AudioIcon } from './audio-icon'
export { ChevronIcon } from './chevron-icon'
export { FormatIcon } from './format-icon'
export { GroupIcon } from './group-icon'
export { ImageIcon } from './image-icon'
export { LockedIcon } from './lock-icon'
export { MembersIcon } from './members-icon'
export { MutedIcon } from './muted-icon'
export { OptionsIcon } from './options-icon'
export { ReactionIcon } from './reaction-icon'
export type { IconProps } from './types'

View File

@ -1,24 +1,25 @@
import { memo } from 'react'
import { Path, Svg } from 'react-native-svg'
import { themed } from './themed'
import type { IconProps } from './types'
export function LockedIcon(props: IconProps) {
function Icon(props: IconProps) {
const { color = 'currentColor', size = 20, ...rest } = props
// FIXME: not need to differentiate sizes in the designs
return (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<path
<Svg width={size} height={size} viewBox="0 0 16 16" fill="none" {...rest}>
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M8 .9h-.012c-.081 0-.137 0-.186.002a3 3 0 00-2.9 2.9c-.002.05-.002.105-.002.186v1.175a3.6 3.6 0 00-2.127 2.405c-.123.46-.123.998-.123 1.832v.2c0 .834 0 1.373.123 1.832a3.6 3.6 0 002.545 2.545c.46.123.998.123 1.832.123H8.85c.834 0 1.373 0 1.832-.123a3.6 3.6 0 002.545-2.545c.123-.46.123-.998.123-1.832v-.2c0-.834 0-1.373-.123-1.832A3.6 3.6 0 0011.1 5.163V3.988c0-.081 0-.137-.002-.186A3 3 0 008.012.9H8zm.75 5.2h-1.5c-.969 0-1.335.005-1.621.082a2.4 2.4 0 00-1.697 1.697c-.077.286-.082.652-.082 1.621s.005 1.335.082 1.621a2.4 2.4 0 001.697 1.697c.286.077.652.082 1.621.082h1.5c.969 0 1.335-.005 1.621-.082a2.4 2.4 0 001.697-1.697c.077-.286.082-.652.082-1.621s-.005-1.335-.082-1.621a2.4 2.4 0 00-1.697-1.697C10.085 6.105 9.72 6.1 8.75 6.1zm.288-1.2H9.9V4v-.159a1.8 1.8 0 00-1.741-1.74 5.726 5.726 0 00-.318 0 1.8 1.8 0 00-1.74 1.74V4.9H9.037zM7.4 8.25h1.2v2.5H7.4v-2.5z"
fill={color}
/>
</svg>
</Svg>
)
}
export const LockedIcon = memo<IconProps>(themed(Icon))

View File

@ -1,29 +1,30 @@
import { memo } from 'react'
import { Path, Svg } from 'react-native-svg'
import { themed } from './themed'
import type { IconProps } from './types'
export function MembersIcon(props: IconProps) {
function Icon(props: IconProps) {
const { color = 'currentColor', size = 20, ...rest } = props
return (
<svg
width={size}
height={size}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<path
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none" {...rest}>
<Path
fillRule="evenodd"
clipRule="evenodd"
d="M6.15 6a1.85 1.85 0 113.7 0 1.85 1.85 0 01-3.7 0zM8 2.85a3.15 3.15 0 100 6.3 3.15 3.15 0 000-6.3zm-1.25 7.5a4.4 4.4 0 00-4.4 4.4c0 1.049.85 1.9 1.9 1.9h7.5a1.9 1.9 0 001.9-1.9 4.4 4.4 0 00-4.4-4.4h-2.5zm-3.1 4.4a3.1 3.1 0 013.1-3.1h2.5a3.1 3.1 0 013.1 3.1.6.6 0 01-.6.6h-7.5a.6.6 0 01-.6-.6z"
fill={color}
/>
<path
<Path
d="M13.5 11h.25a3.75 3.75 0 013.75 3.75v0c0 .69-.56 1.25-1.25 1.25H14.5M11.809 8c1.153 0 2.087-.895 2.087-2s-.934-2-2.087-2"
stroke={color}
strokeWidth={1.3}
strokeLinejoin="round"
/>
</svg>
</Svg>
)
}
export const MembersIcon = memo<IconProps>(themed(Icon))

View File

@ -1,12 +1,11 @@
import { memo } from 'react'
import { themed } from '@status-im/icons/src/themed'
import { Path, Svg } from 'react-native-svg'
import { themed } from '../themed'
import type { IconProps } from './types'
import type { IconProps } from '../IconProps'
const Icon = (props: IconProps) => {
function Icon(props: IconProps) {
const { color, size = 16, ...otherProps } = props
return (
@ -27,6 +26,4 @@ const Icon = (props: IconProps) => {
)
}
Icon.displayName = 'Muted'
export const Muted = memo<IconProps>(themed(Icon))
export const MutedIcon = memo<IconProps>(themed(Icon))

View File

@ -1,20 +1,21 @@
import { memo } from 'react'
import { Circle, Svg } from 'react-native-svg'
import { themed } from './themed'
import type { IconProps } from './types'
export function OptionsIcon(props: IconProps) {
function Icon(props: IconProps) {
const { color = 'currentColor', size = 20, ...rest } = props
return (
<svg
width={size}
height={size}
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...rest}
>
<circle cx={4.5} cy={10} r={1.5} fill={color} />
<circle cx={10} cy={10} r={1.5} fill={color} />
<circle cx={15.5} cy={10} r={1.5} fill={color} />
</svg>
<Svg width={size} height={size} viewBox="0 0 20 20" fill="none" {...rest}>
<Circle cx={4.5} cy={10} r={1.5} fill={color} />
<Circle cx={10} cy={10} r={1.5} fill={color} />
<Circle cx={15.5} cy={10} r={1.5} fill={color} />
</Svg>
)
}
export const OptionsIcon = memo<IconProps>(themed(Icon))

View File

@ -0,0 +1,32 @@
import { memo } from 'react'
import { Circle, Path, Svg } from 'react-native-svg'
import { themed } from './themed'
import type { IconProps } from './types'
export function Icon(props: IconProps) {
const { color = 'currentColor', size = 20, ...rest } = props
return (
<Svg
width={size}
height={size}
viewBox={`0 0 ${size} ${size}`}
fill="none"
{...rest}
>
<Circle cx="10" cy="10" r="6.75" stroke={color} strokeWidth="1.3" />
<Path
d="M7.525 12a3.5 3.5 0 0 0 4.95 0"
stroke={color}
strokeWidth="1.3"
/>
<Circle cx="8" cy="8.5" r="1" fill={color} />
<Circle cx="12" cy="8.5" r="1" fill={color} />
</Svg>
)
}
export const ReactionIcon = memo<IconProps>(themed(Icon))

View File

@ -1,6 +1,6 @@
import { useCurrentColor } from 'tamagui'
import type { IconProps } from './IconProps'
import type { IconProps } from './types'
import type React from 'react'
export function themed(Component: React.ElementType) {

View File

@ -1,9 +1,18 @@
import type { SVGAttributes } from 'react'
import type {
SizeTokens,
StyleObject,
ThemeParsed,
Tokens,
} from '@tamagui/core'
import type { SvgProps } from 'react-native-svg'
export interface IconProps extends SVGAttributes<SVGElement> {
children?: never
width?: never
height?: never
color?: string
size?: number
type GetTokenString<A> = A extends string ? `$${A}` : `$${string}`
export type ColorTokens =
| GetTokenString<keyof Tokens['color']>
| GetTokenString<keyof ThemeParsed>
export type IconProps = SvgProps & {
size?: number | SizeTokens
color?: ColorTokens
style?: StyleObject
}

101
yarn.lock
View File

@ -4421,6 +4421,48 @@
resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa"
integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==
"@react-navigation/core@^6.4.6":
version "6.4.6"
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.4.6.tgz#b0738667dec5927b01c4c496c2f4c73ef8a5e4dd"
integrity sha512-6zaAgUT5k4vhJlddUk2l52RZyMkMelHdrRv1cL57ALi2RZzERdgmbiMKhJerxFLn9S8E3PUe8vwxHzjHOZKG4w==
dependencies:
"@react-navigation/routers" "^6.1.6"
escape-string-regexp "^4.0.0"
nanoid "^3.1.23"
query-string "^7.1.3"
react-is "^16.13.0"
use-latest-callback "^0.1.5"
"@react-navigation/elements@^1.3.13":
version "1.3.13"
resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.13.tgz#5105fa26df8d32810cd9f14d6ec5a3d2c2bb26d2"
integrity sha512-LqqK5s2ZfYHn2cQ376jC5V9dQztLH5ixkkJj9WR7JY2g4SghDd39WJhL3Jillw1Mu3F3b9sZwvAK+QkXhnDeAA==
"@react-navigation/native-stack@^6.9.8":
version "6.9.8"
resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-6.9.8.tgz#c953a169918a4bdde56f7d2dc1073da4726b4cb7"
integrity sha512-74dje939lflsTXJQwCAdznbJ4B6V8sA5CSzuHwbiogL8B6EVXNa/qliXtB7DBAvzeyWDWT3u+gM2vOYJOeXYhA==
dependencies:
"@react-navigation/elements" "^1.3.13"
warn-once "^0.1.0"
"@react-navigation/native@^6.1.2":
version "6.1.2"
resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-6.1.2.tgz#6fffbf4787c233687fff8fe9ce7364ffce696d38"
integrity sha512-qLUe0asHofr5EhxKjvUBJ9DrPPmR4535IEwmW3oU4DRb3cLbNysjajJKHL8kcYtqPvn9Bx9QZG2x0PMb2vN23A==
dependencies:
"@react-navigation/core" "^6.4.6"
escape-string-regexp "^4.0.0"
fast-deep-equal "^3.1.3"
nanoid "^3.1.23"
"@react-navigation/routers@^6.1.6":
version "6.1.6"
resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-6.1.6.tgz#f57f2a73855d329255aa225fdad75ae8e7700c6d"
integrity sha512-Z5DeCW3pUvMafbU9Cjy1qJYC2Bvl8iy3+PfsB0DsAwQ6zZ3WAXW5FTMX4Gb9H+Jg6qHWGbMFFwlYpS3UJ3tlVQ==
dependencies:
nanoid "^3.1.23"
"@rollup/pluginutils@^4.2.0", "@rollup/pluginutils@^4.2.1":
version "4.2.1"
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
@ -8636,7 +8678,7 @@ decamelize@^5.0.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-5.0.1.tgz#db11a92e58c741ef339fb0a2868d8a06a9a7b1e9"
integrity sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==
decode-uri-component@^0.2.0:
decode-uri-component@^0.2.0, decode-uri-component@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
@ -10734,6 +10776,11 @@ fill-range@^7.0.1:
dependencies:
to-regex-range "^5.0.1"
filter-obj@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
finalhandler@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@ -13980,7 +14027,7 @@ nanoid@^3.0.2:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
nanoid@^3.3.1, nanoid@^3.3.4:
nanoid@^3.1.23, nanoid@^3.3.1, nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
@ -15281,6 +15328,16 @@ qs@6.11.0, qs@^6.10.0, qs@^6.4.0:
dependencies:
side-channel "^1.0.4"
query-string@^7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328"
integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==
dependencies:
decode-uri-component "^0.2.2"
filter-obj "^1.1.0"
split-on-first "^1.0.0"
strict-uri-encode "^2.0.0"
querystringify@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
@ -15387,6 +15444,11 @@ react-element-to-jsx-string@^15.0.0:
is-plain-object "5.0.0"
react-is "18.1.0"
react-freeze@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.3.tgz#5e3ca90e682fed1d73a7cb50c2c7402b3e85618d"
integrity sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g==
react-inspector@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-6.0.1.tgz#1a37f0165d9df81ee804d63259eaaeabe841287d"
@ -15402,7 +15464,7 @@ react-is@18.1.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-is@^16.13.1:
react-is@^16.13.0, react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -15427,6 +15489,19 @@ react-native-gradle-plugin@^0.70.3:
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.70.3.tgz#cbcf0619cbfbddaa9128701aa2d7b4145f9c4fc8"
integrity sha512-oOanj84fJEXUg9FoEAQomA8ISG+DVIrTZ3qF7m69VQUJyOGYyDZmPqKcjvRku4KXlEH6hWO9i4ACLzNBh8gC0A==
react-native-safe-area-context@4.4.1:
version "4.4.1"
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.4.1.tgz#239c60b8a9a80eac70a38a822b04c0f1d15ffc01"
integrity sha512-N9XTjiuD73ZpVlejHrUWIFZc+6Z14co1K/p1IFMkImU7+avD69F3y+lhkqA2hN/+vljdZrBSiOwXPkuo43nFQA==
react-native-screens@~3.18.0:
version "3.18.2"
resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-3.18.2.tgz#d7ab2d145258d3db9fa630fa5379dc4474117866"
integrity sha512-ANUEuvMUlsYJ1QKukEhzhfrvOUO9BVH9Nzg+6eWxpn3cfD/O83yPBOF8Mx6x5H/2+sMy+VS5x/chWOOo/U7QJw==
dependencies:
react-freeze "^1.0.0"
warn-once "^0.1.0"
react-native-svg@^13.7.0:
version "13.7.0"
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-13.7.0.tgz#be2ffb935e996762543dd7376bdc910722f7a43c"
@ -16566,6 +16641,11 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95"
integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==
split-on-first@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@ -16658,6 +16738,11 @@ stream-transform@^2.1.3:
dependencies:
mixme "^0.5.1"
strict-uri-encode@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
string-argv@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
@ -17866,6 +17951,11 @@ use-isomorphic-layout-effect@^1.1.1:
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
use-latest-callback@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.5.tgz#a4a836c08fa72f6608730b5b8f4bbd9c57c04f51"
integrity sha512-HtHatS2U4/h32NlkhupDsPlrbiD27gSH5swBdtXbCAlc6pfOFzaj0FehW/FO12rx8j2Vy4/lJScCiJyM01E+bQ==
use-sidecar@^1.0.1:
version "1.0.5"
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.5.tgz#ffff2a17c1df42e348624b699ba6e5c220527f2b"
@ -18081,6 +18171,11 @@ walker@^1.0.7, walker@^1.0.8:
dependencies:
makeerror "1.0.12"
warn-once@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/warn-once/-/warn-once-0.1.1.tgz#952088f4fb56896e73fd4e6a3767272a3fccce43"
integrity sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==
watchpack@^2.2.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"