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-10-29 17:56:49 +00:00
|
|
|
|
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
|