nostros/frontend/Pages/NotePage/index.tsx

320 lines
9.9 KiB
TypeScript
Raw Normal View History

2023-01-15 18:04:59 +00:00
import React, { useCallback, useContext, useEffect, useState } from 'react'
2022-10-31 00:37:42 +00:00
import { AppContext } from '../../Contexts/AppContext'
import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
2022-12-30 18:11:35 +00:00
import NoteCard from '../../Components/NoteCard'
2022-10-31 00:37:42 +00:00
import { EventKind } from '../../lib/nostr/Events'
2023-01-16 09:03:01 +00:00
import { Dimensions, RefreshControl, ScrollView, StyleSheet, Text, View } from 'react-native'
2023-01-15 18:04:59 +00:00
import { Event } from '../../../lib/nostr/Events'
2023-01-16 09:03:01 +00:00
import { getDirectReplies } from '../../Functions/RelayFunctions/Events'
2022-11-15 14:07:17 +00:00
import { RelayFilters } from '../../lib/nostr/RelayPool/intex'
2023-01-16 09:03:01 +00:00
import {
ActivityIndicator,
AnimatedFAB,
Button,
IconButton,
Surface,
useTheme,
} from 'react-native-paper'
2023-01-15 18:04:59 +00:00
import { npubEncode } from 'nostr-tools/nip19'
import moment from 'moment'
import { usernamePubKey } from '../../Functions/RelayFunctions/Users'
import NostrosAvatar from '../../Components/Avatar'
import TextContent from '../../Components/TextContent'
import { getReactionsCount, getUserReaction } from '../../Functions/DatabaseFunctions/Reactions'
import { UserContext } from '../../Contexts/UserContext'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import RBSheet from 'react-native-raw-bottom-sheet'
import ProfileCard from '../../Components/ProfileCard'
2023-01-16 09:03:01 +00:00
import { navigate } from '../../lib/Navigation'
2022-10-24 17:27:31 +00:00
2023-01-15 18:04:59 +00:00
interface NotePageProps {
route: { params: { noteId: string } }
}
export const NotePage: React.FC<NotePageProps> = ({ route }) => {
const { database } = useContext(AppContext)
const { publicKey, privateKey } = useContext(UserContext)
const { relayPool, lastEventId } = useContext(RelayPoolContext)
2022-10-31 00:37:42 +00:00
const [note, setNote] = useState<Note>()
const [replies, setReplies] = useState<Note[]>()
2023-01-15 18:04:59 +00:00
const [refreshing, setRefreshing] = useState(false)
const [nPub, setNPub] = useState<string>()
const [positiveReactions, setPositiveReactions] = useState<number>(0)
const [negaiveReactions, setNegativeReactions] = useState<number>(0)
const [userUpvoted, setUserUpvoted] = useState<boolean>(false)
const [userDownvoted, setUserDownvoted] = useState<boolean>(false)
const [timestamp, setTimestamp] = useState<string>()
const [profileCardPubkey, setProfileCardPubKey] = useState<string>()
2022-10-31 00:37:42 +00:00
const theme = useTheme()
2023-01-15 18:04:59 +00:00
const bottomSheetProfileRef = React.useRef<RBSheet>(null)
2022-10-24 17:27:31 +00:00
2022-11-30 12:34:43 +00:00
useEffect(() => {
2022-11-05 18:29:55 +00:00
relayPool?.unsubscribeAll()
2022-11-11 10:56:04 +00:00
setNote(undefined)
setReplies(undefined)
2022-11-30 12:34:43 +00:00
subscribeNotes()
loadNote()
2023-01-15 18:04:59 +00:00
}, [])
2022-11-05 18:29:55 +00:00
2022-11-11 10:56:04 +00:00
useEffect(() => {
loadNote()
2023-01-16 09:03:01 +00:00
}, [lastEventId])
2022-11-11 10:56:04 +00:00
const loadNote: () => void = async () => {
2023-01-16 09:03:01 +00:00
if (database && publicKey) {
2023-01-15 18:04:59 +00:00
const events = await getNotes(database, { filters: { id: route.params.noteId } })
2022-11-05 18:29:55 +00:00
const event = events[0]
2022-11-11 10:56:04 +00:00
setNote(event)
2023-01-15 18:04:59 +00:00
setNPub(npubEncode(event.pubkey))
setTimestamp(moment.unix(event.created_at).format('HH:mm DD-MM-YY'))
const notes = await getNotes(database, { filters: { reply_event_id: route.params.noteId } })
2022-11-11 10:56:04 +00:00
const rootReplies = getDirectReplies(event, notes)
if (rootReplies.length > 0) {
setReplies(rootReplies as Note[])
const message: RelayFilters = {
kinds: [EventKind.meta],
authors: [...rootReplies.map((note) => note.pubkey), event.pubkey],
2022-11-05 18:29:55 +00:00
}
2023-01-09 14:57:05 +00:00
relayPool?.subscribe('meta-notepage', [message])
2022-11-11 10:56:04 +00:00
} else {
setReplies([])
}
2023-01-16 09:03:01 +00:00
2023-01-16 12:09:18 +00:00
getReactionsCount(database, { positive: true, eventId: route.params.noteId }).then(
(result) => {
setPositiveReactions(result ?? 0)
},
)
getReactionsCount(database, { positive: false, eventId: route.params.noteId }).then(
(result) => {
setNegativeReactions(result ?? 0)
},
)
2023-01-16 09:03:01 +00:00
getUserReaction(database, publicKey, { eventId: route.params.noteId }).then((results) => {
results.forEach((reaction) => {
if (reaction.positive) {
setUserUpvoted(true)
} else {
setUserDownvoted(true)
}
})
2023-01-03 16:26:49 +00:00
})
2023-01-15 18:04:59 +00:00
setRefreshing(false)
2022-11-11 10:56:04 +00:00
}
}
2023-01-15 18:04:59 +00:00
const onRefresh = useCallback(() => {
setRefreshing(true)
relayPool?.unsubscribeAll()
subscribeNotes()
loadNote()
}, [])
2022-11-11 10:56:04 +00:00
const subscribeNotes: (past?: boolean) => Promise<void> = async (past) => {
2023-01-15 18:04:59 +00:00
if (database && route.params.noteId) {
2023-01-09 14:57:05 +00:00
relayPool?.subscribe('notepage', [
{
kinds: [EventKind.textNote],
2023-01-15 18:04:59 +00:00
ids: [route.params.noteId],
2023-01-09 14:57:05 +00:00
},
{
kinds: [EventKind.reaction, EventKind.textNote],
2023-01-15 18:04:59 +00:00
'#e': [route.params.noteId],
2023-01-09 14:57:05 +00:00
},
])
2022-11-05 18:29:55 +00:00
}
}
2023-01-15 18:04:59 +00:00
const publishReaction: (positive: boolean) => void = (positive) => {
if (note) {
const event: Event = {
content: positive ? '+' : '-',
created_at: moment().unix(),
kind: EventKind.reaction,
pubkey: publicKey,
tags: [...note.tags, ['e', note.id], ['p', note.pubkey]],
2022-10-24 17:27:31 +00:00
}
2023-01-15 18:04:59 +00:00
relayPool?.sendEvent(event)
2022-10-24 17:27:31 +00:00
}
2022-10-31 00:37:42 +00:00
}
2022-10-24 17:27:31 +00:00
2023-01-15 18:04:59 +00:00
const renderItem: (note: Note) => JSX.Element = (note) => (
<View style={[styles.noteCard, { borderColor: theme.colors.onSecondary }]} key={note.id}>
<NoteCard
note={note}
onPressOptions={() => {
setProfileCardPubKey(note.pubkey)
bottomSheetProfileRef.current?.open()
}}
2023-01-16 10:54:45 +00:00
showAnswerData={false}
2023-01-15 18:04:59 +00:00
/>
</View>
)
2022-11-01 00:14:19 +00:00
2023-01-15 18:04:59 +00:00
const bottomSheetStyles = React.useMemo(() => {
return {
container: {
backgroundColor: theme.colors.background,
padding: 16,
borderTopRightRadius: 28,
borderTopLeftRadius: 28,
},
draggableIcon: {
backgroundColor: '#000',
},
}
}, [])
2022-10-24 17:27:31 +00:00
2023-01-16 09:03:01 +00:00
const openProfileDrawer: () => void = () => {
setProfileCardPubKey(note?.pubkey)
bottomSheetProfileRef.current?.open()
}
2023-01-15 18:04:59 +00:00
return note && nPub ? (
2023-01-16 09:03:01 +00:00
<View>
<ScrollView
horizontal={false}
showsVerticalScrollIndicator={false}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
>
<Surface elevation={1}>
<View style={styles.title}>
<View style={styles.titleUser}>
<View>
<NostrosAvatar
name={note.name}
pubKey={nPub}
src={note.picture}
lud06={note.lnurl}
size={54}
/>
</View>
<View>
<Text>{usernamePubKey(note.name, nPub)}</Text>
<Text>{timestamp}</Text>
</View>
2023-01-15 18:04:59 +00:00
</View>
<View>
2023-01-16 09:03:01 +00:00
<IconButton icon='dots-vertical' size={25} onPress={openProfileDrawer} />
2023-01-15 18:04:59 +00:00
</View>
</View>
2023-01-16 09:03:01 +00:00
<View style={[styles.titleComment, { borderColor: theme.colors.onSecondary }]}>
<TextContent event={note} />
2023-01-15 18:04:59 +00:00
</View>
2023-01-16 09:03:01 +00:00
{privateKey && (
<View style={[styles.titleRecommend, { borderColor: theme.colors.onSecondary }]}>
<Button
onPress={() => {
if (!userDownvoted && privateKey) {
setUserDownvoted(true)
setNegativeReactions((prev) => prev + 1)
publishReaction(false)
}
}}
icon={() => (
<MaterialCommunityIcons
name={userDownvoted ? 'thumb-down' : 'thumb-down-outline'}
size={25}
/>
)}
>
{negaiveReactions === undefined || negaiveReactions === 0 ? '-' : negaiveReactions}
</Button>
<Button
onPress={() => {
if (!userUpvoted && privateKey) {
setUserUpvoted(true)
setPositiveReactions((prev) => prev + 1)
publishReaction(true)
}
}}
icon={() => (
<MaterialCommunityIcons
name={userUpvoted ? 'thumb-up' : 'thumb-up-outline'}
size={25}
/>
)}
>
{positiveReactions === undefined || positiveReactions === 0
? '-'
: positiveReactions}
</Button>
</View>
)}
</Surface>
{replies && replies.length > 0 && (
<View style={styles.list}>
{replies.map((note) => renderItem(note))}
{replies.length >= 10 && <ActivityIndicator style={styles.loading} animating={true} />}
2023-01-15 18:04:59 +00:00
</View>
2022-10-24 17:27:31 +00:00
)}
2023-01-16 09:03:01 +00:00
</ScrollView>
<AnimatedFAB
style={[styles.fab, { top: Dimensions.get('window').height - 140 }]}
icon='message-plus-outline'
label='Label'
onPress={() => navigate('Reply', { note })}
animateFrom='right'
iconMode='static'
extended={false}
/>
2023-01-15 18:04:59 +00:00
<RBSheet
ref={bottomSheetProfileRef}
closeOnDragDown={true}
height={280}
customStyles={bottomSheetStyles}
>
<ProfileCard userPubKey={profileCardPubkey ?? ''} bottomSheetRef={bottomSheetProfileRef} />
</RBSheet>
</View>
) : (
<></>
2022-10-31 00:37:42 +00:00
)
}
2022-10-24 17:27:31 +00:00
2023-01-15 18:04:59 +00:00
const styles = StyleSheet.create({
title: {
paddingRight: 16,
paddingLeft: 16,
paddingTop: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignContent: 'center',
},
titleUser: {
flexDirection: 'row',
alignContent: 'center',
},
2023-01-16 09:03:01 +00:00
titleRecommend: {
2023-01-15 18:04:59 +00:00
borderTopWidth: 1,
padding: 16,
flexDirection: 'row',
justifyContent: 'space-around',
},
2023-01-16 09:03:01 +00:00
titleComment: {
borderTopWidth: 1,
padding: 16,
flexDirection: 'row',
2023-01-16 12:09:18 +00:00
justifyContent: 'flex-start',
2023-01-16 09:03:01 +00:00
},
2023-01-15 18:04:59 +00:00
list: {
padding: 16,
},
loading: {
2023-01-16 09:03:01 +00:00
paddingBottom: 60,
},
fab: {
right: 16,
position: 'absolute',
2023-01-15 18:04:59 +00:00
},
noteCard: {
borderLeftWidth: 1,
paddingLeft: 32,
paddingBottom: 16,
},
})
2022-10-31 00:37:42 +00:00
export default NotePage