mirror of
https://github.com/KoalaSat/nostros.git
synced 2024-09-29 06:30:47 +00:00
Infinite Loader
This commit is contained in:
parent
12379ee151
commit
2abf638ec3
@ -53,7 +53,7 @@ yarn start
|
||||
|
||||
### Basics
|
||||
|
||||
- [ ] Infinite Load
|
||||
- [x] Infinite Load
|
||||
- [x] Go to replied event
|
||||
- [ ] Relays management (add, remove and recomend)
|
||||
- [ ] Random Key Generator
|
||||
|
@ -109,6 +109,7 @@ export const ContactsPage: React.FC = () => {
|
||||
<>
|
||||
<Layout style={styles.container} level='3'>
|
||||
<ScrollView
|
||||
horizontal={false}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||
>
|
||||
{users.map((user) => (
|
||||
|
@ -1,6 +1,14 @@
|
||||
import { Card, Layout, useTheme } from '@ui-kitten/components'
|
||||
import { Card, Layout, Spinner, useTheme } from '@ui-kitten/components'
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { RefreshControl, ScrollView, StyleSheet, TouchableOpacity } from 'react-native'
|
||||
import {
|
||||
NativeScrollEvent,
|
||||
NativeSyntheticEvent,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import NoteCard from '../NoteCard'
|
||||
@ -9,44 +17,27 @@ import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
import { EventKind } from '../../lib/nostr/Events'
|
||||
import { RelayFilters } from '../../lib/nostr/Relay'
|
||||
import { getReplyEventId } from '../../Functions/RelayFunctions/Events'
|
||||
import { getUsers } from '../../Functions/DatabaseFunctions/Users'
|
||||
import { getUsers, User } from '../../Functions/DatabaseFunctions/Users'
|
||||
import Loading from '../Loading'
|
||||
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
|
||||
|
||||
export const HomePage: React.FC = () => {
|
||||
const { database, goToPage, page } = useContext(AppContext)
|
||||
const initialPageSize = 15
|
||||
const { lastEventId, relayPool, publicKey, privateKey } = useContext(RelayPoolContext)
|
||||
const theme = useTheme()
|
||||
const [pageSize, setPageSize] = useState<number>(initialPageSize)
|
||||
const [notes, setNotes] = useState<Note[]>([])
|
||||
const [totalContacts, setTotalContacts] = useState<number>(-1)
|
||||
const [authors, setAuthors] = useState<User[]>([])
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
const loadNotes: () => void = () => {
|
||||
if (database && publicKey) {
|
||||
getNotes(database, { contacts: true, includeIds: [publicKey], limit: 15 }).then((notes) => {
|
||||
setNotes(notes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const subscribeNotes: () => Promise<void> = async () => {
|
||||
const calculateInitialNotes: () => Promise<void> = async () => {
|
||||
return await new Promise<void>((resolve, reject) => {
|
||||
if (database && publicKey && relayPool) {
|
||||
getNotes(database, { limit: 1 }).then((notes) => {
|
||||
getUsers(database, { contacts: true, includeIds: [publicKey] }).then((users) => {
|
||||
setTotalContacts(users.length)
|
||||
let message: RelayFilters = {
|
||||
kinds: [EventKind.textNote, EventKind.recommendServer],
|
||||
authors: users.map((user) => user.id),
|
||||
limit: 15,
|
||||
}
|
||||
|
||||
if (notes.length !== 0) {
|
||||
message = {
|
||||
...message,
|
||||
since: notes[0].created_at,
|
||||
}
|
||||
}
|
||||
relayPool?.subscribe('main-channel', message)
|
||||
setAuthors(users)
|
||||
subscribeNotes(users, notes[0]?.created_at)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
@ -56,6 +47,36 @@ export const HomePage: React.FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const subscribeNotes: (users: User[], since?: number, until?: number) => void = (
|
||||
users,
|
||||
since,
|
||||
until,
|
||||
) => {
|
||||
const message: RelayFilters = {
|
||||
kinds: [EventKind.textNote, EventKind.recommendServer],
|
||||
authors: users.map((user) => user.id),
|
||||
limit: initialPageSize,
|
||||
}
|
||||
if (since) {
|
||||
message.since = since
|
||||
}
|
||||
if (until) {
|
||||
message.until = until
|
||||
}
|
||||
console.log('message', message)
|
||||
relayPool?.subscribe('main-channel', message)
|
||||
}
|
||||
|
||||
const loadNotes: () => void = () => {
|
||||
if (database && publicKey) {
|
||||
getNotes(database, { contacts: true, includeIds: [publicKey], limit: pageSize }).then(
|
||||
(notes) => {
|
||||
setNotes(notes)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
relayPool?.unsubscribeAll()
|
||||
}, [])
|
||||
@ -64,15 +85,23 @@ export const HomePage: React.FC = () => {
|
||||
loadNotes()
|
||||
}, [lastEventId])
|
||||
|
||||
useEffect(() => {
|
||||
if (pageSize > initialPageSize) {
|
||||
relayPool?.unsubscribeAll()
|
||||
subscribeNotes(authors, undefined, notes[notes.length - 1]?.created_at)
|
||||
loadNotes()
|
||||
}
|
||||
}, [pageSize])
|
||||
|
||||
useEffect(() => {
|
||||
loadNotes()
|
||||
subscribeNotes()
|
||||
calculateInitialNotes()
|
||||
}, [database, publicKey, relayPool])
|
||||
|
||||
const onRefresh = useCallback(() => {
|
||||
setRefreshing(true)
|
||||
relayPool?.unsubscribeAll()
|
||||
subscribeNotes().finally(() => setRefreshing(false))
|
||||
calculateInitialNotes().finally(() => setRefreshing(false))
|
||||
}, [])
|
||||
|
||||
const onPress: (note: Note) => void = (note) => {
|
||||
@ -94,6 +123,12 @@ export const HomePage: React.FC = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void = (event) => {
|
||||
if (handleInfinityScroll(event)) {
|
||||
setPageSize(pageSize + initialPageSize)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
@ -102,19 +137,33 @@ export const HomePage: React.FC = () => {
|
||||
width: 32,
|
||||
height: 32,
|
||||
},
|
||||
loadingBottom: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
padding: 12,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<Layout style={styles.container} level='4'>
|
||||
{notes.length === 0 && totalContacts !== 0 ? (
|
||||
{notes.length === 0 && authors.length !== 0 ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<ScrollView
|
||||
onScroll={onScroll}
|
||||
horizontal={false}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||
>
|
||||
{notes.map((note) => itemCard(note))}
|
||||
{notes.length >= initialPageSize && (
|
||||
<View style={styles.loadingBottom}>
|
||||
<Spinner size='tiny' />
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
)}
|
||||
</Layout>
|
||||
|
@ -183,6 +183,7 @@ export const NotePage: React.FC = () => {
|
||||
<Layout level='4'>
|
||||
{note ? (
|
||||
<ScrollView
|
||||
horizontal={false}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||
>
|
||||
{[note, ...(replies ?? [])].map((note) => itemCard(note))}
|
||||
|
@ -8,7 +8,16 @@ import {
|
||||
useTheme,
|
||||
} from '@ui-kitten/components'
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { Clipboard, RefreshControl, ScrollView, StyleSheet, TouchableOpacity } from 'react-native'
|
||||
import {
|
||||
Clipboard,
|
||||
NativeScrollEvent,
|
||||
NativeSyntheticEvent,
|
||||
RefreshControl,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import UserAvatar from 'react-native-user-avatar'
|
||||
import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
@ -28,14 +37,17 @@ import { populatePets, tagToUser } from '../../Functions/RelayFunctions/Users'
|
||||
import { getReplyEventId } from '../../Functions/RelayFunctions/Events'
|
||||
import Loading from '../Loading'
|
||||
import { storeEvent } from '../../Functions/DatabaseFunctions/Events'
|
||||
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
|
||||
|
||||
export const ProfilePage: React.FC = () => {
|
||||
const { database, page, goToPage, goBack } = useContext(AppContext)
|
||||
const { publicKey, privateKey, lastEventId, relayPool, setLastEventId } =
|
||||
useContext(RelayPoolContext)
|
||||
const theme = useTheme()
|
||||
const initialPageSize = 10
|
||||
const [notes, setNotes] = useState<Note[]>()
|
||||
const [user, setUser] = useState<User>()
|
||||
const [pageSize, setPageSize] = useState<number>(initialPageSize)
|
||||
const [contactsIds, setContactsIds] = useState<string[]>()
|
||||
const [isContact, setIsContact] = useState<boolean>()
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
@ -63,9 +75,7 @@ export const ProfilePage: React.FC = () => {
|
||||
setContactsIds(users.map((user) => user.id))
|
||||
})
|
||||
}
|
||||
getNotes(database, { filters: { pubkey: userId }, limit: 10 }).then((results) => {
|
||||
if (results.length > 0) setNotes(results)
|
||||
})
|
||||
loadNotes()
|
||||
}
|
||||
}, [lastEventId])
|
||||
|
||||
@ -113,7 +123,7 @@ export const ProfilePage: React.FC = () => {
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return <Spinner size='tiny' />
|
||||
return <Spinner size='small' />
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,21 +153,42 @@ export const ProfilePage: React.FC = () => {
|
||||
loadProfile().finally(() => setRefreshing(false))
|
||||
}, [])
|
||||
|
||||
const subscribeNotes: () => void = () => {
|
||||
useEffect(() => {
|
||||
if (pageSize > initialPageSize && notes) {
|
||||
relayPool?.unsubscribeAll()
|
||||
subscribeNotes(undefined, notes[notes.length - 1]?.created_at)
|
||||
loadNotes()
|
||||
}
|
||||
}, [pageSize])
|
||||
|
||||
const loadNotes: () => void = () => {
|
||||
if (database) {
|
||||
getNotes(database, { filters: { pubkey: userId }, limit: 10 }).then((results) => {
|
||||
getNotes(database, { filters: { pubkey: userId }, limit: pageSize }).then((results) => {
|
||||
if (results.length > 0) setNotes(results)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const subscribeNotes: (since?: number, until?: number) => void = (since, until) => {
|
||||
const message: RelayFilters = {
|
||||
kinds: [EventKind.textNote, EventKind.recommendServer],
|
||||
authors: [userId],
|
||||
limit: initialPageSize,
|
||||
}
|
||||
if (since) {
|
||||
message.since = since
|
||||
}
|
||||
if (until) {
|
||||
message.until = until
|
||||
}
|
||||
relayPool?.subscribe('main-channel', message)
|
||||
}
|
||||
|
||||
const calculateInitialNotes: () => void = () => {
|
||||
if (database) {
|
||||
getNotes(database, { filters: { pubkey: userId }, limit: pageSize }).then((results) => {
|
||||
if (results) {
|
||||
const notesEvent: RelayFilters = {
|
||||
kinds: [EventKind.textNote, EventKind.recommendServer],
|
||||
authors: [userId],
|
||||
limit: 10,
|
||||
}
|
||||
|
||||
if (results.length >= 10) {
|
||||
notesEvent.since = results[0]?.created_at
|
||||
}
|
||||
|
||||
relayPool?.subscribe('main-channel', notesEvent)
|
||||
subscribeNotes(results[0]?.created_at)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -181,7 +212,7 @@ export const ProfilePage: React.FC = () => {
|
||||
if (event?.id) setLastEventId(event.id)
|
||||
})
|
||||
}
|
||||
subscribeNotes()
|
||||
calculateInitialNotes()
|
||||
}
|
||||
} else {
|
||||
reject(new Error('Not Ready'))
|
||||
@ -191,6 +222,12 @@ export const ProfilePage: React.FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>) => void = (event) => {
|
||||
if (handleInfinityScroll(event)) {
|
||||
setPageSize(pageSize + initialPageSize)
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
list: {
|
||||
flex: 1,
|
||||
@ -234,6 +271,14 @@ export const ProfilePage: React.FC = () => {
|
||||
marginTop: 16,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
loadingBottom: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexWrap: 'wrap',
|
||||
padding: 12,
|
||||
},
|
||||
})
|
||||
|
||||
const itemCard: (note: Note) => JSX.Element = (note) => {
|
||||
@ -270,7 +315,7 @@ export const ProfilePage: React.FC = () => {
|
||||
if (contactsIds === undefined) {
|
||||
return (
|
||||
<Layout style={styles.stats} level='3'>
|
||||
<Spinner size='tiny' />
|
||||
<Spinner size='small' />
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@ -341,9 +386,16 @@ export const ProfilePage: React.FC = () => {
|
||||
<Layout style={styles.list} level='3'>
|
||||
{notes ? (
|
||||
<ScrollView
|
||||
onScroll={onScroll}
|
||||
horizontal={false}
|
||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
|
||||
>
|
||||
{notes.map((note) => itemCard(note))}
|
||||
{notes.length >= initialPageSize && (
|
||||
<View style={styles.loadingBottom}>
|
||||
<Spinner size='tiny' />
|
||||
</View>
|
||||
)}
|
||||
</ScrollView>
|
||||
) : (
|
||||
<Loading />
|
||||
|
@ -125,7 +125,7 @@ export const SendPage: React.FC = () => {
|
||||
<Button
|
||||
onPress={onPressSend}
|
||||
disabled={sending}
|
||||
accessoryLeft={sending ? <Spinner size='tiny' /> : <></>}
|
||||
accessoryLeft={sending ? <Spinner size='small' /> : <></>}
|
||||
>
|
||||
{t('sendPage.send')}
|
||||
</Button>
|
||||
|
8
frontend/Functions/NativeFunctions/index.ts
Normal file
8
frontend/Functions/NativeFunctions/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export const handleInfinityScroll: (event: any) => boolean = (event) => {
|
||||
const mHeight = event.nativeEvent.layoutMeasurement.height
|
||||
const cSize = event.nativeEvent.contentSize.height
|
||||
const Y = event.nativeEvent.contentOffset.y
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
if (Math.ceil(mHeight + Y) >= cSize) return true
|
||||
return false
|
||||
}
|
Loading…
Reference in New Issue
Block a user