nostros/frontend/Pages/HomeFeed/index.tsx
2023-01-17 15:48:34 +01:00

222 lines
6.5 KiB
TypeScript

import React, { useCallback, useContext, useState, useEffect } from 'react'
import { getUsers, User } from '../../Functions/DatabaseFunctions/Users'
import {
Dimensions,
NativeScrollEvent,
NativeSyntheticEvent,
RefreshControl,
ScrollView,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../Contexts/AppContext'
import { getMainNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
import { UserContext } from '../../Contexts/UserContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { EventKind } from '../../lib/nostr/Events'
import { RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { ActivityIndicator, AnimatedFAB, Button, Text } from 'react-native-paper'
import NoteCard from '../../Components/NoteCard'
import RBSheet from 'react-native-raw-bottom-sheet'
import ProfileCard from '../../Components/ProfileCard'
import { useTheme } from '@react-navigation/native'
import { navigate } from '../../lib/Navigation'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { t } from 'i18next'
interface HomeFeedProps {
jumpTo: (tabName: string) => void
}
export const HomeFeed: React.FC<HomeFeedProps> = ({ jumpTo }) => {
const theme = useTheme()
const { database } = useContext(AppContext)
const { publicKey } = useContext(UserContext)
const { lastEventId, relayPool, lastConfirmationtId } = useContext(RelayPoolContext)
const initialPageSize = 10
const [notes, setNotes] = useState<Note[]>([])
const [authors, setAuthors] = useState<string[]>([])
const [pageSize, setPageSize] = useState<number>(initialPageSize)
const [refreshing, setRefreshing] = useState(true)
const [profileCardPubkey, setProfileCardPubKey] = useState<string>()
const bottomSheetProfileRef = React.useRef<RBSheet>(null)
useEffect(() => {
if (relayPool && publicKey) {
loadNotes()
}
}, [publicKey, relayPool, lastEventId, lastConfirmationtId])
useEffect(() => {
if (pageSize > initialPageSize) {
subscribeNotes(true)
}
}, [pageSize])
const onRefresh = useCallback(() => {
setRefreshing(true)
if (relayPool && publicKey) {
subscribeNotes()
}
}, [])
const subscribeNotes: (past?: boolean) => void = async (past) => {
if (!database || !publicKey) return
let newAuthors: string[] = authors
if (newAuthors.length === 0) {
const users: User[] = await getUsers(database, { contacts: true })
newAuthors = [...users.map((user) => user.id), publicKey]
}
const lastNotes: Note[] = await getMainNotes(database, publicKey, initialPageSize)
const lastNote: Note = lastNotes[lastNotes.length - 1]
const message: RelayFilters = {
kinds: [EventKind.textNote, EventKind.recommendServer],
authors: newAuthors,
}
if (lastNote && lastNotes.length >= pageSize && !past) {
message.since = lastNote.created_at
} else {
message.limit = pageSize + initialPageSize
}
relayPool?.subscribe('homepage-main', [message])
relayPool?.subscribe('homepage-contacts', [
{
kinds: [EventKind.petNames],
authors: newAuthors,
},
])
setAuthors(newAuthors)
}
const onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void = (event) => {
if (handleInfinityScroll(event)) {
setPageSize(pageSize + initialPageSize)
}
}
const loadNotes: () => void = () => {
if (database && publicKey) {
getMainNotes(database, publicKey, pageSize).then((notes) => {
setNotes(notes)
setRefreshing(false)
if (notes.length > 0) {
relayPool?.subscribe('homepage-reactions', [
{
kinds: [EventKind.reaction, EventKind.textNote, EventKind.recommendServer],
'#e': notes.map((note) => note.id ?? ''),
},
])
}
})
}
}
const renderItem: (note: Note) => JSX.Element = (note) => {
return (
<View style={styles.noteCard} key={note.id}>
<NoteCard
note={note}
onPressOptions={() => {
setProfileCardPubKey(note.pubkey)
bottomSheetProfileRef.current?.open()
}}
/>
</View>
)
}
const bottomSheetStyles = React.useMemo(() => {
return {
container: {
backgroundColor: theme.colors.background,
padding: 16,
borderTopRightRadius: 28,
borderTopLeftRadius: 28,
},
draggableIcon: {
backgroundColor: '#000',
},
}
}, [])
return (
<View style={styles.container}>
{notes && notes.length > 0 ? (
<ScrollView
onScroll={onScroll}
horizontal={false}
showsVerticalScrollIndicator={false}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
style={styles.list}
>
{notes.map((note) => renderItem(note))}
{notes.length >= 10 && <ActivityIndicator animating={true} />}
</ScrollView>
) : (
<View style={styles.blank}>
<MaterialCommunityIcons name='account-group-outline' size={64} style={styles.center} />
<Text variant='headlineSmall' style={styles.center}>
{t('homeFeed.emptyTitle')}
</Text>
<Text variant='bodyMedium' style={styles.center}>
{t('homeFeed.emptyDescription')}
</Text>
<Button mode='contained' compact onPress={() => jumpTo('contacts')}>
{t('homeFeed.emptyButton')}
</Button>
</View>
)}
<AnimatedFAB
style={[styles.fab, { top: Dimensions.get('window').height - 220 }]}
icon='pencil-outline'
label='Label'
onPress={() => navigate('Send')}
animateFrom='right'
iconMode='static'
extended={false}
/>
<RBSheet
ref={bottomSheetProfileRef}
closeOnDragDown={true}
height={280}
customStyles={bottomSheetStyles}
>
<ProfileCard userPubKey={profileCardPubkey ?? ''} bottomSheetRef={bottomSheetProfileRef} />
</RBSheet>
</View>
)
}
const styles = StyleSheet.create({
list: {
padding: 16,
},
noteCard: {
marginBottom: 16,
},
fab: {
right: 16,
position: 'absolute',
},
container: {
padding: 16,
flex: 1,
},
center: {
alignContent: 'center',
textAlign: 'center',
},
blank: {
justifyContent: 'space-between',
height: 200,
marginTop: 60,
},
})
export default HomeFeed