mirror of
https://github.com/KoalaSat/nostros.git
synced 2024-09-29 22:50:43 +00:00
316 lines
9.0 KiB
TypeScript
316 lines
9.0 KiB
TypeScript
import React, { useContext, useEffect, useState } from 'react'
|
|
import {
|
|
Clipboard,
|
|
Dimensions,
|
|
FlatList,
|
|
ListRenderItem,
|
|
ScrollView,
|
|
StyleSheet,
|
|
View,
|
|
} from 'react-native'
|
|
import { AppContext } from '../../Contexts/AppContext'
|
|
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
|
import { EventKind } from '../../lib/nostr/Events'
|
|
import {
|
|
DirectMessage,
|
|
getGroupedDirectMessages,
|
|
} from '../../Functions/DatabaseFunctions/DirectMessages'
|
|
import { getUsers, User } from '../../Functions/DatabaseFunctions/Users'
|
|
import { getOtherPubKey } from '../../Functions/RelayFunctions/DirectMessages'
|
|
import { NostrosAvatar } from '../../Components/NostrosAvatar'
|
|
import { formatPubKey, username } from '../../Functions/RelayFunctions/Users'
|
|
import {
|
|
AnimatedFAB,
|
|
Badge,
|
|
Button,
|
|
Divider,
|
|
List,
|
|
Text,
|
|
TextInput,
|
|
TouchableRipple,
|
|
useTheme,
|
|
} from 'react-native-paper'
|
|
import { UserContext } from '../../Contexts/UserContext'
|
|
import { navigate } from '../../lib/Navigation'
|
|
import moment from 'moment'
|
|
import RBSheet from 'react-native-raw-bottom-sheet'
|
|
import { useTranslation } from 'react-i18next'
|
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
|
|
|
export const ConversationsFeed: React.FC = () => {
|
|
const theme = useTheme()
|
|
const { t } = useTranslation('common')
|
|
const { database } = useContext(AppContext)
|
|
const { publicKey, privateKey } = useContext(UserContext)
|
|
const { relayPool, lastEventId } = useContext(RelayPoolContext)
|
|
const [directMessages, settDirectMessages] = useState<DirectMessage[]>([])
|
|
const [sendPubKeyInput, setSendPubKeyInput] = useState<string>('')
|
|
const [users, setUsers] = useState<User[]>()
|
|
const bottomSheetCreateRef = React.useRef<RBSheet>(null)
|
|
const bottomSheetUserListRef = React.useRef<RBSheet>(null)
|
|
const bottomSheetPubKeyRef = React.useRef<RBSheet>(null)
|
|
|
|
useEffect(() => {
|
|
loadDirectMessages()
|
|
}, [lastEventId])
|
|
|
|
useEffect(() => {
|
|
loadDirectMessages()
|
|
subscribeDirectMessages()
|
|
}, [])
|
|
|
|
const loadDirectMessages: () => void = () => {
|
|
if (database && publicKey) {
|
|
getGroupedDirectMessages(database, {}).then((results) => {
|
|
if (results && results.length > 0) {
|
|
settDirectMessages(results)
|
|
const otherUsers = results.map((message) => getOtherPubKey(message, publicKey))
|
|
relayPool?.subscribe('directmessages-meta', [
|
|
{
|
|
kinds: [EventKind.meta],
|
|
authors: otherUsers,
|
|
},
|
|
])
|
|
getUsers(database, { contacts: true }).then(setUsers)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
const subscribeDirectMessages: () => void = async () => {
|
|
relayPool?.unsubscribeAll()
|
|
if (publicKey) {
|
|
relayPool?.subscribe('directmessages-user', [
|
|
{
|
|
kinds: [EventKind.directMessage],
|
|
authors: [publicKey],
|
|
},
|
|
{
|
|
kinds: [EventKind.directMessage],
|
|
'#p': [publicKey],
|
|
},
|
|
])
|
|
}
|
|
}
|
|
|
|
const renderConversationItem: ListRenderItem<DirectMessage> = ({ index, item }) => {
|
|
if (!publicKey || !privateKey) return <></>
|
|
|
|
const otherPubKey = getOtherPubKey(item, publicKey)
|
|
const user: User = users?.find((user) => user.id === otherPubKey) ?? { id: otherPubKey }
|
|
|
|
return (
|
|
<TouchableRipple
|
|
onPress={() =>
|
|
navigate('Conversation', { pubKey: user.id, conversationId: item.conversation_id })
|
|
}
|
|
>
|
|
<View key={user.id} style={styles.contactRow}>
|
|
<View style={styles.contactUser}>
|
|
<NostrosAvatar
|
|
name={user.name}
|
|
pubKey={user.id}
|
|
src={user.picture}
|
|
lud06={user.lnurl}
|
|
size={40}
|
|
/>
|
|
<View style={styles.contactName}>
|
|
<Text variant='titleSmall'>{username(user)}</Text>
|
|
</View>
|
|
</View>
|
|
<View style={styles.contactInfo}>
|
|
<View style={styles.contactName}>
|
|
<Text>{moment.unix(item.created_at).format('HH:mm DD-MM-YY')}</Text>
|
|
{!item.read && <Badge size={16}></Badge>}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</TouchableRipple>
|
|
)
|
|
}
|
|
|
|
const pastePubKey: () => void = () => {
|
|
Clipboard.getString().then((value) => {
|
|
setSendPubKeyInput(value ?? '')
|
|
})
|
|
}
|
|
|
|
const bottomSheetStyles = React.useMemo(() => {
|
|
return {
|
|
container: {
|
|
backgroundColor: theme.colors.background,
|
|
padding: 16,
|
|
borderTopRightRadius: 28,
|
|
borderTopLeftRadius: 28,
|
|
},
|
|
draggableIcon: {
|
|
backgroundColor: '#000',
|
|
},
|
|
}
|
|
}, [])
|
|
|
|
const createOptions = React.useMemo(() => {
|
|
return [
|
|
{
|
|
key: 1,
|
|
title: t('conversationsFeed.newMessageContact'),
|
|
left: () => (
|
|
<List.Icon
|
|
icon={() => <MaterialCommunityIcons name='account-multiple-plus-outline' size={25} />}
|
|
/>
|
|
),
|
|
onPress: async () => bottomSheetUserListRef.current?.open(),
|
|
},
|
|
{
|
|
key: 2,
|
|
title: t('conversationsFeed.addPubKey'),
|
|
left: () => (
|
|
<List.Icon
|
|
icon={() => <MaterialCommunityIcons name='account-multiple-plus-outline' size={25} />}
|
|
/>
|
|
),
|
|
onPress: async () => bottomSheetPubKeyRef.current?.open(),
|
|
},
|
|
]
|
|
}, [])
|
|
|
|
const renderUserItem: ListRenderItem<User> = ({ index, item }) => (
|
|
<TouchableRipple onPress={() => {}}>
|
|
<View key={item.id} style={styles.contactRow}>
|
|
<View style={styles.contactUser}>
|
|
<NostrosAvatar
|
|
name={item.name}
|
|
pubKey={item.id}
|
|
src={item.picture}
|
|
lud06={item.lnurl}
|
|
size={40}
|
|
/>
|
|
<View style={styles.contactName}>
|
|
<Text variant='titleSmall'>{formatPubKey(item.id)}</Text>
|
|
{item.name && <Text variant='titleSmall'>{username(item)}</Text>}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</TouchableRipple>
|
|
)
|
|
|
|
return (
|
|
<View>
|
|
<ScrollView horizontal={false}>
|
|
<FlatList
|
|
data={directMessages}
|
|
renderItem={renderConversationItem}
|
|
ItemSeparatorComponent={Divider}
|
|
/>
|
|
</ScrollView>
|
|
<AnimatedFAB
|
|
style={[styles.fab, { top: Dimensions.get('window').height - 220 }]}
|
|
icon='pencil-outline'
|
|
label='Label'
|
|
onPress={() => bottomSheetCreateRef.current?.open()}
|
|
animateFrom='right'
|
|
iconMode='static'
|
|
extended={false}
|
|
/>
|
|
<RBSheet
|
|
ref={bottomSheetCreateRef}
|
|
closeOnDragDown={true}
|
|
height={190}
|
|
customStyles={bottomSheetStyles}
|
|
>
|
|
<ScrollView horizontal={false}>
|
|
<FlatList
|
|
data={createOptions}
|
|
renderItem={({ item }) => {
|
|
return (
|
|
<List.Item
|
|
key={item.key}
|
|
title={item.title}
|
|
onPress={item.onPress}
|
|
left={item.left}
|
|
/>
|
|
)
|
|
}}
|
|
ItemSeparatorComponent={Divider}
|
|
/>
|
|
</ScrollView>
|
|
</RBSheet>
|
|
<RBSheet
|
|
ref={bottomSheetUserListRef}
|
|
closeOnDragDown={true}
|
|
height={620}
|
|
customStyles={bottomSheetStyles}
|
|
>
|
|
<ScrollView horizontal={false}>
|
|
<FlatList data={users} renderItem={renderUserItem} ItemSeparatorComponent={Divider} />
|
|
</ScrollView>
|
|
</RBSheet>
|
|
<RBSheet
|
|
ref={bottomSheetPubKeyRef}
|
|
closeOnDragDown={true}
|
|
height={220}
|
|
customStyles={bottomSheetStyles}
|
|
>
|
|
<View>
|
|
<Text variant='titleLarge'>{t('conversationsFeed.openMessageTitle')}</Text>
|
|
<Text variant='bodyMedium'>{t('conversationsFeed.openMessageDescription')}</Text>
|
|
<TextInput
|
|
mode='outlined'
|
|
label={t('conversationsFeed.openMessage') ?? ''}
|
|
onChangeText={setSendPubKeyInput}
|
|
value={sendPubKeyInput}
|
|
right={
|
|
<TextInput.Icon
|
|
icon='content-paste'
|
|
onPress={pastePubKey}
|
|
forceTextInputFocus={false}
|
|
/>
|
|
}
|
|
/>
|
|
<Button
|
|
mode='contained'
|
|
disabled={!sendPubKeyInput || sendPubKeyInput === ''}
|
|
onPress={() => navigate('Conversation', { pubKey: sendPubKeyInput })}
|
|
>
|
|
{t('conversationsFeed.openMessage')}
|
|
</Button>
|
|
</View>
|
|
</RBSheet>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
contactRow: {
|
|
paddingLeft: 16,
|
|
paddingRight: 16,
|
|
paddingTop: 16,
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
width: '100%',
|
|
},
|
|
contactName: {
|
|
paddingLeft: 16,
|
|
},
|
|
contactUser: {
|
|
flexDirection: 'row',
|
|
alignContent: 'center',
|
|
},
|
|
contactInfo: {
|
|
alignContent: 'center',
|
|
justifyContent: 'space-between',
|
|
},
|
|
contactFollow: {
|
|
justifyContent: 'center',
|
|
},
|
|
fab: {
|
|
right: 16,
|
|
position: 'absolute',
|
|
},
|
|
})
|
|
|
|
export default ConversationsFeed
|