mirror of
https://github.com/KoalaSat/nostros.git
synced 2024-09-29 06:30:47 +00:00
Followers list
This commit is contained in:
parent
acb36bcc94
commit
f83f6d82da
@ -1,4 +1,4 @@
|
||||
import { Button, Card, Input, Layout, Modal, useTheme } from '@ui-kitten/components'
|
||||
import { Button, Card, Input, Layout, Modal, Tab, TabBar, useTheme } from '@ui-kitten/components'
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react'
|
||||
import { RefreshControl, ScrollView, StyleSheet, TouchableOpacity } from 'react-native'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
@ -6,9 +6,10 @@ import Icon from 'react-native-vector-icons/FontAwesome5'
|
||||
import { Event, EventKind } from '../../lib/nostr/Events'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
addContact,
|
||||
getUsers,
|
||||
insertUserContact,
|
||||
insertUserPets,
|
||||
updateUserContact,
|
||||
updateUserFollower,
|
||||
User,
|
||||
} from '../../Functions/DatabaseFunctions/Users'
|
||||
import UserCard from '../UserCard'
|
||||
@ -25,6 +26,7 @@ export const ContactsPage: React.FC = () => {
|
||||
const [refreshing, setRefreshing] = useState(true)
|
||||
const [showAddContact, setShowAddContact] = useState<boolean>(false)
|
||||
const [contactInput, setContactInput] = useState<string>()
|
||||
const [selectedTab, setSelectedTab] = useState(0)
|
||||
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
@ -33,15 +35,23 @@ export const ContactsPage: React.FC = () => {
|
||||
}, [lastEventId])
|
||||
|
||||
useEffect(() => {
|
||||
setUsers([])
|
||||
subscribeContacts()
|
||||
}, [])
|
||||
}, [selectedTab])
|
||||
|
||||
const loadUsers: () => void = () => {
|
||||
if (database && publicKey) {
|
||||
setRefreshing(true)
|
||||
setTimeout(() => setRefreshing(false), 5000)
|
||||
getUsers(database, { contacts: true }).then((results) => {
|
||||
if (results) setUsers(results)
|
||||
})
|
||||
if (selectedTab === 0) {
|
||||
getUsers(database, { contacts: true }).then((results) => {
|
||||
if (results) setUsers(results)
|
||||
})
|
||||
} else {
|
||||
getUsers(database, { followers: true }).then((results) => {
|
||||
if (results) setUsers(results)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,22 +59,36 @@ export const ContactsPage: React.FC = () => {
|
||||
relayPool?.unsubscribeAll()
|
||||
relayPool?.on('event', 'contacts', (relay: Relay, _subId?: string, event?: Event) => {
|
||||
console.log('CONTACTS PAGE EVENT =======>', relay.url, event)
|
||||
if (database && event?.id && event.kind === EventKind.petNames) {
|
||||
insertUserContact(event, database).finally(() => setLastEventId(event?.id ?? ''))
|
||||
relayPool?.removeOn('event', 'contacts')
|
||||
if (database && event?.id && publicKey && event.kind === EventKind.petNames) {
|
||||
if (event.pubkey === publicKey) {
|
||||
insertUserPets(event, database).finally(() => setLastEventId(event?.id ?? ''))
|
||||
relayPool?.removeOn('event', 'contacts')
|
||||
} else {
|
||||
const isFollower = event.tags.some((tag) => tag[1] === publicKey)
|
||||
updateUserFollower(event.pubkey, database, isFollower).finally(() =>
|
||||
setLastEventId(event?.id ?? ''),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (publicKey) {
|
||||
relayPool?.subscribe('main-channel', {
|
||||
kinds: [EventKind.petNames],
|
||||
authors: [publicKey],
|
||||
})
|
||||
if (selectedTab === 0) {
|
||||
relayPool?.subscribe('main-channel', {
|
||||
kinds: [EventKind.petNames],
|
||||
authors: [publicKey],
|
||||
})
|
||||
} else if (selectedTab === 1) {
|
||||
relayPool?.subscribe('main-channel', {
|
||||
kinds: [EventKind.petNames],
|
||||
'#p': [publicKey],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onPressAddContact: () => void = () => {
|
||||
if (contactInput && relayPool && database && publicKey) {
|
||||
addContact(contactInput, database).then(() => {
|
||||
updateUserContact(contactInput, database, true).then(() => {
|
||||
populatePets(relayPool, database, publicKey)
|
||||
setShowAddContact(false)
|
||||
loadUsers()
|
||||
@ -76,6 +100,12 @@ export const ContactsPage: React.FC = () => {
|
||||
setRefreshing(true)
|
||||
relayPool?.unsubscribeAll()
|
||||
subscribeContacts()
|
||||
if (users && users?.length > 0) {
|
||||
relayPool?.subscribe('main-channel', {
|
||||
kinds: [EventKind.meta],
|
||||
authors: users?.map((user) => user.id),
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@ -103,10 +133,21 @@ export const ContactsPage: React.FC = () => {
|
||||
backdrop: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
topBar: {
|
||||
height: 64,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabBar
|
||||
style={styles.topBar}
|
||||
selectedIndex={selectedTab}
|
||||
onSelect={(index) => setSelectedTab(index)}
|
||||
>
|
||||
<Tab title={t('contactsPage.following')} />
|
||||
<Tab title={t('contactsPage.followers')} />
|
||||
</TabBar>
|
||||
<Layout style={styles.container} level='3'>
|
||||
<ScrollView
|
||||
horizontal={false}
|
||||
|
@ -7,7 +7,7 @@ import { tagToUser } from '../../../Functions/RelayFunctions/Users'
|
||||
import Relay from '../../../lib/nostr/Relay'
|
||||
import { Event, EventKind } from '../../../lib/nostr/Events'
|
||||
import { AppContext } from '../../../Contexts/AppContext'
|
||||
import { insertUserContact, insertUserMeta } from '../../../Functions/DatabaseFunctions/Users'
|
||||
import { insertUserPets, insertUserMeta } from '../../../Functions/DatabaseFunctions/Users'
|
||||
import SInfo from 'react-native-sensitive-info'
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5'
|
||||
import { generateRandomKey, getPublickey } from '../../../lib/nostr/Bip'
|
||||
@ -75,7 +75,7 @@ export const Logger: React.FC = () => {
|
||||
if (database) {
|
||||
if (event.tags.length > 0) {
|
||||
setStatus(2)
|
||||
insertUserContact(event, database).then(() => {
|
||||
insertUserPets(event, database).then(() => {
|
||||
requestUserData(event)
|
||||
})
|
||||
} else {
|
||||
|
@ -22,7 +22,13 @@ import { AppContext } from '../../Contexts/AppContext'
|
||||
import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import NoteCard from '../NoteCard'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
import { getUser, removeContact, addContact, User } from '../../Functions/DatabaseFunctions/Users'
|
||||
import {
|
||||
getUser,
|
||||
removeContact,
|
||||
User,
|
||||
updateUserFollower,
|
||||
updateUserContact,
|
||||
} from '../../Functions/DatabaseFunctions/Users'
|
||||
import { EventKind, Event } from '../../lib/nostr/Events'
|
||||
import Relay, { RelayFilters } from '../../lib/nostr/Relay'
|
||||
import Icon from 'react-native-vector-icons/FontAwesome5'
|
||||
@ -35,7 +41,8 @@ import Avatar from '../Avatar'
|
||||
|
||||
export const ProfilePage: React.FC = () => {
|
||||
const { database, page, goToPage, goBack } = useContext(AppContext)
|
||||
const { publicKey, privateKey, lastEventId, relayPool } = useContext(RelayPoolContext)
|
||||
const { publicKey, privateKey, lastEventId, relayPool, setLastEventId } =
|
||||
useContext(RelayPoolContext)
|
||||
const theme = useTheme()
|
||||
const initialPageSize = 10
|
||||
const [notes, setNotes] = useState<Note[]>()
|
||||
@ -114,6 +121,12 @@ export const ProfilePage: React.FC = () => {
|
||||
setTimeout(() => setRefreshing(false), 5000)
|
||||
storeEvent(event, database)
|
||||
subscribeNotes()
|
||||
if (publicKey && event.pubkey !== publicKey) {
|
||||
const isFollower = event.tags.some((tag) => tag[1] === publicKey)
|
||||
updateUserFollower(event.pubkey, database, isFollower).finally(() =>
|
||||
setLastEventId(event?.id ?? ''),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -153,7 +166,7 @@ export const ProfilePage: React.FC = () => {
|
||||
|
||||
const addAuthor: () => void = () => {
|
||||
if (relayPool && database && publicKey) {
|
||||
addContact(userId, database).then(() => {
|
||||
updateUserContact(userId, database, true).then(() => {
|
||||
populatePets(relayPool, database, publicKey)
|
||||
setIsContact(true)
|
||||
})
|
||||
|
@ -22,7 +22,7 @@ export const RelaysPage: React.FC = () => {
|
||||
const { goBack, database } = useContext(AppContext)
|
||||
const { relayPool, publicKey, setRelayPool } = useContext(RelayPoolContext)
|
||||
const { t } = useTranslation('common')
|
||||
const [relays, setRelays] = useState<Relay[]>()
|
||||
const [relays, setRelays] = useState<Relay[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
|
||||
const loadRelays: () => void = () => {
|
||||
@ -157,7 +157,7 @@ export const RelaysPage: React.FC = () => {
|
||||
/>
|
||||
<Layout style={styles.actionContainer} level='2'>
|
||||
<ScrollView refreshControl={<RefreshControl refreshing={loading} />}>
|
||||
{relays?.length ? (
|
||||
{relays ? (
|
||||
[...relays, ...defaultList()].map((item, index) => {
|
||||
return renderItem(item, index)
|
||||
})
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
|
||||
import { initDatabase } from '../Functions/DatabaseFunctions'
|
||||
import { createInitDatabase } from '../Functions/DatabaseFunctions/Migrations'
|
||||
import { createInitDatabase, runMigrations } from '../Functions/DatabaseFunctions/Migrations'
|
||||
import FlashMessage from 'react-native-flash-message'
|
||||
import SInfo from 'react-native-sensitive-info'
|
||||
import { BackHandler } from 'react-native'
|
||||
@ -50,6 +50,7 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
|
||||
setLoadingDb(false)
|
||||
})
|
||||
} else {
|
||||
runMigrations(db)
|
||||
setLoadingDb(false)
|
||||
}
|
||||
})
|
||||
|
@ -8,7 +8,6 @@ import { getRelays, addRelay } from '../Functions/DatabaseFunctions/Relays'
|
||||
import { showMessage } from 'react-native-flash-message'
|
||||
import SInfo from 'react-native-sensitive-info'
|
||||
import { getPublickey } from '../lib/nostr/Bip'
|
||||
import { pickRandomItems } from '../Functions/NativeFunctions'
|
||||
import { defaultRelays } from '../Constants/RelayConstants'
|
||||
|
||||
export interface RelayPoolContextProps {
|
||||
@ -58,10 +57,10 @@ export const RelayPoolContextProvider = ({
|
||||
initRelayPool.add(relay.url)
|
||||
})
|
||||
} else {
|
||||
pickRandomItems(defaultRelays, 2).forEach((relayUrl) => {
|
||||
initRelayPool.add(relayUrl)
|
||||
addRelay({ url: relayUrl }, database)
|
||||
})
|
||||
// pickRandomItems(defaultRelays, 1).forEach((relayUrl) => {
|
||||
initRelayPool.add(defaultRelays[4])
|
||||
addRelay({ url: defaultRelays[4] }, database)
|
||||
// })
|
||||
}
|
||||
|
||||
initRelayPool?.on(
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
|
||||
import { simpleExecute } from '..'
|
||||
import { ColumnMetadata, QuickSQLiteConnection } from 'react-native-quick-sqlite'
|
||||
import { dropTables, simpleExecute } from '..'
|
||||
|
||||
export const createInitDatabase: (db: QuickSQLiteConnection) => Promise<void> = async (db) => {
|
||||
dropTables(db)
|
||||
await simpleExecute(
|
||||
`
|
||||
CREATE TABLE IF NOT EXISTS nostros_notes(
|
||||
@ -40,4 +41,13 @@ export const createInitDatabase: (db: QuickSQLiteConnection) => Promise<void> =
|
||||
`,
|
||||
db,
|
||||
)
|
||||
await runMigrations(db)
|
||||
}
|
||||
|
||||
export const runMigrations: (db: QuickSQLiteConnection) => Promise<void> = async (db) => {
|
||||
const { metadata } = db.execute('SELECT * FROM nostros_users;')
|
||||
// v0.1.8.1 alpha
|
||||
if (!metadata?.some((meta: ColumnMetadata) => meta.columnName === 'follower')) {
|
||||
await simpleExecute('ALTER TABLE nostros_users ADD COLUMN follower BOOLEAN DEFAULT FALSE;', db)
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ export const getNotes: (
|
||||
} else {
|
||||
notesQuery += 'WHERE '
|
||||
}
|
||||
notesQuery += 'nostros_users.contact = TRUE '
|
||||
notesQuery += 'nostros_users.contact = 1 '
|
||||
}
|
||||
if (includeIds) {
|
||||
if (Object.keys(filters).length > 0 || contacts) {
|
||||
|
@ -10,6 +10,7 @@ export interface User {
|
||||
picture?: string
|
||||
about?: string
|
||||
contact?: boolean
|
||||
follower?: boolean
|
||||
}
|
||||
|
||||
const databaseToEntity: (object: object) => User = (object) => {
|
||||
@ -32,9 +33,9 @@ export const insertUserMeta: (
|
||||
|
||||
const query = `
|
||||
INSERT OR REPLACE INTO nostros_users
|
||||
(id, name, picture, about, main_relay, contact)
|
||||
(id, name, picture, about, main_relay, contact, follower)
|
||||
VALUES
|
||||
(?,?,?,?,?, (SELECT contact FROM nostros_users WHERE id = ?));
|
||||
(?,?,?,?,?,(SELECT contact FROM nostros_users WHERE id = ?),(SELECT follower FROM nostros_users WHERE id = ?));
|
||||
`
|
||||
const queryParams = [
|
||||
id,
|
||||
@ -43,15 +44,36 @@ export const insertUserMeta: (
|
||||
about.split("'").join("''"),
|
||||
mainRelay.split("'").join("''"),
|
||||
id,
|
||||
id,
|
||||
]
|
||||
|
||||
return await db.executeAsync(query, queryParams)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const insertUserContact: (
|
||||
export const updateUserFollower: (
|
||||
userId: string,
|
||||
db: QuickSQLiteConnection,
|
||||
follower: boolean,
|
||||
) => Promise<QueryResult | null> = async (userId, db, follower) => {
|
||||
const userQuery = `UPDATE nostros_users SET follower = ? WHERE id = ?`
|
||||
|
||||
await addUser(userId, db)
|
||||
return db.execute(userQuery, [follower ? 1 : 0, userId])
|
||||
}
|
||||
export const updateUserContact: (
|
||||
userId: string,
|
||||
db: QuickSQLiteConnection,
|
||||
contact: boolean,
|
||||
) => Promise<QueryResult | null> = async (userId, db, contact) => {
|
||||
const userQuery = `UPDATE nostros_users SET contact = ? WHERE id = ?`
|
||||
|
||||
await addUser(userId, db)
|
||||
return db.execute(userQuery, [contact ? 1 : 0, userId])
|
||||
}
|
||||
|
||||
export const insertUserPets: (
|
||||
event: Event,
|
||||
db: QuickSQLiteConnection,
|
||||
) => Promise<User[] | null> = async (event, db) => {
|
||||
@ -60,7 +82,7 @@ export const insertUserContact: (
|
||||
if (valid && event.kind === EventKind.petNames) {
|
||||
const users: User[] = event.tags.map((tag) => tagToUser(tag))
|
||||
users.map(async (user) => {
|
||||
return await addContact(user.id, db)
|
||||
return await updateUserContact(user.id, db, true)
|
||||
})
|
||||
return users
|
||||
} else {
|
||||
@ -88,51 +110,50 @@ export const removeContact: (
|
||||
pubkey: string,
|
||||
db: QuickSQLiteConnection,
|
||||
) => Promise<QueryResult> = async (pubkey, db) => {
|
||||
const userQuery = `UPDATE nostros_users SET contact = FALSE WHERE id = ?`
|
||||
const userQuery = `UPDATE nostros_users SET contact = 0 WHERE id = ?`
|
||||
|
||||
return db.execute(userQuery, [pubkey])
|
||||
}
|
||||
|
||||
export const addContact: (
|
||||
pubKey: string,
|
||||
db: QuickSQLiteConnection,
|
||||
) => Promise<QueryResult> = async (pubKey, db) => {
|
||||
export const addUser: (pubKey: string, db: QuickSQLiteConnection) => Promise<QueryResult> = async (
|
||||
pubKey,
|
||||
db,
|
||||
) => {
|
||||
const query = `
|
||||
INSERT INTO nostros_users (id, contact)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT (id) DO
|
||||
UPDATE SET contact=excluded.contact;
|
||||
INSERT OR IGNORE INTO nostros_users (id) VALUES (?)
|
||||
`
|
||||
|
||||
return db.execute(query, [pubKey, '1'])
|
||||
return db.execute(query, [pubKey])
|
||||
}
|
||||
|
||||
export const getUsers: (
|
||||
db: QuickSQLiteConnection,
|
||||
options: { exludeIds?: string[]; contacts?: boolean; includeIds?: string[] },
|
||||
) => Promise<User[]> = async (db, { exludeIds, contacts, includeIds }) => {
|
||||
options: { exludeIds?: string[]; contacts?: boolean; followers?: boolean; includeIds?: string[] },
|
||||
) => Promise<User[]> = async (db, { exludeIds, contacts, followers, includeIds }) => {
|
||||
let userQuery = 'SELECT * FROM nostros_users '
|
||||
|
||||
if (contacts) {
|
||||
userQuery += 'WHERE contact = TRUE '
|
||||
}
|
||||
const filters = []
|
||||
|
||||
if (exludeIds && exludeIds.length > 0) {
|
||||
if (!contacts) {
|
||||
userQuery += 'WHERE '
|
||||
} else {
|
||||
userQuery += 'AND '
|
||||
}
|
||||
userQuery += `id NOT IN ('${exludeIds.join("', '")}') `
|
||||
if (contacts) {
|
||||
filters.push('contact = 1')
|
||||
}
|
||||
|
||||
if (includeIds && includeIds.length > 0) {
|
||||
if (!contacts && !exludeIds) {
|
||||
userQuery += 'WHERE '
|
||||
} else {
|
||||
userQuery += 'OR '
|
||||
filters.push(`id IN ('${includeIds.join("', '")}')`)
|
||||
}
|
||||
|
||||
if (followers) {
|
||||
filters.push('follower = 1')
|
||||
}
|
||||
|
||||
if (filters.length > 0) {
|
||||
userQuery += `WHERE ${filters.join(' OR ')} `
|
||||
if (exludeIds && exludeIds.length > 0) {
|
||||
userQuery += `AND id NOT IN ('${exludeIds.join("', '")}') `
|
||||
}
|
||||
} else {
|
||||
if (exludeIds && exludeIds.length > 0) {
|
||||
userQuery += `WHERE id NOT IN ('${exludeIds.join("', '")}') `
|
||||
}
|
||||
userQuery += `id IN ('${includeIds.join("', '")}') `
|
||||
}
|
||||
|
||||
userQuery += 'ORDER BY name,id'
|
||||
|
@ -42,6 +42,8 @@
|
||||
"privateKey": "Private Key"
|
||||
},
|
||||
"contactsPage": {
|
||||
"following": "Following",
|
||||
"followers": "Followers",
|
||||
"add": "Add User",
|
||||
"addContact": {
|
||||
"placeholder": "Public Key",
|
||||
|
Loading…
Reference in New Issue
Block a user