diff --git a/apps/mobile/app/[site_id]/(tabs)/_layout.tsx b/apps/mobile/app/[site_id]/(tabs)/_layout.tsx index 6c18015b..ca5c2064 100644 --- a/apps/mobile/app/[site_id]/(tabs)/_layout.tsx +++ b/apps/mobile/app/[site_id]/(tabs)/_layout.tsx @@ -2,9 +2,13 @@ import React from 'react'; import { Tabs } from 'expo-router'; import { SvgProps } from 'react-native-svg'; import HomeIcon from '@assets/icons/HomeIcon.svg'; +import HomeOutlineIcon from '@assets/icons/HomeOutlineIcon.svg'; import ProfileIcon from '@assets/icons/ProfileIcon.svg'; +import ProfileOutlineIcon from '@assets/icons/ProfileOutlineIcon.svg'; import ChatIcon from '@assets/icons/ChatIcon.svg'; +import ChatOutlineIcon from '@assets/icons/ChatOutlineIcon.svg'; import BellIcon from '@assets/icons/BellIcon.svg'; +import BellOutlineIcon from '@assets/icons/BellOutlineIcon.svg'; import { useColorScheme } from '@hooks/useColorScheme' export default function TabLayout() { @@ -14,14 +18,19 @@ export default function TabLayout() { // Common styles const tabBarStyle = { - backgroundColor: dark ? 'rgba(12, 10, 21, 0.8)' : 'rgba(255, 255, 255, 0.8)', + backgroundColor: dark ? 'rgba(08, 08, 08, 0.8)' : 'rgba(255, 255, 255, 0.8)', borderTopWidth: 1, - borderTopColor: dark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.05)', + borderTopColor: dark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.05)', paddingTop: 4, + shadowColor: dark ? '#000' : '#000', + shadowOffset: { width: 0, height: -2 }, + shadowOpacity: 0.03, + shadowRadius: 5, + elevation: 5 } const headerStyle = { - backgroundColor: dark ? 'rgba(12, 10, 21, 0)' : 'rgba(249, 249, 249, 1)', + backgroundColor: dark ? 'rgba(08, 08, 08, 0)' : 'rgba(249, 249, 249, 1)', borderBottomWidth: 1, borderBottomColor: dark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0)', } @@ -31,23 +40,30 @@ export default function TabLayout() { }) const getTabBarIcon = - (IconComponent: React.FC) => + (FilledIcon: React.FC, OutlineIcon: React.FC) => ({ color, focused }: { color: string; focused: boolean }) => - ( - - ) + focused ? ( + + ) : ( + + ) return ( diff --git a/apps/mobile/app/[site_id]/(tabs)/home/_layout.tsx b/apps/mobile/app/[site_id]/(tabs)/home/_layout.tsx index 22a4a853..29f5c09c 100644 --- a/apps/mobile/app/[site_id]/(tabs)/home/_layout.tsx +++ b/apps/mobile/app/[site_id]/(tabs)/home/_layout.tsx @@ -8,12 +8,12 @@ const HomeLayout = () => { return ( ) diff --git a/apps/mobile/app/[site_id]/(tabs)/home/index.tsx b/apps/mobile/app/[site_id]/(tabs)/home/index.tsx index 4cb38065..b8f5b0c5 100644 --- a/apps/mobile/app/[site_id]/(tabs)/home/index.tsx +++ b/apps/mobile/app/[site_id]/(tabs)/home/index.tsx @@ -1,119 +1,53 @@ -import { View } from 'react-native'; +import { Pressable, SafeAreaView, ScrollView, View } from 'react-native'; import { ThemeToggle } from '@components/nativewindui/ThemeToggle'; +import { Text } from '@components/nativewindui/Text'; +import { useColorScheme } from '@hooks/useColorScheme'; +import PlusIcon from '@assets/icons/PlusIcon.svg'; +import { SearchInput } from '@components/nativewindui/SearchInput'; import ChannelList from '@components/features/chat/ChannelList/ChannelList'; -import { useRouter } from 'expo-router'; +import DMList from '@components/features/chat/DMList/DMList'; export default function Home() { - const router = useRouter() - const handleChannelSelect = (channelId: string) => { - router.push('../chat/' + channelId) - } + const { colors } = useColorScheme() return ( - - - console.log('channel long pressed')} /> - + + + + console.log('Workspace pressed')}> + Workspace + + + + + + + + + + + + console.log('Create channel pressed')}> + + Add teammates + + + + + ) +} + +const Divider = () => { + const { colors } = useColorScheme() + return ( + ) } \ No newline at end of file diff --git a/apps/mobile/assets/icons/BellOutlineIcon.svg b/apps/mobile/assets/icons/BellOutlineIcon.svg new file mode 100644 index 00000000..6a3d2311 --- /dev/null +++ b/apps/mobile/assets/icons/BellOutlineIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/mobile/assets/icons/ChatOutlineIcon.svg b/apps/mobile/assets/icons/ChatOutlineIcon.svg new file mode 100644 index 00000000..8c361929 --- /dev/null +++ b/apps/mobile/assets/icons/ChatOutlineIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/mobile/assets/icons/HomeIcon.svg b/apps/mobile/assets/icons/HomeIcon.svg index da1088a9..fed5cc35 100644 --- a/apps/mobile/assets/icons/HomeIcon.svg +++ b/apps/mobile/assets/icons/HomeIcon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/apps/mobile/assets/icons/HomeOutlineIcon.svg b/apps/mobile/assets/icons/HomeOutlineIcon.svg new file mode 100644 index 00000000..554c6eb7 --- /dev/null +++ b/apps/mobile/assets/icons/HomeOutlineIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/mobile/assets/icons/ProfileOutlineIcon.svg b/apps/mobile/assets/icons/ProfileOutlineIcon.svg new file mode 100644 index 00000000..d9120d93 --- /dev/null +++ b/apps/mobile/assets/icons/ProfileOutlineIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/mobile/components/features/chat/ChannelList/ChannelList.tsx b/apps/mobile/components/features/chat/ChannelList/ChannelList.tsx index a133d63c..3bb31042 100644 --- a/apps/mobile/components/features/chat/ChannelList/ChannelList.tsx +++ b/apps/mobile/components/features/chat/ChannelList/ChannelList.tsx @@ -1,93 +1,9 @@ -import React, { useState } from 'react'; -import { View, TouchableOpacity, Pressable, StyleSheet } from 'react-native'; -import { ChannelListItem } from '../../../../types/channels'; -import { ChannelIcon } from './ChannelIcon'; -import { Text } from '@components/nativewindui/Text'; -import ChevronDownIcon from '@assets/icons/ChevronDownIcon.svg'; -import ChevronRightIcon from '@assets/icons/ChevronRightIcon.svg'; -import { useColorScheme } from '@hooks/useColorScheme'; -import PlusIcon from '@assets/icons/PlusIcon.svg'; +import useGetChannels from '@raven/lib/hooks/useGetChannels'; +import ChannelListUI from './ChannelListUI'; -interface ChannelListProps { - channels: ChannelListItem[]; - onChannelSelect: (channelId: string) => void; - onLongPress: (channelId: string) => void; +const ChannelList = () => { + const { channels } = useGetChannels({ showArchived: false }) + return } -const ChannelList = ({ channels, onChannelSelect, onLongPress }: ChannelListProps) => { - - const [isExpanded, setIsExpanded] = useState(true) - const colors = useColorScheme() - - const toggleAccordion = () => { - setIsExpanded((prev) => !prev) - } - - return ( - - - Channels - {isExpanded ? : } - - {isExpanded && <> - {channels.map((channel) => ( - onChannelSelect(channel.name)} - onLongPress={() => onLongPress(channel.name)} - style={styles.channelRow} - > - - {channel.channel_name} - - ))} - console.log('Create channel pressed')}> - - Add Channel - - } - - ) -} - -const styles = StyleSheet.create({ - container: { - padding: 10, - }, - header: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingVertical: 12, - paddingHorizontal: 10, - }, - headerText: { - fontWeight: '600', - fontSize: 16, - }, - channelRow: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: 6, - paddingHorizontal: 10, - borderRadius: 10, - }, - channelText: { - marginLeft: 12, - fontSize: 16, - }, - addChannelButton: { - flexDirection: 'row', - alignItems: 'center', - paddingVertical: 12, - paddingHorizontal: 10, - borderRadius: 10, - }, - addChannelText: { - marginLeft: 12, - fontSize: 16, - }, -}) - -export default ChannelList +export default ChannelList \ No newline at end of file diff --git a/apps/mobile/components/features/chat/ChannelList/ChannelListUI.tsx b/apps/mobile/components/features/chat/ChannelList/ChannelListUI.tsx new file mode 100644 index 00000000..8ce478a3 --- /dev/null +++ b/apps/mobile/components/features/chat/ChannelList/ChannelListUI.tsx @@ -0,0 +1,93 @@ +import { useState } from 'react'; +import { View, TouchableOpacity, Pressable, StyleSheet } from 'react-native'; +import { ChannelIcon } from './ChannelIcon'; +import { Text } from '@components/nativewindui/Text'; +import ChevronDownIcon from '@assets/icons/ChevronDownIcon.svg'; +import ChevronRightIcon from '@assets/icons/ChevronRightIcon.svg'; +import { useColorScheme } from '@hooks/useColorScheme'; +import PlusIcon from '@assets/icons/PlusIcon.svg'; +import { ChannelListItem } from '@raven/types/common/ChannelListItem'; +import { Link } from 'expo-router'; + +interface ChannelListUIProps { + channels: ChannelListItem[]; +} + +const ChannelListUI = ({ channels }: ChannelListUIProps) => { + + const [isExpanded, setIsExpanded] = useState(true) + const colors = useColorScheme() + + const toggleAccordion = () => { + setIsExpanded((prev) => !prev) + } + + return ( + + + Channels + {isExpanded ? : } + + {isExpanded && <> + {channels.map((channel) => )} + console.log('Create channel pressed')}> + + Add channel + + } + + ) +} + +const ChannelListRow = ({ channel }: { channel: ChannelListItem }) => { + const colors = useColorScheme() + return ( + + console.log(`channel long pressed - ${channel.name}`)} + // Use tailwind classes for layout and ios:active state + className={`flex-row items-center px-3 py-2 rounded-lg ios:active:bg-[${colors.colors.linkColor}] ios:active:dark:bg-[${colors.colors.linkColor}]`} + // Add a subtle ripple effect on Android + android_ripple={{ color: 'rgba(0,0,0,0.1)', borderless: false }} + > + + {channel.channel_name} + + + ) +} + +const styles = StyleSheet.create({ + container: { + padding: 10, + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 12, + paddingHorizontal: 10, + }, + headerText: { + fontWeight: '600', + fontSize: 16, + }, + channelText: { + marginLeft: 12, + fontSize: 16, + }, + addChannelButton: { + flexDirection: 'row', + alignItems: 'center', + paddingVertical: 10, + paddingHorizontal: 10, + borderRadius: 10, + }, + addChannelText: { + marginLeft: 12, + fontSize: 16, + }, +}) + +export default ChannelListUI \ No newline at end of file diff --git a/apps/mobile/components/features/chat/DMList/DMList.tsx b/apps/mobile/components/features/chat/DMList/DMList.tsx new file mode 100644 index 00000000..d0722aeb --- /dev/null +++ b/apps/mobile/components/features/chat/DMList/DMList.tsx @@ -0,0 +1,9 @@ +import useGetDirectMessageChannels from '@raven/lib/hooks/useGetDirectMessageChannels'; +import DMListUI from './DMListUI'; + +const DMList = () => { + const { dmChannels } = useGetDirectMessageChannels() + return +} + +export default DMList \ No newline at end of file diff --git a/apps/mobile/components/features/chat/DMList/DMListUI.tsx b/apps/mobile/components/features/chat/DMList/DMListUI.tsx new file mode 100644 index 00000000..003291e6 --- /dev/null +++ b/apps/mobile/components/features/chat/DMList/DMListUI.tsx @@ -0,0 +1,77 @@ +import { useState } from 'react'; +import { View, Pressable, StyleSheet, TouchableOpacity } from 'react-native'; +import { Text } from '@components/nativewindui/Text'; +import { useColorScheme } from '@hooks/useColorScheme'; +import ChevronDownIcon from '@assets/icons/ChevronDownIcon.svg'; +import ChevronRightIcon from '@assets/icons/ChevronRightIcon.svg'; +import { DMChannelListItem } from '@raven/types/common/ChannelListItem'; +import { useGetUser } from '@raven/lib/hooks/useGetUser'; +import UserAvatar from '@components/layout/UserAvatar'; +import { Link } from 'expo-router'; + +interface DMListUIProps { + dms: DMChannelListItem[] +} + +const DMListUI = ({ dms }: DMListUIProps) => { + + const [isExpanded, setIsExpanded] = useState(true) + const colors = useColorScheme() + + const toggleAccordion = () => { + setIsExpanded((prev) => !prev) + } + + return ( + + + Direct Messages + {isExpanded ? : } + + {isExpanded && <> + {dms.map((dm) => )} + } + + ) +} + +const DMListRow = ({ dm }: { dm: DMChannelListItem }) => { + const user = useGetUser(dm.peer_user_id) + const colors = useColorScheme() + return ( + + + + {user?.full_name} + + + ) +} + +const styles = StyleSheet.create({ + container: { + padding: 10, + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 12, + paddingHorizontal: 10, + }, + headerText: { + fontWeight: '600', + fontSize: 16, + }, + dmChannelText: { + marginLeft: 12, + fontSize: 16, + }, +}) + +export default DMListUI; \ No newline at end of file diff --git a/apps/mobile/components/nativewindui/SearchInput/SearchInput.ios.tsx b/apps/mobile/components/nativewindui/SearchInput/SearchInput.ios.tsx index 204d0f96..d2be82b6 100644 --- a/apps/mobile/components/nativewindui/SearchInput/SearchInput.ios.tsx +++ b/apps/mobile/components/nativewindui/SearchInput/SearchInput.ios.tsx @@ -23,7 +23,6 @@ import { Text } from '@components/nativewindui/Text'; import { cn } from '@lib/cn'; import { useColorScheme } from '@hooks/useColorScheme'; -// Add as class when possible: https://github.com/marklawlor/nativewind/issues/522 const BORDER_CURVE: ViewStyle = { borderCurve: 'continuous', }; @@ -34,7 +33,7 @@ const SearchInput = React.forwardRef, SearchI value: valueProp, onChangeText: onChangeTextProp, onFocus: onFocusProp, - placeholder = 'Search...', + placeholder = 'Jump to or search...', cancelText = 'Cancel', containerClassName, iconContainerClassName, @@ -45,6 +44,7 @@ const SearchInput = React.forwardRef, SearchI ref ) => { const { colors } = useColorScheme(); + const inputRef = useAugmentedRef({ ref, methods: { focus, blur, clear } }); const [showCancel, setShowCancel] = React.useState(false); const showCancelDerivedValue = useDerivedValue(() => showCancel, [showCancel]); @@ -58,7 +58,6 @@ const SearchInput = React.forwardRef, SearchI const rootStyle = useAnimatedStyle(() => { if (_WORKLET) { - // safely use measure const measurement = measure(animatedRef); return { paddingRight: showCancelDerivedValue.value @@ -72,9 +71,9 @@ const SearchInput = React.forwardRef, SearchI : withTiming(0), }; }); + const buttonStyle3 = useAnimatedStyle(() => { if (_WORKLET) { - // safely use measure const measurement = measure(animatedRef); return { position: 'absolute', @@ -126,20 +125,23 @@ const SearchInput = React.forwardRef, SearchI + className={cn( + 'bg-card/15 flex-1 flex-row rounded-lg', + containerClassName + )}> - + , SearchI onFocus={onFocus} clearButtonMode="while-editing" role="searchbox" + placeholderTextColor={colors.greyText} {...props} /> @@ -163,7 +166,7 @@ const SearchInput = React.forwardRef, SearchI disabled={!showCancel} pointerEvents={!showCancel ? 'none' : 'auto'} className="flex-1 justify-center active:opacity-50"> - {cancelText} + {cancelText} diff --git a/apps/mobile/components/nativewindui/SearchInput/SearchInput.tsx b/apps/mobile/components/nativewindui/SearchInput/SearchInput.tsx index 2d6fcceb..dd0734c7 100644 --- a/apps/mobile/components/nativewindui/SearchInput/SearchInput.tsx +++ b/apps/mobile/components/nativewindui/SearchInput/SearchInput.tsx @@ -16,7 +16,7 @@ const SearchInput = React.forwardRef, SearchI { value: valueProp, onChangeText: onChangeTextProp, - placeholder = 'Search...', + placeholder = 'Jump to or search...', cancelText = 'Cancel', containerClassName, iconContainerClassName, @@ -55,7 +55,7 @@ const SearchInput = React.forwardRef, SearchI )} onPress={focus}> - + @@ -63,7 +63,7 @@ const SearchInput = React.forwardRef, SearchI ref={inputRef} placeholder={placeholder} className={cn('text-foreground flex-1 rounded-r-full p-2 text-[17px]', className)} - placeholderTextColor={colors.grey2} + placeholderTextColor={colors.greyText} value={value} onChangeText={onChangeText} role="searchbox" @@ -73,7 +73,7 @@ const SearchInput = React.forwardRef, SearchI {!!value && ( - + )} diff --git a/apps/mobile/components/nativewindui/ThemeToggle.tsx b/apps/mobile/components/nativewindui/ThemeToggle.tsx index 2acd12fd..277ca97d 100644 --- a/apps/mobile/components/nativewindui/ThemeToggle.tsx +++ b/apps/mobile/components/nativewindui/ThemeToggle.tsx @@ -24,12 +24,12 @@ export function ThemeToggle() { {colorScheme === 'dark' ? ({ pressed }) => ( - + ) : ({ pressed }) => ( - + )} diff --git a/apps/mobile/theme/colors.ts b/apps/mobile/theme/colors.ts index 2d07f918..272306c4 100644 --- a/apps/mobile/theme/colors.ts +++ b/apps/mobile/theme/colors.ts @@ -15,23 +15,27 @@ const IOS_SYSTEM_COLORS = { root: 'rgb(255, 255, 255)', card: 'rgb(255, 255, 255)', icon: '#1C2024', + greyText: 'rgb(175, 176, 180)', destructive: 'rgb(255, 56, 43)', primary: '#5753C6', + linkColor: '#E6E6EB', }, dark: { grey6: 'rgb(21, 21, 24)', - grey5: 'rgb(40, 40, 42)', + grey5: 'rgb(30, 30, 32)', grey4: 'rgb(55, 55, 57)', grey3: 'rgb(70, 70, 73)', grey2: 'rgb(99, 99, 102)', grey: 'rgb(142, 142, 147)', - background: 'rgb(0, 0, 0)', + background: 'rgb(10, 10, 10)', foreground: 'rgb(255, 255, 255)', root: 'rgb(0, 0, 0)', card: 'rgb(21, 21, 24)', icon: '#B9BBC6', + greyText: 'rgb(175, 176, 180)', destructive: 'rgb(254, 67, 54)', primary: '#5753C6', + linkColor: '#1A1A1A', }, } as const; @@ -50,23 +54,27 @@ const ANDROID_COLORS = { root: 'rgb(255, 255, 255)', card: 'rgb(255, 255, 255)', icon: '#1C2024', + greyText: 'rgb(175, 176, 180)', destructive: 'rgb(186, 26, 26)', primary: '#5753C6', + linkColor: '#E6E6EB', }, dark: { grey6: 'rgb(16, 19, 27)', - grey5: 'rgb(39, 42, 50)', + grey5: 'rgb(30, 30, 32)', grey4: 'rgb(49, 53, 61)', grey3: 'rgb(54, 57, 66)', grey2: 'rgb(139, 144, 160)', grey: 'rgb(193, 198, 215)', - background: 'rgb(0, 0, 0)', + background: 'rgb(10, 10, 10)', foreground: 'rgb(255, 255, 255)', root: 'rgb(0, 0, 0)', card: 'rgb(16, 19, 27)', icon: '#B9BBC6', + greyText: 'rgb(175, 176, 180)', destructive: 'rgb(147, 0, 10)', primary: '#5753C6', + linkColor: '#1A1A1A', }, } as const; diff --git a/apps/mobile/types/channels.ts b/apps/mobile/types/channels.ts deleted file mode 100644 index 6fa122cf..00000000 --- a/apps/mobile/types/channels.ts +++ /dev/null @@ -1,32 +0,0 @@ -export interface RavenChannel { - creation: string - name: string - modified: string - owner: string - modified_by: string - docstatus: 0 | 1 | 2 - parent?: string - parentfield?: string - parenttype?: string - idx?: number - /** Channel Name : Data */ - channel_name: string - /** Channel Description : Data */ - channel_description?: string - /** Type : Select */ - type: "Private" | "Public" | "Open" - /** Is Direct Message : Check */ - is_direct_message?: 0 | 1 - /** Is Self Message : Check */ - is_self_message?: 0 | 1 - /** Is Archived : Check */ - is_archived?: 0 | 1 - /** Last Message Timestamp : Datetime */ - last_message_timestamp?: string - /** Last Message Details : JSON */ - last_message_details?: any -} - -export type ChannelListItem = Pick \ No newline at end of file diff --git a/packages/lib/hooks/useGetDirectMessageChannels.ts b/packages/lib/hooks/useGetDirectMessageChannels.ts new file mode 100644 index 00000000..3770cd9d --- /dev/null +++ b/packages/lib/hooks/useGetDirectMessageChannels.ts @@ -0,0 +1,19 @@ +import { useContext } from 'react' +import { ChannelListContext } from '../providers/ChannelListProvider' + +/** + * Hook to get the DM channels from the channel list provider. Also returns if the channel list is loading or not. + * @param props + * @returns + */ +const useGetDirectMessageChannels = () => { + + const channelListContextData = useContext(ChannelListContext) + + return { + dmChannels: channelListContextData?.dm_channels ?? [], + isLoading: channelListContextData?.isLoading ?? false + } +} + +export default useGetDirectMessageChannels \ No newline at end of file