diff --git a/.eslintrc.js b/.eslintrc.js index 25282dd..1dbda4e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,8 +4,7 @@ module.exports = { 'eslint:recommended', 'plugin:react/recommended', 'standard-with-typescript', - 'prettier', - '@react-native-community', + 'prettier' ], parserOptions: { ecmaVersion: 8, diff --git a/App.tsx b/App.tsx index 5a09bac..fe12dae 100644 --- a/App.tsx +++ b/App.tsx @@ -1,6 +1,6 @@ -import App from './frontend'; -import { Buffer as SafeBuffer } from 'safe-buffer'; +import App from './frontend' +import { Buffer as SafeBuffer } from 'safe-buffer' -global.Buffer = SafeBuffer; +global.Buffer = SafeBuffer -export default App; +export default App diff --git a/__tests__/App-test.js b/__tests__/App-test.js deleted file mode 100644 index 1784766..0000000 --- a/__tests__/App-test.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @format - */ - -import 'react-native'; -import React from 'react'; -import App from '../App'; - -// Note: test renderer must be required after react-native. -import renderer from 'react-test-renderer'; - -it('renders correctly', () => { - renderer.create(); -}); diff --git a/frontend/Components/ConfigPage/index.tsx b/frontend/Components/ConfigPage/index.tsx index 4067e59..5efa00e 100644 --- a/frontend/Components/ConfigPage/index.tsx +++ b/frontend/Components/ConfigPage/index.tsx @@ -3,66 +3,66 @@ import { Layout, TopNavigation, TopNavigationAction, - useTheme, -} from '@ui-kitten/components'; -import React, { useContext, useEffect } from 'react'; -import { StyleSheet } from 'react-native'; -import { AppContext } from '../../Contexts/AppContext'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import { useTranslation } from 'react-i18next'; + useTheme +} from '@ui-kitten/components' +import React, { useContext, useEffect } from 'react' +import { StyleSheet } from 'react-native' +import { AppContext } from '../../Contexts/AppContext' +import Icon from 'react-native-vector-icons/FontAwesome5' +import { useTranslation } from 'react-i18next' // import EncryptedStorage from 'react-native-encrypted-storage'; -import { dropTables } from '../../Functions/DatabaseFunctions'; -import { RelayPoolContext } from '../../Contexts/RelayPoolContext'; +import { dropTables } from '../../Functions/DatabaseFunctions' +import { RelayPoolContext } from '../../Contexts/RelayPoolContext' export const ConfigPage: React.FC = () => { - const theme = useTheme(); - const { goToPage, goBack, database, init } = useContext(AppContext); - const { setPrivateKey, setPublicKey, relayPool } = useContext(RelayPoolContext); - const { t } = useTranslation('common'); + const theme = useTheme() + const { goToPage, goBack, database, init } = useContext(AppContext) + const { setPrivateKey, setPublicKey, relayPool } = useContext(RelayPoolContext) + const { t } = useTranslation('common') useEffect(() => { - relayPool?.unsubscribeAll(); - }, []); + relayPool?.unsubscribeAll() + }, []) const onPressBack: () => void = () => { - relayPool?.unsubscribeAll(); - goBack(); - }; + relayPool?.unsubscribeAll() + goBack() + } const onPressLogout: () => void = () => { if (database) { - relayPool?.unsubscribeAll(); - setPrivateKey(undefined); - setPublicKey(undefined); + relayPool?.unsubscribeAll() + setPrivateKey(undefined) + setPublicKey(undefined) dropTables(database).then(() => { // EncryptedStorage.removeItem('privateKey').then(() => { - init(); - goToPage('landing', true); + init() + goToPage('landing', true) // }); - }); + }) } - }; + } const renderBackAction = (): JSX.Element => ( } onPress={onPressBack} /> - ); + ) const styles = StyleSheet.create({ container: { - flex: 1, + flex: 1 }, actionContainer: { marginTop: 30, paddingLeft: 32, - paddingRight: 32, + paddingRight: 32 }, button: { - marginTop: 30, - }, - }); + marginTop: 30 + } + }) return ( <> @@ -87,7 +87,7 @@ export const ConfigPage: React.FC = () => { - ); -}; + ) +} -export default ConfigPage; +export default ConfigPage diff --git a/frontend/Components/ContactsPage/index.tsx b/frontend/Components/ContactsPage/index.tsx index f1f5078..4e804fb 100644 --- a/frontend/Components/ContactsPage/index.tsx +++ b/frontend/Components/ContactsPage/index.tsx @@ -1,109 +1,109 @@ -import { Button, Card, Input, Layout, Modal, useTheme } from '@ui-kitten/components'; -import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { RefreshControl, ScrollView, StyleSheet } from 'react-native'; -import ActionButton from 'react-native-action-button'; -import { AppContext } from '../../Contexts/AppContext'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import { Event, EventKind } from '../../lib/nostr/Events'; -import { useTranslation } from 'react-i18next'; +import { Button, Card, Input, Layout, Modal, useTheme } from '@ui-kitten/components' +import React, { useCallback, useContext, useEffect, useState } from 'react' +import { RefreshControl, ScrollView, StyleSheet } from 'react-native' +import ActionButton from 'react-native-action-button' +import { AppContext } from '../../Contexts/AppContext' +import Icon from 'react-native-vector-icons/FontAwesome5' +import { Event, EventKind } from '../../lib/nostr/Events' +import { useTranslation } from 'react-i18next' import { addContact, getUsers, insertUserContact, - User, -} from '../../Functions/DatabaseFunctions/Users'; -import UserCard from '../UserCard'; -import { RelayPoolContext } from '../../Contexts/RelayPoolContext'; -import Relay from '../../lib/nostr/Relay'; -import { populatePets, tagToUser } from '../../Functions/RelayFunctions/Users'; + User +} from '../../Functions/DatabaseFunctions/Users' +import UserCard from '../UserCard' +import { RelayPoolContext } from '../../Contexts/RelayPoolContext' +import Relay from '../../lib/nostr/Relay' +import { populatePets, tagToUser } from '../../Functions/RelayFunctions/Users' export const ContactsPage: React.FC = () => { - const { database } = useContext(AppContext); - const { relayPool, publicKey, lastEventId, setLastEventId } = useContext(RelayPoolContext); - const theme = useTheme(); - const [users, setUsers] = useState([]); - const [refreshing, setRefreshing] = useState(false); - const [showAddContact, setShowAddContant] = useState(false); - const [contactInput, setContactInput] = useState(); - const { t } = useTranslation('common'); + const { database } = useContext(AppContext) + const { relayPool, publicKey, lastEventId, setLastEventId } = useContext(RelayPoolContext) + const theme = useTheme() + const [users, setUsers] = useState([]) + const [refreshing, setRefreshing] = useState(false) + const [showAddContact, setShowAddContant] = useState(false) + const [contactInput, setContactInput] = useState() + const { t } = useTranslation('common') useEffect(() => { if (database && publicKey) { getUsers(database, { contacts: true }).then((results) => { - if (results) setUsers(results); - }); + if (results) setUsers(results) + }) } - }, [lastEventId]); + }, [lastEventId]) useEffect(() => { - subscribeContacts(); - }, []); + subscribeContacts() + }, []) const subscribeContacts: () => Promise = async () => { return await new Promise((resolve, _reject) => { - relayPool?.unsubscribeAll(); + relayPool?.unsubscribeAll() relayPool?.on('event', 'contacts', (relay: Relay, _subId?: string, event?: Event) => { - console.log('CONTACTS PAGE EVENT =======>', relay.url, event); + console.log('CONTACTS PAGE EVENT =======>', relay.url, event) if (database && event?.id && event.kind === EventKind.petNames) { - insertUserContact(event, database).finally(() => setLastEventId(event?.id ?? '')); + insertUserContact(event, database).finally(() => setLastEventId(event?.id ?? '')) relayPool?.subscribe('main-channel', { kinds: [EventKind.meta], - authors: event.tags.map((tag) => tagToUser(tag).id), - }); - relayPool?.removeOn('event', 'contacts'); + authors: event.tags.map((tag) => tagToUser(tag).id) + }) + relayPool?.removeOn('event', 'contacts') } - }); + }) if (publicKey) { relayPool?.subscribe('main-channel', { kinds: [EventKind.petNames], - authors: [publicKey], - }); + authors: [publicKey] + }) } - resolve(); - }); - }; + resolve() + }) + } const onPressAddContact: () => void = () => { if (contactInput && relayPool && database && publicKey) { addContact(contactInput, database).then(() => { - populatePets(relayPool, database, publicKey); - setShowAddContant(false); - }); + populatePets(relayPool, database, publicKey) + setShowAddContant(false) + }) } - }; + } const onRefresh = useCallback(() => { - setRefreshing(true); - relayPool?.unsubscribeAll(); - subscribeContacts().finally(() => setRefreshing(false)); - }, []); + setRefreshing(true) + relayPool?.unsubscribeAll() + subscribeContacts().finally(() => setRefreshing(false)) + }, []) const styles = StyleSheet.create({ container: { - flex: 1, + flex: 1 }, actionContainer: { marginTop: 30, marginBottom: 30, paddingLeft: 12, - paddingRight: 12, + paddingRight: 12 }, button: { - marginTop: 30, + marginTop: 30 }, icon: { width: 32, - height: 32, + height: 32 }, modal: { paddingLeft: 32, paddingRight: 32, - width: '100%', + width: '100%' }, backdrop: { - backgroundColor: 'rgba(0, 0, 0, 0.5)', - }, - }); + backgroundColor: 'rgba(0, 0, 0, 0.5)' + } + }) return ( <> @@ -152,7 +152,7 @@ export const ContactsPage: React.FC = () => { */} - ); -}; + ) +} -export default ContactsPage; +export default ContactsPage diff --git a/frontend/Components/HomePage/index.tsx b/frontend/Components/HomePage/index.tsx index 67fbcf3..8266a47 100644 --- a/frontend/Components/HomePage/index.tsx +++ b/frontend/Components/HomePage/index.tsx @@ -1,111 +1,111 @@ -import { Card, Layout, useTheme } from '@ui-kitten/components'; -import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { RefreshControl, ScrollView, StyleSheet } from 'react-native'; -import { AppContext } from '../../Contexts/AppContext'; -import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'; -import NoteCard from '../NoteCard'; -import ActionButton from 'react-native-action-button'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -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 Loading from '../Loading'; +import { Card, Layout, useTheme } from '@ui-kitten/components' +import React, { useCallback, useContext, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { RefreshControl, ScrollView, StyleSheet } from 'react-native' +import { AppContext } from '../../Contexts/AppContext' +import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes' +import NoteCard from '../NoteCard' +import ActionButton from 'react-native-action-button' +import Icon from 'react-native-vector-icons/FontAwesome5' +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 Loading from '../Loading' export const HomePage: React.FC = () => { - const { database, goToPage, page } = useContext(AppContext); - const { lastEventId, relayPool, publicKey } = useContext(RelayPoolContext); - const theme = useTheme(); - const [notes, setNotes] = useState([]); - const [totalContacts, setTotalContacts] = useState(-1); - const [refreshing, setRefreshing] = useState(false); - const { t } = useTranslation('common'); + const { database, goToPage, page } = useContext(AppContext) + const { lastEventId, relayPool, publicKey } = useContext(RelayPoolContext) + const theme = useTheme() + const [notes, setNotes] = useState([]) + const [totalContacts, setTotalContacts] = useState(-1) + const [refreshing, setRefreshing] = useState(false) + const { t } = useTranslation('common') const loadNotes: () => void = () => { if (database && publicKey) { getNotes(database, { contacts: true, includeIds: [publicKey], limit: 15 }).then((notes) => { - setNotes(notes); - }); + setNotes(notes) + }) } - }; + } const subscribeNotes: () => Promise = async () => { return await new Promise((resolve, reject) => { if (database && publicKey && relayPool) { getNotes(database, { limit: 1 }).then((notes) => { getUsers(database, { contacts: true, includeIds: [publicKey] }).then((users) => { - setTotalContacts(users.length); + setTotalContacts(users.length) let message: RelayFilters = { kinds: [EventKind.textNote, EventKind.recommendServer], authors: users.map((user) => user.id), - limit: 15, - }; + limit: 15 + } if (notes.length !== 0) { message = { ...message, - since: notes[0].created_at, - }; + since: notes[0].created_at + } } - relayPool?.subscribe('main-channel', message); - resolve(); - }); - }); + relayPool?.subscribe('main-channel', message) + resolve() + }) + }) } else { - reject(new Error('Not Ready')); + reject(new Error('Not Ready')) } - }); - }; + }) + } useEffect(() => { - relayPool?.unsubscribeAll(); - }, []); + relayPool?.unsubscribeAll() + }, []) useEffect(() => { - loadNotes(); - }, [lastEventId]); + loadNotes() + }, [lastEventId]) useEffect(() => { - loadNotes(); - subscribeNotes(); - }, [database, publicKey, relayPool]); + loadNotes() + subscribeNotes() + }, [database, publicKey, relayPool]) const onRefresh = useCallback(() => { - setRefreshing(true); - relayPool?.unsubscribeAll(); - subscribeNotes().finally(() => setRefreshing(false)); - }, []); + setRefreshing(true) + relayPool?.unsubscribeAll() + subscribeNotes().finally(() => setRefreshing(false)) + }, []) const onPress: (note: Note) => void = (note) => { if (note.kind !== EventKind.recommendServer) { - const replyEventId = getReplyEventId(note); + const replyEventId = getReplyEventId(note) if (replyEventId) { - goToPage(`note#${replyEventId}`); + goToPage(`note#${replyEventId}`) } else if (note.id) { - goToPage(`note#${note.id}`); + goToPage(`note#${note.id}`) } } - }; + } const itemCard: (note: Note) => JSX.Element = (note) => { return ( onPress(note)} key={note.id ?? ''}> - ); - }; + ) + } const styles = StyleSheet.create({ container: { - flex: 1, + flex: 1 }, icon: { width: 32, - height: 32, - }, - }); + height: 32 + } + }) return ( <> @@ -135,7 +135,7 @@ export const HomePage: React.FC = () => { */} - ); -}; + ) +} -export default HomePage; +export default HomePage diff --git a/frontend/Components/LandingPage/Logger/index.tsx b/frontend/Components/LandingPage/Logger/index.tsx index c8ba9d3..6a7100d 100644 --- a/frontend/Components/LandingPage/Logger/index.tsx +++ b/frontend/Components/LandingPage/Logger/index.tsx @@ -1,112 +1,112 @@ -import React, { useContext, useEffect, useState } from 'react'; -import { Button, Input } from '@ui-kitten/components'; -import { StyleSheet } from 'react-native'; -import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'; -import { useTranslation } from 'react-i18next'; -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 } from '../../../Functions/DatabaseFunctions/Users'; +import React, { useContext, useEffect, useState } from 'react' +import { Button, Input } from '@ui-kitten/components' +import { StyleSheet } from 'react-native' +import { RelayPoolContext } from '../../../Contexts/RelayPoolContext' +import { useTranslation } from 'react-i18next' +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 } from '../../../Functions/DatabaseFunctions/Users' // import EncryptedStorage from 'react-native-encrypted-storage'; export const Logger: React.FC = () => { - const { database, goToPage } = useContext(AppContext); - const { privateKey, publicKey, relayPool, setPrivateKey } = useContext(RelayPoolContext); - const { t } = useTranslation('common'); - const [loading, setLoading] = useState(false); - const [status, setStatus] = useState(0); - const [totalPets, setTotalPets] = useState(); - const [inputValue, setInputValue] = useState(''); - const [loadedUsers, setLoadedUsers] = useState(); + const { database, goToPage } = useContext(AppContext) + const { privateKey, publicKey, relayPool, setPrivateKey } = useContext(RelayPoolContext) + const { t } = useTranslation('common') + const [loading, setLoading] = useState(false) + const [status, setStatus] = useState(0) + const [totalPets, setTotalPets] = useState() + const [inputValue, setInputValue] = useState('') + const [loadedUsers, setLoadedUsers] = useState() const styles = StyleSheet.create({ input: { marginVertical: 2, - padding: 32, - }, - }); + padding: 32 + } + }) useEffect(() => { if (relayPool && publicKey) { - relayPool?.unsubscribeAll(); - setStatus(1); - initEvents(); + relayPool?.unsubscribeAll() + setStatus(1) + initEvents() relayPool?.subscribe('main-channel', { kinds: [EventKind.petNames], - authors: [publicKey], - }); + authors: [publicKey] + }) } - }, [relayPool, publicKey]); + }, [relayPool, publicKey]) useEffect(() => { if (status > 2) { - relayPool?.removeOn('event', 'landing'); - goToPage('home', true); + relayPool?.removeOn('event', 'landing') + goToPage('home', true) } - }, [status]); + }, [status]) useEffect(() => { if (loadedUsers) { - const timer = setTimeout(() => setStatus(3), 4000); + const timer = setTimeout(() => setStatus(3), 4000) return () => { - clearTimeout(timer); - }; + clearTimeout(timer) + } } - }, [loadedUsers]); + }, [loadedUsers]) const initEvents: () => void = () => { relayPool?.on('event', 'landing', (_relay: Relay, _subId?: string, event?: Event) => { - console.log('LANDING EVENT =======>', event); + console.log('LANDING EVENT =======>', event) if (event && database) { if (event.kind === EventKind.petNames) { - loadPets(event); + loadPets(event) } else if (event.kind === EventKind.meta) { - setLoadedUsers((prev) => (prev ? prev + 1 : 1)); - if (loadedUsers && loadedUsers - 1 === totalPets) setStatus(3); + setLoadedUsers((prev) => (prev ? prev + 1 : 1)) + if (loadedUsers && loadedUsers - 1 === totalPets) setStatus(3) } } - }); - }; + }) + } const loadPets: (event: Event) => void = (event) => { if (database) { - setTotalPets(event.tags.length); + setTotalPets(event.tags.length) if (event.tags.length > 0) { - setStatus(2); + setStatus(2) insertUserContact(event, database).then(() => { - requestUserData(event); - }); + requestUserData(event) + }) } else { - setStatus(3); + setStatus(3) } } - }; + } const requestUserData: (event: Event) => void = (event) => { if (publicKey) { - const authors: string[] = [publicKey, ...event.tags.map((tag) => tagToUser(tag).id)]; + const authors: string[] = [publicKey, ...event.tags.map((tag) => tagToUser(tag).id)] relayPool?.subscribe('main-channel', { kinds: [EventKind.meta], - authors, - }); + authors + }) } - }; + } const onPress: () => void = () => { if (inputValue && inputValue !== '') { - setLoading(true); - setPrivateKey(inputValue); - setStatus(1); + setLoading(true) + setPrivateKey(inputValue) + setStatus(1) // EncryptedStorage.setItem('privateKey', inputValue); } - }; + } const statusName: { [status: number]: string } = { 0: t('landing.connect'), 1: t('landing.connecting'), 2: t('landing.loadingContacts'), - 3: t('landing.ready'), - }; + 3: t('landing.ready') + } return !privateKey || status !== 0 ? ( <> @@ -125,7 +125,7 @@ export const Logger: React.FC = () => { ) : ( <> - ); -}; + ) +} -export default Logger; +export default Logger diff --git a/frontend/Components/LandingPage/index.tsx b/frontend/Components/LandingPage/index.tsx index b45fc91..5cfc747 100644 --- a/frontend/Components/LandingPage/index.tsx +++ b/frontend/Components/LandingPage/index.tsx @@ -1,27 +1,27 @@ -import React from 'react'; -import { Layout, Text } from '@ui-kitten/components'; -import { StyleSheet } from 'react-native'; -import Loading from '../Loading'; -import Logger from './Logger'; +import React from 'react' +import { Layout, Text } from '@ui-kitten/components' +import { StyleSheet } from 'react-native' +import Loading from '../Loading' +import Logger from './Logger' export const LandingPage: React.FC = () => { const styles = StyleSheet.create({ tab: { height: '100%', alignItems: 'center', - justifyContent: 'center', + justifyContent: 'center' }, svg: { height: 340, - width: 340, + width: 340 }, title: { marginTop: -40, marginBottom: -20, fontFamily: 'SpaceGrotesk-Bold', - fontSize: 45, - }, - }); + fontSize: 45 + } + }) return ( @@ -33,7 +33,7 @@ export const LandingPage: React.FC = () => { - ); -}; + ) +} -export default LandingPage; +export default LandingPage diff --git a/frontend/Components/Loading/index.tsx b/frontend/Components/Loading/index.tsx index ad5f132..cc35692 100644 --- a/frontend/Components/Loading/index.tsx +++ b/frontend/Components/Loading/index.tsx @@ -1,31 +1,31 @@ -import React from 'react'; -import { Animated, Easing } from 'react-native'; -import { SvgXml } from 'react-native-svg'; +import React from 'react' +import { Animated, Easing } from 'react-native' +import { SvgXml } from 'react-native-svg' interface LoadingProps { - style?: object; + style?: object } export const Loading: React.FC = ({ style = {} }) => { - const logo = ``; + const logo = '' - const spinValue = new Animated.Value(0); + const spinValue = new Animated.Value(0) Animated.timing(spinValue, { toValue: 1, duration: 100000, easing: Easing.linear, // Easing is an additional import from react-native - useNativeDriver: true, // To make use of native driver for performance - }).start(); + useNativeDriver: true // To make use of native driver for performance + }).start() const spin = spinValue.interpolate({ inputRange: [0, 1], - outputRange: ['0deg', '2016deg'], - }); + outputRange: ['0deg', '2016deg'] + }) return ( - ); -}; + ) +} -export default Loading; +export default Loading diff --git a/frontend/Components/MainLayout/index.tsx b/frontend/Components/MainLayout/index.tsx index 1496def..44340af 100644 --- a/frontend/Components/MainLayout/index.tsx +++ b/frontend/Components/MainLayout/index.tsx @@ -1,24 +1,24 @@ -import React, { useContext } from 'react'; -import { Layout } from '@ui-kitten/components'; -import { StyleSheet } from 'react-native'; -import { AppContext } from '../../Contexts/AppContext'; -import HomePage from '../HomePage'; -import ProfilePage from '../ProfilePage'; -import NavigationBar from '../NavigationBar'; -import SendPage from '../SendPage'; -import ContactsPage from '../ContactsPage'; -import NotePage from '../NotePage'; -import LandingPage from '../LandingPage'; -import ConfigPage from '../ConfigPage'; +import React, { useContext } from 'react' +import { Layout } from '@ui-kitten/components' +import { StyleSheet } from 'react-native' +import { AppContext } from '../../Contexts/AppContext' +import HomePage from '../HomePage' +import ProfilePage from '../ProfilePage' +import NavigationBar from '../NavigationBar' +import SendPage from '../SendPage' +import ContactsPage from '../ContactsPage' +import NotePage from '../NotePage' +import LandingPage from '../LandingPage' +import ConfigPage from '../ConfigPage' export const MainLayout: React.FC = () => { - const { page } = useContext(AppContext); + const { page } = useContext(AppContext) const styles = StyleSheet.create({ container: { - flex: 1, - }, - }); + flex: 1 + } + }) const pagination: { [pageName: string]: JSX.Element } = { landing: , @@ -27,21 +27,21 @@ export const MainLayout: React.FC = () => { profile: , contacts: , note: , - config: , - }; + config: + } - const breadcrump: string[] = page.split('%'); - const pageToDisplay: string = breadcrump[breadcrump.length - 1].split('#')[0]; + const breadcrump: string[] = page.split('%') + const pageToDisplay: string = breadcrump[breadcrump.length - 1].split('#')[0] const view: () => JSX.Element = () => { if (page === '') { - return ; + return } else if (page === 'landing') { return ( - ); + ) } else { return ( <> @@ -50,11 +50,11 @@ export const MainLayout: React.FC = () => { - ); + ) } - }; + } - return <>{view()}; -}; + return <>{view()} +} -export default MainLayout; +export default MainLayout diff --git a/frontend/Components/NavigationBar/index.tsx b/frontend/Components/NavigationBar/index.tsx index 8742d43..6b5abce 100644 --- a/frontend/Components/NavigationBar/index.tsx +++ b/frontend/Components/NavigationBar/index.tsx @@ -1,26 +1,26 @@ -import React, { useContext } from 'react'; -import { BottomNavigation, BottomNavigationTab, useTheme } from '@ui-kitten/components'; -import { AppContext } from '../../Contexts/AppContext'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import { RelayPoolContext } from '../../Contexts/RelayPoolContext'; +import React, { useContext } from 'react' +import { BottomNavigation, BottomNavigationTab, useTheme } from '@ui-kitten/components' +import { AppContext } from '../../Contexts/AppContext' +import Icon from 'react-native-vector-icons/FontAwesome5' +import { RelayPoolContext } from '../../Contexts/RelayPoolContext' export const NavigationBar: React.FC = () => { - const { goToPage, page } = useContext(AppContext); - const { publicKey } = useContext(RelayPoolContext); - const theme = useTheme(); - const profilePage = `profile#${publicKey ?? ''}`; + const { goToPage, page } = useContext(AppContext) + const { publicKey } = useContext(RelayPoolContext) + const theme = useTheme() + const profilePage = `profile#${publicKey ?? ''}` - const pageIndex: string[] = ['home', 'contacts', profilePage]; + const pageIndex: string[] = ['home', 'contacts', profilePage] const getIndex: () => number = () => { if (page.includes('profile')) { - return page === profilePage ? 2 : 1; + return page === profilePage ? 2 : 1 } else if (page.includes('note#')) { - return 0; + return 0 } else { - return pageIndex.indexOf(page); + return pageIndex.indexOf(page) } - }; + } return page ? ( { ) : ( <> - ); -}; + ) +} -export default NavigationBar; +export default NavigationBar diff --git a/frontend/Components/NoteCard/index.tsx b/frontend/Components/NoteCard/index.tsx index 0b9e241..d45ec58 100644 --- a/frontend/Components/NoteCard/index.tsx +++ b/frontend/Components/NoteCard/index.tsx @@ -1,31 +1,31 @@ -import React, { useContext, useState } from 'react'; -import { Button, Layout, Text, useTheme } from '@ui-kitten/components'; -import { Note } from '../../Functions/DatabaseFunctions/Notes'; -import { StyleSheet, TouchableOpacity } from 'react-native'; -import UserAvatar from 'react-native-user-avatar'; -import Markdown from 'react-native-markdown-display'; -import { EventKind } from '../../lib/nostr/Events'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import { RelayPoolContext } from '../../Contexts/RelayPoolContext'; -import { storeRelay } from '../../Functions/DatabaseFunctions/Relays'; -import { AppContext } from '../../Contexts/AppContext'; -import { showMessage } from 'react-native-flash-message'; -import { t } from 'i18next'; -import { getReplyEventId } from '../../Functions/RelayFunctions/Events'; -import moment from 'moment'; -import { populateRelay } from '../../Functions/RelayFunctions'; +import React, { useContext, useState } from 'react' +import { Button, Layout, Text, useTheme } from '@ui-kitten/components' +import { Note } from '../../Functions/DatabaseFunctions/Notes' +import { StyleSheet, TouchableOpacity } from 'react-native' +import UserAvatar from 'react-native-user-avatar' +import Markdown from 'react-native-markdown-display' +import { EventKind } from '../../lib/nostr/Events' +import Icon from 'react-native-vector-icons/FontAwesome5' +import { RelayPoolContext } from '../../Contexts/RelayPoolContext' +import { storeRelay } from '../../Functions/DatabaseFunctions/Relays' +import { AppContext } from '../../Contexts/AppContext' +import { showMessage } from 'react-native-flash-message' +import { t } from 'i18next' +import { getReplyEventId } from '../../Functions/RelayFunctions/Events' +import moment from 'moment' +import { populateRelay } from '../../Functions/RelayFunctions' interface NoteCardProps { - note: Note; + note: Note } export const NoteCard: React.FC = ({ note }) => { - const theme = useTheme(); - const { relayPool, setRelayPool, publicKey } = useContext(RelayPoolContext); - const { database, goToPage } = useContext(AppContext); + const theme = useTheme() + const { relayPool, setRelayPool, publicKey } = useContext(RelayPoolContext) + const { database, goToPage } = useContext(AppContext) const [relayAdded, setRelayAdded] = useState( - Object.keys(relayPool?.relays ?? {}).includes(note.content), - ); + Object.keys(relayPool?.relays ?? {}).includes(note.content) + ) const textNote: () => JSX.Element = () => { return ( @@ -65,26 +65,26 @@ export const NoteCard: React.FC = ({ note }) => { - ); - }; + ) + } const relayNote: () => JSX.Element = () => { - const relayName = note.content.split('wss://')[1].split('/')[0]; + const relayName = note.content.split('wss://')[1].split('/')[0] const addRelay: () => void = () => { if (relayPool && database && publicKey) { - relayPool.add(note.content); - setRelayPool(relayPool); - storeRelay({ url: note.content }, database); - populateRelay(relayPool, database, publicKey); + relayPool.add(note.content) + setRelayPool(relayPool) + storeRelay({ url: note.content }, database) + populateRelay(relayPool, database, publicKey) showMessage({ message: t('alerts.relayAdded'), description: note.content, - type: 'success', - }); - setRelayAdded(true); + type: 'success' + }) + setRelayAdded(true) } - }; + } return ( <> @@ -107,97 +107,97 @@ export const NoteCard: React.FC = ({ note }) => { )} - ); - }; + ) + } const onPressUser: () => void = () => { - goToPage(`profile#${note.pubkey}`); - }; + goToPage(`profile#${note.pubkey}`) + } const styles = StyleSheet.create({ layout: { flexDirection: 'row', - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, profile: { flex: 1, width: 38, alignItems: 'center', - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, content: { flex: 4, backgroundColor: 'transparent', paddingLeft: 16, - paddingRight: 16, + paddingRight: 16 }, contentNoAction: { flex: 5, backgroundColor: 'transparent', paddingLeft: 16, - paddingRight: 16, + paddingRight: 16 }, actions: { flex: 1, - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, pubkey: { - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, footer: { - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, tags: { backgroundColor: 'transparent', - marginLeft: 12, + marginLeft: 12 }, titleText: { backgroundColor: 'transparent', - flexDirection: 'row', + flexDirection: 'row' }, text: { backgroundColor: 'transparent', - paddingRight: 10, - }, - }); + paddingRight: 10 + } + }) const markdownStyle = { text: { - color: theme['text-basic-color'], + color: theme['text-basic-color'] }, tr: { - borderColor: theme['border-primary-color-5'], + borderColor: theme['border-primary-color-5'] }, table: { - borderColor: theme['border-primary-color-5'], + borderColor: theme['border-primary-color-5'] }, blocklink: { - borderColor: theme['border-primary-color-5'], + borderColor: theme['border-primary-color-5'] }, hr: { - backgroundColor: theme['background-basic-color-3'], + backgroundColor: theme['background-basic-color-3'] }, blockquote: { backgroundColor: theme['background-basic-color-3'], borderColor: theme['border-primary-color-5'], - color: theme['text-basic-color'], + color: theme['text-basic-color'] }, code_inline: { borderColor: theme['border-primary-color-5'], backgroundColor: theme['background-basic-color-3'], - color: theme['text-basic-color'], + color: theme['text-basic-color'] }, code_block: { borderColor: theme['border-primary-color-5'], backgroundColor: theme['background-basic-color-3'], - color: theme['text-basic-color'], + color: theme['text-basic-color'] }, fence: { borderColor: theme['border-primary-color-5'], backgroundColor: theme['background-basic-color-3'], - color: theme['text-basic-color'], - }, - }; + color: theme['text-basic-color'] + } + } return ( note && ( @@ -205,7 +205,7 @@ export const NoteCard: React.FC = ({ note }) => { {note.kind === EventKind.recommendServer ? relayNote() : textNote()} ) - ); -}; + ) +} -export default NoteCard; +export default NoteCard diff --git a/frontend/Components/NotePage/index.tsx b/frontend/Components/NotePage/index.tsx index 02facd9..27a1638 100644 --- a/frontend/Components/NotePage/index.tsx +++ b/frontend/Components/NotePage/index.tsx @@ -1,59 +1,59 @@ -import { Card, Layout, TopNavigation, TopNavigationAction, useTheme } from '@ui-kitten/components'; -import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { AppContext } from '../../Contexts/AppContext'; -import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'; -import { RelayPoolContext } from '../../Contexts/RelayPoolContext'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import NoteCard from '../NoteCard'; -import { EventKind } from '../../lib/nostr/Events'; -import { RelayFilters } from '../../lib/nostr/Relay'; -import { RefreshControl, ScrollView, StyleSheet } from 'react-native'; -import Loading from '../Loading'; -import ActionButton from 'react-native-action-button'; -import { useTranslation } from 'react-i18next'; -import { getDirectReplies, getReplyEventId } from '../../Functions/RelayFunctions/Events'; +import { Card, Layout, TopNavigation, TopNavigationAction, useTheme } from '@ui-kitten/components' +import React, { useCallback, useContext, useEffect, useState } from 'react' +import { AppContext } from '../../Contexts/AppContext' +import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes' +import { RelayPoolContext } from '../../Contexts/RelayPoolContext' +import Icon from 'react-native-vector-icons/FontAwesome5' +import NoteCard from '../NoteCard' +import { EventKind } from '../../lib/nostr/Events' +import { RelayFilters } from '../../lib/nostr/Relay' +import { RefreshControl, ScrollView, StyleSheet } from 'react-native' +import Loading from '../Loading' +import ActionButton from 'react-native-action-button' +import { useTranslation } from 'react-i18next' +import { getDirectReplies, getReplyEventId } from '../../Functions/RelayFunctions/Events' export const NotePage: React.FC = () => { - const { page, goBack, goToPage, database } = useContext(AppContext); - const { lastEventId, relayPool } = useContext(RelayPoolContext); - const [note, setNote] = useState(); - const [replies, setReplies] = useState(); - const [refreshing, setRefreshing] = useState(false); - const theme = useTheme(); - const { t } = useTranslation('common'); - const breadcrump = page.split('%'); - const eventId = breadcrump[breadcrump.length - 1].split('#')[1]; + const { page, goBack, goToPage, database } = useContext(AppContext) + const { lastEventId, relayPool } = useContext(RelayPoolContext) + const [note, setNote] = useState() + const [replies, setReplies] = useState() + const [refreshing, setRefreshing] = useState(false) + const theme = useTheme() + const { t } = useTranslation('common') + const breadcrump = page.split('%') + const eventId = breadcrump[breadcrump.length - 1].split('#')[1] const reload: (newEventId?: string) => void = (newEventId) => { - setNote(undefined); - setReplies(undefined); - relayPool?.unsubscribeAll(); + setNote(undefined) + setReplies(undefined) + relayPool?.unsubscribeAll() relayPool?.subscribe('main-channel', { kinds: [EventKind.textNote], - ids: [newEventId ?? eventId], - }); - }; + ids: [newEventId ?? eventId] + }) + } - useEffect(reload, []); + useEffect(reload, []) useEffect(() => { - subscribeNotes(); - }, [lastEventId, page]); + subscribeNotes() + }, [lastEventId, page]) const onPressBack: () => void = () => { - relayPool?.unsubscribeAll(); - goBack(); - }; + relayPool?.unsubscribeAll() + goBack() + } const onPressGoParent: () => void = () => { if (note) { - const replyId = getReplyEventId(note); + const replyId = getReplyEventId(note) if (replyId) { - goToPage(`note#${replyId}`); - reload(replyId); + goToPage(`note#${replyId}`) + reload(replyId) } } - }; + } const renderBackAction = (): JSX.Element => { return ( @@ -61,8 +61,8 @@ export const NotePage: React.FC = () => { icon={} onPress={onPressBack} /> - ); - }; + ) + } const renderNoteActions = (): JSX.Element => { return note && getReplyEventId(note) ? ( @@ -72,20 +72,20 @@ export const NotePage: React.FC = () => { /> ) : ( <> - ); - }; + ) + } const onPressNote: (note: Note) => void = (note) => { if (note.kind !== EventKind.recommendServer) { - const replyEventId = getReplyEventId(note); + const replyEventId = getReplyEventId(note) if (replyEventId && replyEventId !== eventId) { - goToPage(`note#${replyEventId}`); + goToPage(`note#${replyEventId}`) } else if (note.id) { - goToPage(`note#${note.id}`); + goToPage(`note#${note.id}`) } - reload(); + reload() } - }; + } const itemCard: (note?: Note) => JSX.Element = (note) => { if (note?.id === eventId) { @@ -93,72 +93,72 @@ export const NotePage: React.FC = () => { - ); + ) } else if (note) { return ( onPressNote(note)} key={note.id ?? ''}> - ); + ) } else { - return <>; + return <> } - }; + } const subscribeNotes: () => Promise = async () => { return await new Promise((resolve, reject) => { if (database) { getNotes(database, { filters: { id: eventId } }).then((events) => { if (events.length > 0) { - const event = events[0]; - setNote(event); + const event = events[0] + setNote(event) if (!replies) { relayPool?.subscribe('main-channel', { kinds: [EventKind.textNote], - '#e': [eventId], - }); + '#e': [eventId] + }) } getNotes(database, { filters: { reply_event_id: eventId } }).then((notes) => { - const rootReplies = getDirectReplies(event, notes); + const rootReplies = getDirectReplies(event, notes) if (rootReplies.length > 0) { - setReplies(rootReplies as Note[]); + setReplies(rootReplies as Note[]) const message: RelayFilters = { kinds: [EventKind.meta], - authors: [...rootReplies.map((note) => note.pubkey), event.pubkey], - }; - relayPool?.subscribe('main-channel', message); + authors: [...rootReplies.map((note) => note.pubkey), event.pubkey] + } + relayPool?.subscribe('main-channel', message) } else { - setReplies([]); + setReplies([]) } - resolve(); - }); + resolve() + }) } else { - resolve(); + resolve() } - }); + }) } else { - reject(new Error('Not Ready')); + reject(new Error('Not Ready')) } - }); - }; + }) + } const onRefresh = useCallback(() => { - setRefreshing(true); - relayPool?.unsubscribeAll(); - subscribeNotes().finally(() => setRefreshing(false)); - }, []); + setRefreshing(true) + relayPool?.unsubscribeAll() + subscribeNotes().finally(() => setRefreshing(false)) + }, []) const styles = StyleSheet.create({ main: { paddingBottom: 32, paddingTop: 26, paddingLeft: 26, - paddingRight: 26, + paddingRight: 26 }, loading: { - maxHeight: 160, - }, - }); + maxHeight: 160 + } + }) return ( <> @@ -193,7 +193,7 @@ export const NotePage: React.FC = () => { */} - ); -}; + ) +} -export default NotePage; +export default NotePage diff --git a/frontend/Components/ProfilePage/index.tsx b/frontend/Components/ProfilePage/index.tsx index c3bb9e4..582f280 100644 --- a/frontend/Components/ProfilePage/index.tsx +++ b/frontend/Components/ProfilePage/index.tsx @@ -5,89 +5,89 @@ import { Text, TopNavigation, TopNavigationAction, - 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'; -import UserAvatar from 'react-native-user-avatar'; -import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'; -import NoteCard from '../NoteCard'; -import { RelayPoolContext } from '../../Contexts/RelayPoolContext'; + 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' +import UserAvatar from 'react-native-user-avatar' +import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes' +import NoteCard from '../NoteCard' +import { RelayPoolContext } from '../../Contexts/RelayPoolContext' import { getUser, removeContact, addContact, User, - getUsers, -} 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'; -import ActionButton from 'react-native-action-button'; -import { useTranslation } from 'react-i18next'; -import { populatePets, tagToUser } from '../../Functions/RelayFunctions/Users'; -import { getReplyEventId } from '../../Functions/RelayFunctions/Events'; -import Loading from '../Loading'; -import { storeEvent } from '../../Functions/DatabaseFunctions/Events'; + getUsers +} 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' +import ActionButton from 'react-native-action-button' +import { useTranslation } from 'react-i18next' +import { populatePets, tagToUser } from '../../Functions/RelayFunctions/Users' +import { getReplyEventId } from '../../Functions/RelayFunctions/Events' +import Loading from '../Loading' +import { storeEvent } from '../../Functions/DatabaseFunctions/Events' export const ProfilePage: React.FC = () => { - const { database, page, goToPage, goBack } = useContext(AppContext); - const { publicKey, lastEventId, relayPool, setLastEventId } = useContext(RelayPoolContext); - const theme = useTheme(); - const [notes, setNotes] = useState(); - const { t } = useTranslation('common'); - const [user, setUser] = useState(); - const [contactsIds, setContactsIds] = useState(); - const [isContact, setIsContact] = useState(); - const [refreshing, setRefreshing] = useState(false); - const breadcrump = page.split('%'); - const userId = breadcrump[breadcrump.length - 1].split('#')[1] ?? publicKey; - const username = user?.name === '' ? user?.id : user?.name; + const { database, page, goToPage, goBack } = useContext(AppContext) + const { publicKey, lastEventId, relayPool, setLastEventId } = useContext(RelayPoolContext) + const theme = useTheme() + const [notes, setNotes] = useState() + const { t } = useTranslation('common') + const [user, setUser] = useState() + const [contactsIds, setContactsIds] = useState() + const [isContact, setIsContact] = useState() + const [refreshing, setRefreshing] = useState(false) + const breadcrump = page.split('%') + const userId = breadcrump[breadcrump.length - 1].split('#')[1] ?? publicKey + const username = user?.name === '' ? user?.id : user?.name useEffect(() => { - setContactsIds(undefined); - setNotes(undefined); - setUser(undefined); - loadProfile(); - }, [page]); + setContactsIds(undefined) + setNotes(undefined) + setUser(undefined) + loadProfile() + }, [page]) useEffect(() => { if (database) { getUser(userId, database).then((result) => { if (result) { - setUser(result); - setIsContact(result?.contact); + setUser(result) + setIsContact(result?.contact) } - }); + }) if (userId === publicKey) { getUsers(database, { contacts: true }).then((users) => { - setContactsIds(users.map((user) => user.id)); - }); + setContactsIds(users.map((user) => user.id)) + }) } getNotes(database, { filters: { pubkey: userId }, limit: 10 }).then((results) => { - if (results.length > 0) setNotes(results); - }); + if (results.length > 0) setNotes(results) + }) } - }, [lastEventId]); + }, [lastEventId]) const removeAuthor: () => void = () => { if (relayPool && database && publicKey) { removeContact(userId, database).then(() => { - populatePets(relayPool, database, publicKey); - setIsContact(false); - }); + populatePets(relayPool, database, publicKey) + setIsContact(false) + }) } - }; + } const addAuthor: () => void = () => { if (relayPool && database && publicKey) { addContact(userId, database).then(() => { - populatePets(relayPool, database, publicKey); - setIsContact(true); - }); + populatePets(relayPool, database, publicKey) + setIsContact(true) + }) } - }; + } const renderOptions: () => JSX.Element = () => { if (publicKey === userId) { @@ -96,7 +96,7 @@ export const ProfilePage: React.FC = () => { icon={} onPress={() => goToPage('config')} /> - ); + ) } else { if (user) { if (isContact) { @@ -105,45 +105,45 @@ export const ProfilePage: React.FC = () => { icon={} onPress={removeAuthor} /> - ); + ) } else { return ( } onPress={addAuthor} /> - ); + ) } } else { - return ; + return } } - }; + } const onPressBack: () => void = () => { - relayPool?.removeOn('event', 'profile'); - relayPool?.unsubscribeAll(); - goBack(); - }; + relayPool?.removeOn('event', 'profile') + relayPool?.unsubscribeAll() + goBack() + } const renderBackAction = (): JSX.Element => { if (publicKey === userId) { - return <>; + return <> } else { return ( } onPress={onPressBack} /> - ); + ) } - }; + } const onRefresh = useCallback(() => { - setRefreshing(true); - relayPool?.unsubscribeAll(); - loadProfile().finally(() => setRefreshing(false)); - }, []); + setRefreshing(true) + relayPool?.unsubscribeAll() + loadProfile().finally(() => setRefreshing(false)) + }, []) const subscribeNotes: () => void = () => { if (database) { @@ -152,62 +152,62 @@ export const ProfilePage: React.FC = () => { const notesEvent: RelayFilters = { kinds: [EventKind.textNote, EventKind.recommendServer], authors: [userId], - limit: 10, - }; - - if (results.length >= 10) { - notesEvent.since = results[0]?.created_at; + limit: 10 } - relayPool?.subscribe('main-channel', notesEvent); + if (results.length >= 10) { + notesEvent.since = results[0]?.created_at + } + + relayPool?.subscribe('main-channel', notesEvent) } - }); + }) } - }; + } const loadProfile: () => Promise = async () => { return await new Promise((resolve, reject) => { relayPool?.subscribe('main-channel', { kinds: [EventKind.meta, EventKind.petNames], - authors: [userId], - }); + authors: [userId] + }) relayPool?.on('event', 'profile', (_relay: Relay, _subId?: string, event?: Event) => { - console.log('PROFILE EVENT =======>', event); + console.log('PROFILE EVENT =======>', event) if (database) { if (event?.id && event.pubkey === userId) { if (event.kind === EventKind.petNames) { - const ids = event.tags.map((tag) => tagToUser(tag).id); - setContactsIds(ids); + const ids = event.tags.map((tag) => tagToUser(tag).id) + setContactsIds(ids) } else if (event.kind === EventKind.meta) { storeEvent(event, database).finally(() => { - if (event?.id) setLastEventId(event.id); - }); + if (event?.id) setLastEventId(event.id) + }) } - subscribeNotes(); + subscribeNotes() } } else { - reject(new Error('Not Ready')); + reject(new Error('Not Ready')) } - }); - resolve(); - }); - }; + }) + resolve() + }) + } const styles = StyleSheet.create({ list: { - flex: 1, + flex: 1 }, icon: { width: 32, - height: 32, + height: 32 }, settingsIcon: { width: 48, - height: 48, + height: 48 }, avatar: { width: 130, - marginBottom: 16, + marginBottom: 16 }, profile: { flex: 1, @@ -215,59 +215,59 @@ export const ProfilePage: React.FC = () => { alignItems: 'center', marginBottom: 2, paddingLeft: 32, - paddingRight: 32, + paddingRight: 32 }, loading: { - maxHeight: 160, + maxHeight: 160 }, about: { flex: 4, - maxHeight: 200, + maxHeight: 200 }, stats: { - flex: 1, + flex: 1 }, statsItem: { flexDirection: 'row', alignItems: 'center', - marginBottom: 5, + marginBottom: 5 }, description: { marginTop: 16, - flexDirection: 'row', - }, - }); + flexDirection: 'row' + } + }) const itemCard: (note: Note) => JSX.Element = (note) => { return ( onPressNote(note)} key={note.id ?? ''}> - ); - }; + ) + } const onPressNote: (note: Note) => void = (note) => { if (note.kind !== EventKind.recommendServer) { - const mainEventId = getReplyEventId(note); + const mainEventId = getReplyEventId(note) if (mainEventId) { - goToPage(`note#${mainEventId}`); + goToPage(`note#${mainEventId}`) } else if (note.id) { - goToPage(`note#${note.id}`); + goToPage(`note#${note.id}`) } } - }; + } const onPressId: () => void = () => { // FIXME // Clipboard.setString(user?.id ?? ''); - }; + } const isFollowingUser: () => boolean = () => { if (contactsIds !== undefined && publicKey) { - return contactsIds?.includes(publicKey); + return contactsIds?.includes(publicKey) } - return false; - }; + return false + } const stats: () => JSX.Element = () => { if (contactsIds === undefined) { @@ -275,7 +275,7 @@ export const ProfilePage: React.FC = () => { - ); + ) } return ( @@ -297,8 +297,8 @@ export const ProfilePage: React.FC = () => { )} - ); - }; + ) + } const profile: JSX.Element = ( @@ -330,7 +330,7 @@ export const ProfilePage: React.FC = () => { )} - ); + ) return ( <> @@ -369,7 +369,7 @@ export const ProfilePage: React.FC = () => { <> )} - ); -}; + ) +} -export default ProfilePage; +export default ProfilePage diff --git a/frontend/Components/SendPage/index.tsx b/frontend/Components/SendPage/index.tsx index 16102e4..719b395 100644 --- a/frontend/Components/SendPage/index.tsx +++ b/frontend/Components/SendPage/index.tsx @@ -5,70 +5,70 @@ import { Spinner, TopNavigation, TopNavigationAction, - useTheme, -} from '@ui-kitten/components'; -import React, { useContext, useEffect, useState } from 'react'; -import { StyleSheet } from 'react-native'; -import { AppContext } from '../../Contexts/AppContext'; -import Icon from 'react-native-vector-icons/FontAwesome5'; -import { Event, EventKind } from '../../lib/nostr/Events'; -import { useTranslation } from 'react-i18next'; -import { RelayPoolContext } from '../../Contexts/RelayPoolContext'; -import moment from 'moment'; -import { getNotes } from '../../Functions/DatabaseFunctions/Notes'; -import { getETags } from '../../Functions/RelayFunctions/Events'; + useTheme +} from '@ui-kitten/components' +import React, { useContext, useEffect, useState } from 'react' +import { StyleSheet } from 'react-native' +import { AppContext } from '../../Contexts/AppContext' +import Icon from 'react-native-vector-icons/FontAwesome5' +import { Event, EventKind } from '../../lib/nostr/Events' +import { useTranslation } from 'react-i18next' +import { RelayPoolContext } from '../../Contexts/RelayPoolContext' +import moment from 'moment' +import { getNotes } from '../../Functions/DatabaseFunctions/Notes' +import { getETags } from '../../Functions/RelayFunctions/Events' export const SendPage: React.FC = () => { - const theme = useTheme(); - const { goBack, page, database } = useContext(AppContext); - const { relayPool, publicKey, lastEventId } = useContext(RelayPoolContext); - const { t } = useTranslation('common'); - const [content, setContent] = useState(''); - const [sending, setSending] = useState(false); - const [noteId, setNoteId] = useState(); - const breadcrump = page.split('%'); - const eventId = breadcrump[breadcrump.length - 1].split('#')[1]; + const theme = useTheme() + const { goBack, page, database } = useContext(AppContext) + const { relayPool, publicKey, lastEventId } = useContext(RelayPoolContext) + const { t } = useTranslation('common') + const [content, setContent] = useState('') + const [sending, setSending] = useState(false) + const [noteId, setNoteId] = useState() + const breadcrump = page.split('%') + const eventId = breadcrump[breadcrump.length - 1].split('#')[1] useEffect(() => { - relayPool?.unsubscribeAll(); - }, []); + relayPool?.unsubscribeAll() + }, []) useEffect(() => { if (sending && noteId === lastEventId) { - goBack(); + goBack() } - }, [lastEventId]); + }, [lastEventId]) const styles = StyleSheet.create({ container: { - flex: 1, + flex: 1 }, actionContainer: { marginTop: 30, paddingLeft: 32, - paddingRight: 32, + paddingRight: 32 }, button: { - marginTop: 30, - }, - }); + marginTop: 30 + } + }) const onPressBack: () => void = () => { - goBack(); - }; + goBack() + } const onPressSend: () => void = () => { if (database && publicKey) { getNotes(database, { filters: { id: eventId } }).then((notes) => { - let tags: string[][] = []; - const note = notes[0]; + let tags: string[][] = [] + const note = notes[0] if (note) { - tags = note.tags; + tags = note.tags if (getETags(note).length === 0) { - tags.push(['e', eventId, '', 'root']); + tags.push(['e', eventId, '', 'root']) } else { - tags.push(['e', eventId, '', 'reply']); + tags.push(['e', eventId, '', 'reply']) } } @@ -77,28 +77,28 @@ export const SendPage: React.FC = () => { created_at: moment().unix(), kind: EventKind.textNote, pubkey: publicKey, - tags, - }; + tags + } relayPool?.sendEvent(event).then((sentNote) => { if (sentNote?.id) { relayPool?.subscribe('main-channel', { kinds: [EventKind.textNote], - ids: [sentNote.id], - }); - setNoteId(sentNote.id); + ids: [sentNote.id] + }) + setNoteId(sentNote.id) } - }); - setSending(true); - }); + }) + setSending(true) + }) } - }; + } const renderBackAction = (): JSX.Element => ( } onPress={onPressBack} /> - ); + ) return ( <> @@ -133,7 +133,7 @@ export const SendPage: React.FC = () => { - ); -}; + ) +} -export default SendPage; +export default SendPage diff --git a/frontend/Components/UserCard/index.tsx b/frontend/Components/UserCard/index.tsx index b6310fb..3818785 100644 --- a/frontend/Components/UserCard/index.tsx +++ b/frontend/Components/UserCard/index.tsx @@ -1,40 +1,40 @@ -import React, { useContext } from 'react'; -import { Card, Layout, Text, useTheme } from '@ui-kitten/components'; -import { User } from '../../Functions/DatabaseFunctions/Users'; -import { StyleSheet } from 'react-native'; -import UserAvatar from 'react-native-user-avatar'; -import { AppContext } from '../../Contexts/AppContext'; +import React, { useContext } from 'react' +import { Card, Layout, Text, useTheme } from '@ui-kitten/components' +import { User } from '../../Functions/DatabaseFunctions/Users' +import { StyleSheet } from 'react-native' +import UserAvatar from 'react-native-user-avatar' +import { AppContext } from '../../Contexts/AppContext' interface NoteCardProps { - user: User; + user: User } export const NoteCard: React.FC = ({ user }) => { - const { goToPage } = useContext(AppContext); - const theme = useTheme(); + const { goToPage } = useContext(AppContext) + const theme = useTheme() const styles = StyleSheet.create({ layout: { flex: 1, flexDirection: 'row', - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, profile: { flex: 1, width: 38, justifyContent: 'center', alignItems: 'center', - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, content: { flex: 5, - backgroundColor: 'transparent', + backgroundColor: 'transparent' }, actions: { flex: 1, - backgroundColor: 'transparent', - }, - }); + backgroundColor: 'transparent' + } + }) return ( user && ( @@ -55,7 +55,7 @@ export const NoteCard: React.FC = ({ user }) => { ) - ); -}; + ) +} -export default NoteCard; +export default NoteCard diff --git a/frontend/Contexts/AppContext.tsx b/frontend/Contexts/AppContext.tsx index 960e3a5..72053a7 100644 --- a/frontend/Contexts/AppContext.tsx +++ b/frontend/Contexts/AppContext.tsx @@ -1,21 +1,21 @@ -import React, { useEffect, useState } from 'react'; -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import { initDatabase } from '../Functions/DatabaseFunctions'; -import { createInitDatabase } from '../Functions/DatabaseFunctions/Migrations'; -import FlashMessage from 'react-native-flash-message'; +import React, { useEffect, useState } from 'react' +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import { initDatabase } from '../Functions/DatabaseFunctions' +import { createInitDatabase } from '../Functions/DatabaseFunctions/Migrations' +import FlashMessage from 'react-native-flash-message' // import EncryptedStorage from 'react-native-encrypted-storage'; export interface AppContextProps { - page: string; - goToPage: (path: string, root?: boolean) => void; - goBack: () => void; - init: () => void; - loadingDb: boolean; - database: SQLiteDatabase | null; + page: string + goToPage: (path: string, root?: boolean) => void + goBack: () => void + init: () => void + loadingDb: boolean + database: SQLiteDatabase | null } export interface AppContextProviderProps { - children: React.ReactNode; + children: React.ReactNode } export const initialAppContext: AppContextProps = { @@ -24,43 +24,43 @@ export const initialAppContext: AppContextProps = { goToPage: () => {}, goBack: () => {}, loadingDb: true, - database: null, -}; + database: null +} export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.Element => { - const [page, setPage] = useState(initialAppContext.page); - const [database, setDatabase] = useState(null); - const [loadingDb, setLoadingDb] = useState(initialAppContext.loadingDb); + const [page, setPage] = useState(initialAppContext.page) + const [database, setDatabase] = useState(null) + const [loadingDb, setLoadingDb] = useState(initialAppContext.loadingDb) const init: () => void = () => { - const result = ''; + const result = '' // EncryptedStorage.getItem('privateKey').then((result) => { - const db = initDatabase(); - setDatabase(db); - if (!result || result === '') { - createInitDatabase(db).then(() => { - setLoadingDb(false); - }); - } else { - setLoadingDb(false); - } + const db = initDatabase() + setDatabase(db) + if (!result || result === '') { + createInitDatabase(db).then(() => { + setLoadingDb(false) + }) + } else { + setLoadingDb(false) + } // }); - }; + } - useEffect(init, []); + useEffect(init, []) const goToPage: (path: string, root?: boolean) => void = (path, root) => { if (page !== '' && !root) { - setPage(`${page}%${path}`); + setPage(`${page}%${path}`) } else { - setPage(path); + setPage(path) } - }; + } const goBack: () => void = () => { - const breadcrump = page.split('%'); - setPage(breadcrump.slice(0, -1).join('%') || 'home'); - }; + const breadcrump = page.split('%') + setPage(breadcrump.slice(0, -1).join('%') || 'home') + } return ( {children} - ); -}; + ) +} -export const AppContext = React.createContext(initialAppContext); +export const AppContext = React.createContext(initialAppContext) diff --git a/frontend/Contexts/RelayPoolContext.tsx b/frontend/Contexts/RelayPoolContext.tsx index f296c65..131e44e 100644 --- a/frontend/Contexts/RelayPoolContext.tsx +++ b/frontend/Contexts/RelayPoolContext.tsx @@ -1,60 +1,60 @@ -import React, { useContext, useEffect, useState } from 'react'; -import Relay from '../lib/nostr/Relay'; -import { Event, EventKind } from '../lib/nostr/Events'; -import RelayPool from '../lib/nostr/RelayPool/intex'; -import { AppContext } from './AppContext'; -import { storeEvent } from '../Functions/DatabaseFunctions/Events'; -import { getRelays, Relay as RelayEntity, storeRelay } from '../Functions/DatabaseFunctions/Relays'; -import { showMessage } from 'react-native-flash-message'; +import React, { useContext, useEffect, useState } from 'react' +import Relay from '../lib/nostr/Relay' +import { Event, EventKind } from '../lib/nostr/Events' +import RelayPool from '../lib/nostr/RelayPool/intex' +import { AppContext } from './AppContext' +import { storeEvent } from '../Functions/DatabaseFunctions/Events' +import { getRelays, Relay as RelayEntity, storeRelay } from '../Functions/DatabaseFunctions/Relays' +import { showMessage } from 'react-native-flash-message' // import EncryptedStorage from 'react-native-encrypted-storage'; -import { getPublickey } from '../lib/nostr/Bip'; +import { getPublickey } from '../lib/nostr/Bip' export interface RelayPoolContextProps { - relayPool?: RelayPool; - setRelayPool: (relayPool: RelayPool) => void; - publicKey?: string; - setPublicKey: (privateKey: string | undefined) => void; - privateKey?: string; - setPrivateKey: (privateKey: string | undefined) => void; - lastEventId?: string; - setLastEventId: (lastEventId: string) => void; + relayPool?: RelayPool + setRelayPool: (relayPool: RelayPool) => void + publicKey?: string + setPublicKey: (privateKey: string | undefined) => void + privateKey?: string + setPrivateKey: (privateKey: string | undefined) => void + lastEventId?: string + setLastEventId: (lastEventId: string) => void } export interface RelayPoolContextProviderProps { - children: React.ReactNode; + children: React.ReactNode } export const initialRelayPoolContext: RelayPoolContextProps = { setPublicKey: () => {}, setPrivateKey: () => {}, setRelayPool: () => {}, - setLastEventId: () => {}, -}; + setLastEventId: () => {} +} export const RelayPoolContextProvider = ({ - children, + children }: RelayPoolContextProviderProps): JSX.Element => { - const { database, loadingDb, goToPage, page } = useContext(AppContext); + const { database, loadingDb, goToPage, page } = useContext(AppContext) - const [publicKey, setPublicKey] = useState(); - const [privateKey, setPrivateKey] = useState(); - const [relayPool, setRelayPool] = useState(); - const [lastEventId, setLastEventId] = useState(); - const [lastPage, setLastPage] = useState(page); + const [publicKey, setPublicKey] = useState() + const [privateKey, setPrivateKey] = useState() + const [relayPool, setRelayPool] = useState() + const [lastEventId, setLastEventId] = useState() + const [lastPage, setLastPage] = useState(page) const loadRelayPool: () => void = () => { if (database && privateKey) { getRelays(database).then((relays: RelayEntity[]) => { - const initRelayPool = new RelayPool([], privateKey); + const initRelayPool = new RelayPool([], privateKey) if (relays.length > 0) { relays.forEach((relay) => { - initRelayPool.add(relay.url); - }); + initRelayPool.add(relay.url) + }) } else { ['wss://relay.damus.io'].forEach((relayUrl) => { - initRelayPool.add(relayUrl); - storeRelay({ url: relayUrl }, database); - }); + initRelayPool.add(relayUrl) + storeRelay({ url: relayUrl }, database) + }) } initRelayPool?.on( @@ -64,59 +64,59 @@ export const RelayPoolContextProvider = ({ showMessage({ message: relay.url, description: event?.content ?? '', - type: 'info', - }); - }, - ); + type: 'info' + }) + } + ) initRelayPool?.on( 'event', 'RelayPoolContextProvider', (relay: Relay, _subId?: string, event?: Event) => { - console.log('RELAYPOOL EVENT =======>', relay.url, event); + console.log('RELAYPOOL EVENT =======>', relay.url, event) if (database && event?.id && event.kind !== EventKind.petNames) { storeEvent(event, database) .then(() => setLastEventId(event.id)) - .catch(() => setLastEventId(event.id)); + .catch(() => setLastEventId(event.id)) } - }, - ); - setRelayPool(initRelayPool); - }); + } + ) + setRelayPool(initRelayPool) + }) } - }; + } useEffect(() => { if (privateKey && privateKey !== '') { - setPublicKey(getPublickey(privateKey)); + setPublicKey(getPublickey(privateKey)) } - }, [privateKey]); + }, [privateKey]) useEffect(() => { if (privateKey !== '' && !loadingDb && !relayPool) { - loadRelayPool(); + loadRelayPool() } - }, [privateKey, loadingDb]); + }, [privateKey, loadingDb]) useEffect(() => { if (relayPool && lastPage !== page) { - relayPool.removeOn('event', lastPage); - setLastPage(page); + relayPool.removeOn('event', lastPage) + setLastPage(page) } - }, [page]); + }, [page]) useEffect(() => { const result = '' // EncryptedStorage.getItem('privateKey').then((result) => { - if (result && result !== '') { - loadRelayPool(); - goToPage('home', true); - setPrivateKey(result); - setPublicKey(getPublickey(result)); - } else { - goToPage('landing', true); - } + if (result && result !== '') { + loadRelayPool() + goToPage('home', true) + setPrivateKey(result) + setPublicKey(getPublickey(result)) + } else { + goToPage('landing', true) + } // }); - }, []); + }, []) return ( {children} - ); -}; + ) +} -export const RelayPoolContext = React.createContext(initialRelayPoolContext); +export const RelayPoolContext = React.createContext(initialRelayPoolContext) diff --git a/frontend/Functions/DatabaseFunctions/Errors/index.ts b/frontend/Functions/DatabaseFunctions/Errors/index.ts index b5cd66b..879bcb1 100644 --- a/frontend/Functions/DatabaseFunctions/Errors/index.ts +++ b/frontend/Functions/DatabaseFunctions/Errors/index.ts @@ -1,13 +1,13 @@ -import { SQLError, Transaction } from 'react-native-sqlite-storage'; +import { SQLError, Transaction } from 'react-native-sqlite-storage' export const errorCallback: ( query: string, reject?: (reason?: any) => void, ) => (transaction: Transaction, error: SQLError) => void = (query, reject) => { const callback: (transaction: Transaction, error: SQLError) => void = (_transaction, error) => { - console.log('SQL ERROR =========>', query, error); - if (reject) reject(error); - }; + console.log('SQL ERROR =========>', query, error) + if (reject) reject(error) + } - return callback; -}; + return callback +} diff --git a/frontend/Functions/DatabaseFunctions/Events/index.ts b/frontend/Functions/DatabaseFunctions/Events/index.ts index 60bafd6..05e0b3e 100644 --- a/frontend/Functions/DatabaseFunctions/Events/index.ts +++ b/frontend/Functions/DatabaseFunctions/Events/index.ts @@ -1,21 +1,21 @@ -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import { Event, EventKind } from '../../../lib/nostr/Events'; -import { insertNote } from '../Notes'; -import { insertUserMeta } from '../Users'; +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import { Event, EventKind } from '../../../lib/nostr/Events' +import { insertNote } from '../Notes' +import { insertUserMeta } from '../Users' export const storeEvent: (event: Event, db: SQLiteDatabase) => Promise = async ( event, - db, + db ) => { return await new Promise((resolve, reject) => { try { if (event.kind === EventKind.meta) { - insertUserMeta(event, db).then(resolve); + insertUserMeta(event, db).then(resolve) } else if (event.kind === EventKind.textNote || event.kind === EventKind.recommendServer) { - insertNote(event, db).then(resolve); + insertNote(event, db).then(resolve) } } catch (e) { - reject(e); + reject(e) } - }); -}; + }) +} diff --git a/frontend/Functions/DatabaseFunctions/Migrations/index.ts b/frontend/Functions/DatabaseFunctions/Migrations/index.ts index 3ba7c7b..77b50df 100644 --- a/frontend/Functions/DatabaseFunctions/Migrations/index.ts +++ b/frontend/Functions/DatabaseFunctions/Migrations/index.ts @@ -1,5 +1,5 @@ -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import { simpleExecute } from '..'; +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import { simpleExecute } from '..' export const createInitDatabase: (db: SQLiteDatabase) => Promise = async (db) => { return await new Promise((resolve) => { @@ -17,7 +17,7 @@ export const createInitDatabase: (db: SQLiteDatabase) => Promise = async ( reply_event_id TEXT ); `, - db, + db ).then(() => { simpleExecute( ` @@ -30,7 +30,7 @@ export const createInitDatabase: (db: SQLiteDatabase) => Promise = async ( contact BOOLEAN DEFAULT FALSE ); `, - db, + db ).then(() => { simpleExecute( ` @@ -39,9 +39,9 @@ export const createInitDatabase: (db: SQLiteDatabase) => Promise = async ( pet INTEGER ); `, - db, - ).then(() => resolve()); - }); - }); - }); -}; + db + ).then(() => resolve()) + }) + }) + }) +} diff --git a/frontend/Functions/DatabaseFunctions/Notes/index.ts b/frontend/Functions/DatabaseFunctions/Notes/index.ts index 42b105a..9aefeb1 100644 --- a/frontend/Functions/DatabaseFunctions/Notes/index.ts +++ b/frontend/Functions/DatabaseFunctions/Notes/index.ts @@ -1,34 +1,34 @@ -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import { getItems } from '..'; -import { Event, EventKind, verifySignature } from '../../../lib/nostr/Events'; -import { getMainEventId, getReplyEventId } from '../../RelayFunctions/Events'; -import { errorCallback } from '../Errors'; +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import { getItems } from '..' +import { Event, EventKind, verifySignature } from '../../../lib/nostr/Events' +import { getMainEventId, getReplyEventId } from '../../RelayFunctions/Events' +import { errorCallback } from '../Errors' export interface Note extends Event { - name: string; - picture: string; + name: string + picture: string } const databaseToEntity: (object: any) => Note = (object) => { - object.tags = JSON.parse(object.tags); - return object as Note; -}; + object.tags = JSON.parse(object.tags) + return object as Note +} export const insertNote: (event: Event, db: SQLiteDatabase) => Promise = async ( event, - db, + db ) => { return await new Promise((resolve, reject) => { - if (!verifySignature(event) || !event.id) return reject(new Error('Bad event')); + if (!verifySignature(event) || !event.id) return reject(new Error('Bad event')) if (![EventKind.textNote, EventKind.recommendServer].includes(event.kind)) - return reject(new Error('Bad Kind')); + return reject(new Error('Bad Kind')) getNotes(db, { filters: { id: event.id } }).then((notes) => { if (notes.length === 0 && event.id && event.sig) { - const mainEventId = getMainEventId(event) ?? ''; - const replyEventId = getReplyEventId(event) ?? ''; - const content = event.content.split("'").join("''"); - const tags = JSON.stringify(event.tags).split("'").join("''"); + const mainEventId = getMainEventId(event) ?? '' + const replyEventId = getReplyEventId(event) ?? '' + const content = event.content.split("'").join("''") + const tags = JSON.stringify(event.tags).split("'").join("''") const eventQuery = `INSERT INTO nostros_notes (id,content,created_at,kind,pubkey,sig,tags,main_event_id,reply_event_id) VALUES @@ -42,67 +42,67 @@ export const insertNote: (event: Event, db: SQLiteDatabase) => Promise = a '${tags}', '${mainEventId}', '${replyEventId}' - );`; + );` db.transaction((transaction) => { transaction.executeSql( eventQuery, [], () => resolve(), - errorCallback(eventQuery, reject), - ); - }); + errorCallback(eventQuery, reject) + ) + }) } else { - reject(new Error('Note already exists')); + reject(new Error('Note already exists')) } - }); - }); -}; + }) + }) +} export const getNotes: ( db: SQLiteDatabase, options: { - filters?: { [column: string]: string }; - limit?: number; - contacts?: boolean; - includeIds?: string[]; + filters?: { [column: string]: string } + limit?: number + contacts?: boolean + includeIds?: string[] }, ) => Promise = async (db, { filters = {}, limit, contacts, includeIds }) => { let notesQuery = ` SELECT nostros_notes.*, nostros_users.name, nostros_users.picture, nostros_users.contact FROM nostros_notes LEFT JOIN nostros_users ON nostros_users.id = nostros_notes.pubkey - `; + ` if (filters) { - const keys = Object.keys(filters); + const keys = Object.keys(filters) if (Object.keys(filters).length > 0) { - notesQuery += 'WHERE '; + notesQuery += 'WHERE ' keys.forEach((column, index) => { - notesQuery += `nostros_notes.${column} = '${filters[column]}' `; - if (index < keys.length - 1) notesQuery += 'AND '; - }); + notesQuery += `nostros_notes.${column} = '${filters[column]}' ` + if (index < keys.length - 1) notesQuery += 'AND ' + }) } } if (contacts) { if (Object.keys(filters).length > 0) { - notesQuery += 'AND '; + notesQuery += 'AND ' } else { - notesQuery += 'WHERE '; + notesQuery += 'WHERE ' } - notesQuery += 'nostros_users.contact = TRUE '; + notesQuery += 'nostros_users.contact = TRUE ' } if (includeIds) { if (Object.keys(filters).length > 0 || contacts) { - notesQuery += 'OR '; + notesQuery += 'OR ' } else { - notesQuery += 'WHERE '; + notesQuery += 'WHERE ' } - notesQuery += `nostros_users.id IN ('${includeIds.join("', '")}') `; + notesQuery += `nostros_users.id IN ('${includeIds.join("', '")}') ` } - notesQuery += `ORDER BY created_at DESC `; + notesQuery += 'ORDER BY created_at DESC ' if (limit) { - notesQuery += `LIMIT ${limit}`; + notesQuery += `LIMIT ${limit}` } return await new Promise((resolve, reject) => { @@ -111,12 +111,12 @@ export const getNotes: ( notesQuery, [], (_transaction, resultSet) => { - const items: object[] = getItems(resultSet); - const notes: Note[] = items.map((object) => databaseToEntity(object)); - resolve(notes); + const items: object[] = getItems(resultSet) + const notes: Note[] = items.map((object) => databaseToEntity(object)) + resolve(notes) }, - errorCallback(notesQuery, reject), - ); - }); - }); -}; + errorCallback(notesQuery, reject) + ) + }) + }) +} diff --git a/frontend/Functions/DatabaseFunctions/Relays/index.ts b/frontend/Functions/DatabaseFunctions/Relays/index.ts index 4da9366..120e3d7 100644 --- a/frontend/Functions/DatabaseFunctions/Relays/index.ts +++ b/frontend/Functions/DatabaseFunctions/Relays/index.ts @@ -1,19 +1,19 @@ -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import { getItems } from '..'; -import { errorCallback } from '../Errors'; +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import { getItems } from '..' +import { errorCallback } from '../Errors' export interface Relay { - url: string; - name?: string; + url: string + name?: string } const databaseToEntity: (object: any) => Relay = (object) => { - return object as Relay; -}; + return object as Relay +} export const storeRelay: (relay: Relay, db: SQLiteDatabase) => void = async (relay, db) => { if (relay.url) { - const relays: Relay[] = await searchRelays(relay.url, db); + const relays: Relay[] = await searchRelays(relay.url, db) if (relays.length === 0) { const eventQuery = ` INSERT INTO nostros_relays @@ -22,7 +22,7 @@ export const storeRelay: (relay: Relay, db: SQLiteDatabase) => void = async (rel ( '${relay.url.split("'").join("''")}' ); - `; + ` await new Promise((resolve, reject) => { db.transaction((transaction) => { @@ -30,21 +30,21 @@ export const storeRelay: (relay: Relay, db: SQLiteDatabase) => void = async (rel eventQuery, [], () => resolve(), - errorCallback(eventQuery, reject), - ); - }); - }); + errorCallback(eventQuery, reject) + ) + }) + }) } } -}; +} export const searchRelays: (relayUrl: string, db: SQLiteDatabase) => Promise = async ( relayUrl, - db, + db ) => { const searchQuery = ` SELECT * FROM nostros_relays WHERE url = '${relayUrl}'; - `; + ` return await new Promise((resolve, reject) => { db.transaction((transaction) => { @@ -52,18 +52,18 @@ export const searchRelays: (relayUrl: string, db: SQLiteDatabase) => Promise { - const items: object[] = getItems(resultSet); - const notes: Relay[] = items.map((object) => databaseToEntity(object)); - resolve(notes); + const items: object[] = getItems(resultSet) + const notes: Relay[] = items.map((object) => databaseToEntity(object)) + resolve(notes) }, - errorCallback(searchQuery, reject), - ); - }); - }); -}; + errorCallback(searchQuery, reject) + ) + }) + }) +} export const getRelays: (db: SQLiteDatabase) => Promise = async (db) => { - const notesQuery = `SELECT * FROM nostros_relays;`; + const notesQuery = 'SELECT * FROM nostros_relays;' return await new Promise((resolve, reject) => { db.readTransaction((transaction) => { @@ -71,12 +71,12 @@ export const getRelays: (db: SQLiteDatabase) => Promise = async (db) => notesQuery, [], (_transaction, resultSet) => { - const items: object[] = getItems(resultSet); - const relays: Relay[] = items.map((object) => databaseToEntity(object)); - resolve(relays); + const items: object[] = getItems(resultSet) + const relays: Relay[] = items.map((object) => databaseToEntity(object)) + resolve(relays) }, - errorCallback(notesQuery, reject), - ); - }); - }); -}; + errorCallback(notesQuery, reject) + ) + }) + }) +} diff --git a/frontend/Functions/DatabaseFunctions/Users/index.ts b/frontend/Functions/DatabaseFunctions/Users/index.ts index 6240aa9..f2fb314 100644 --- a/frontend/Functions/DatabaseFunctions/Users/index.ts +++ b/frontend/Functions/DatabaseFunctions/Users/index.ts @@ -1,39 +1,39 @@ -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import { getItems } from '..'; -import { Event, EventKind, verifySignature } from '../../../lib/nostr/Events'; -import { tagToUser } from '../../RelayFunctions/Users'; -import { errorCallback } from '../Errors'; +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import { getItems } from '..' +import { Event, EventKind, verifySignature } from '../../../lib/nostr/Events' +import { tagToUser } from '../../RelayFunctions/Users' +import { errorCallback } from '../Errors' export interface User { - id: string; - main_relay?: string; - name?: string; - picture?: string; - about?: string; - contact?: boolean; + id: string + main_relay?: string + name?: string + picture?: string + about?: string + contact?: boolean } const databaseToEntity: (object: object) => User = (object) => { - return object as User; -}; + return object as User +} export const insertUserMeta: (event: Event, db: SQLiteDatabase) => Promise = async ( event, - db, + db ) => { return await new Promise((resolve, reject) => { - if (!verifySignature(event)) return reject(new Error('Bad signature')); - if (event.kind !== EventKind.meta) return reject(new Error('Bad Kind')); + if (!verifySignature(event)) return reject(new Error('Bad signature')) + if (event.kind !== EventKind.meta) return reject(new Error('Bad Kind')) - const user: User = JSON.parse(event.content); - user.id = event.pubkey; + const user: User = JSON.parse(event.content) + user.id = event.pubkey getUser(user.id, db).then((userDb) => { - let userQuery = ''; - const id = user.id?.replace("\\'", "'") ?? ''; - const name = user.name?.replace("\\'", "'") ?? ''; - const mainRelay = user.main_relay?.replace("\\'", "'") ?? ''; - const about = user.about?.replace("\\'", "'") ?? ''; - const picture = user.picture?.replace("\\'", "'") ?? ''; + let userQuery = '' + const id = user.id?.replace("\\'", "'") ?? '' + const name = user.name?.replace("\\'", "'") ?? '' + const mainRelay = user.main_relay?.replace("\\'", "'") ?? '' + const about = user.about?.replace("\\'", "'") ?? '' + const picture = user.picture?.replace("\\'", "'") ?? '' if (userDb) { userQuery = ` @@ -43,7 +43,7 @@ export const insertUserMeta: (event: Event, db: SQLiteDatabase) => Promise about = '${about.split("'").join("''")}', picture = '${picture.split("'").join("''")}' WHERE id = '${user.id}'; - `; + ` } else { userQuery = ` INSERT INTO nostros_users @@ -52,35 +52,35 @@ export const insertUserMeta: (event: Event, db: SQLiteDatabase) => Promise ('${id}', '${name.split("'").join("''")}', '${picture.split("'").join("''")}', '${about .split("'") .join("''")}', ''); - `; + ` } db.transaction((transaction) => { - transaction.executeSql(userQuery, [], () => resolve(), errorCallback(userQuery, reject)); - }); - }); - }); -}; + transaction.executeSql(userQuery, [], () => resolve(), errorCallback(userQuery, reject)) + }) + }) + }) +} export const insertUserContact: (event: Event, db: SQLiteDatabase) => Promise = async ( event, - db, + db ) => { return await new Promise((resolve, reject) => { - if (!verifySignature(event)) return reject(new Error('Bad signature')); - if (event.kind !== EventKind.petNames) return reject(new Error('Bad Kind')); - const userTags: string[] = event.tags.map((tag) => tagToUser(tag).id); + if (!verifySignature(event)) return reject(new Error('Bad signature')) + if (event.kind !== EventKind.petNames) return reject(new Error('Bad Kind')) + const userTags: string[] = event.tags.map((tag) => tagToUser(tag).id) userTags.forEach((userId) => { - addContact(userId, db); - }); - resolve(); - }); -}; + addContact(userId, db) + }) + resolve() + }) +} export const getUser: (pubkey: string, db: SQLiteDatabase) => Promise = async ( pubkey, - db, + db ) => { - const userQuery = `SELECT * FROM nostros_users WHERE id = '${pubkey}';`; + const userQuery = `SELECT * FROM nostros_users WHERE id = '${pubkey}';` return await new Promise((resolve, reject) => { db.readTransaction((transaction) => { transaction.executeSql( @@ -88,83 +88,83 @@ export const getUser: (pubkey: string, db: SQLiteDatabase) => Promise { if (resultSet.rows.length > 0) { - const items: object[] = getItems(resultSet); - const user: User = databaseToEntity(items[0]); - resolve(user); + const items: object[] = getItems(resultSet) + const user: User = databaseToEntity(items[0]) + resolve(user) } else { - resolve(null); + resolve(null) } }, - errorCallback(userQuery, reject), - ); - }); - }); -}; + errorCallback(userQuery, reject) + ) + }) + }) +} export const removeContact: (pubkey: string, db: SQLiteDatabase) => Promise = async ( pubkey, - db, + db ) => { - const userQuery = `UPDATE nostros_users SET contact = FALSE WHERE id = '${pubkey}'`; + const userQuery = `UPDATE nostros_users SET contact = FALSE WHERE id = '${pubkey}'` return await new Promise((resolve, reject) => { db.transaction((transaction) => { - transaction.executeSql(userQuery, [], () => resolve(), errorCallback(userQuery, reject)); - }); - }); -}; + transaction.executeSql(userQuery, [], () => resolve(), errorCallback(userQuery, reject)) + }) + }) +} export const addContact: (pubkey: string, db: SQLiteDatabase) => Promise = async ( pubkey, - db, + db ) => { return await new Promise((resolve, reject) => { getUser(pubkey, db).then((userDb) => { - let userQuery = ''; + let userQuery = '' if (userDb) { userQuery = ` UPDATE nostros_users SET contact = TRUE WHERE id = '${pubkey}'; - `; + ` } else { userQuery = ` INSERT INTO nostros_users (id, contact) VALUES ('${pubkey}', TRUE); - `; + ` } db.transaction((transaction) => { - transaction.executeSql(userQuery, [], () => resolve(), errorCallback(userQuery, reject)); - }); - }); - }); -}; + transaction.executeSql(userQuery, [], () => resolve(), errorCallback(userQuery, reject)) + }) + }) + }) +} export const getUsers: ( db: SQLiteDatabase, - options: { exludeIds?: string[]; contacts?: boolean; includeIds?: string[] }, + options: { exludeIds?: string[], contacts?: boolean, includeIds?: string[] }, ) => Promise = async (db, { exludeIds, contacts, includeIds }) => { - let userQuery = `SELECT * FROM nostros_users `; + let userQuery = 'SELECT * FROM nostros_users ' if (contacts) { - userQuery += `WHERE contact = TRUE `; + userQuery += 'WHERE contact = TRUE ' } if (exludeIds && exludeIds.length > 0) { if (!contacts) { - userQuery += `WHERE `; + userQuery += 'WHERE ' } else { - userQuery += `AND `; + userQuery += 'AND ' } - userQuery += `id NOT IN ('${exludeIds.join("', '")}') `; + userQuery += `id NOT IN ('${exludeIds.join("', '")}') ` } if (includeIds && includeIds.length > 0) { if (!contacts && !exludeIds) { - userQuery += `WHERE `; + userQuery += 'WHERE ' } else { - userQuery += `OR `; + userQuery += 'OR ' } - userQuery += `id IN ('${includeIds.join("', '")}') `; + userQuery += `id IN ('${includeIds.join("', '")}') ` } - userQuery += `ORDER BY name,id`; + userQuery += 'ORDER BY name,id' return await new Promise((resolve, reject) => { db.readTransaction((transaction) => { @@ -173,15 +173,15 @@ export const getUsers: ( [], (_transaction, resultSet) => { if (resultSet.rows.length > 0) { - const items: object[] = getItems(resultSet); - const users: User[] = items.map((object) => databaseToEntity(object)); - resolve(users); + const items: object[] = getItems(resultSet) + const users: User[] = items.map((object) => databaseToEntity(object)) + resolve(users) } else { - resolve([]); + resolve([]) } }, - errorCallback(userQuery, reject), - ); - }); - }); -}; + errorCallback(userQuery, reject) + ) + }) + }) +} diff --git a/frontend/Functions/DatabaseFunctions/index.ts b/frontend/Functions/DatabaseFunctions/index.ts index 67ae216..f202846 100644 --- a/frontend/Functions/DatabaseFunctions/index.ts +++ b/frontend/Functions/DatabaseFunctions/index.ts @@ -1,42 +1,42 @@ -import SQLite, { ResultSet, SQLiteDatabase, Transaction } from 'react-native-sqlite-storage'; -import { errorCallback } from './Errors'; +import SQLite, { ResultSet, SQLiteDatabase, Transaction } from 'react-native-sqlite-storage' +import { errorCallback } from './Errors' export const initDatabase: () => SQLiteDatabase = () => { return SQLite.openDatabase( { name: 'nostros.db', location: 'default' }, () => {}, - () => {}, - ); -}; + () => {} + ) +} export const getItems: (resultSet: ResultSet) => object[] = (resultSet) => { - const result: object[] = []; + const result: object[] = [] for (let i = 0; i < resultSet.rows.length; i++) { - result.push(resultSet.rows.item(i)); + result.push(resultSet.rows.item(i)) } - return result; -}; + return result +} export const simpleExecute: (query: string, db: SQLiteDatabase) => Promise = async ( query, - db, + db ) => { return await db.transaction((transaction) => { - transaction.executeSql(query, [], () => {}, errorCallback(query)); - }); -}; + transaction.executeSql(query, [], () => {}, errorCallback(query)) + }) +} export const dropTables: (db: SQLiteDatabase) => Promise = async (db) => { const dropQueries = [ 'DROP TABLE IF EXISTS nostros_notes;', 'DROP TABLE IF EXISTS nostros_users;', - 'DROP TABLE IF EXISTS nostros_relays;', - ]; + 'DROP TABLE IF EXISTS nostros_relays;' + ] return await db.transaction((transaction) => { dropQueries.forEach((query) => { - transaction.executeSql(query, [], () => {}, errorCallback(query)); - }); - }); -}; + transaction.executeSql(query, [], () => {}, errorCallback(query)) + }) + }) +} diff --git a/frontend/Functions/RelayFunctions/Events/index.ts b/frontend/Functions/RelayFunctions/Events/index.ts index f8b6b77..6d364d0 100644 --- a/frontend/Functions/RelayFunctions/Events/index.ts +++ b/frontend/Functions/RelayFunctions/Events/index.ts @@ -1,48 +1,48 @@ -import { Event } from '../../../lib/nostr/Events'; +import { Event } from '../../../lib/nostr/Events' export const getMainEventId: (event: Event) => string | null = (event) => { - const eTags = getETags(event); + const eTags = getETags(event) let mainTag = eTags.find((tag) => { - return tag[3] === 'root'; - }); + return tag[3] === 'root' + }) if (!mainTag) { - mainTag = eTags[0]; + mainTag = eTags[0] } - return mainTag ? mainTag[1] : null; -}; + return mainTag ? mainTag[1] : null +} export const getReplyEventId: (event: Event) => string | null = (event) => { - const eTags = getETags(event); + const eTags = getETags(event) let mainTag = eTags.find((tag) => { - return tag[3] === 'reply'; - }); + return tag[3] === 'reply' + }) if (!mainTag) { - mainTag = eTags[eTags.length - 1]; + mainTag = eTags[eTags.length - 1] } - return mainTag ? mainTag[1] : null; -}; + return mainTag ? mainTag[1] : null +} export const getDirectReplies: (event: Event, replies: Event[]) => Event[] = (event, replies) => { - return replies.filter((item) => isDirectReply(event, item)); -}; + return replies.filter((item) => isDirectReply(event, item)) +} export const isDirectReply: (mainEvent: Event, reply: Event) => boolean = (mainEvent, reply) => { - const taggedMainEventsIds: string[] = getTaggedEventIds(mainEvent); - const taggedReplyEventsIds: string[] = getTaggedEventIds(reply); - const difference = taggedReplyEventsIds.filter((item) => !taggedMainEventsIds.includes(item)); + const taggedMainEventsIds: string[] = getTaggedEventIds(mainEvent) + const taggedReplyEventsIds: string[] = getTaggedEventIds(reply) + const difference = taggedReplyEventsIds.filter((item) => !taggedMainEventsIds.includes(item)) - return difference.length === 1 && difference[0] === mainEvent.id; -}; + return difference.length === 1 && difference[0] === mainEvent.id +} export const getTaggedEventIds: (event: Event) => string[] = (event) => { - const mainEventETags: string[][] = getETags(event); - return mainEventETags.map((item) => item[1] ?? ''); -}; + const mainEventETags: string[][] = getETags(event) + return mainEventETags.map((item) => item[1] ?? '') +} export const getETags: (event: Event) => string[][] = (event) => { - return event.tags.filter((tag) => tag[0] === 'e'); -}; + return event.tags.filter((tag) => tag[0] === 'e') +} diff --git a/frontend/Functions/RelayFunctions/Users/index.ts b/frontend/Functions/RelayFunctions/Users/index.ts index db4388e..8e175e8 100644 --- a/frontend/Functions/RelayFunctions/Users/index.ts +++ b/frontend/Functions/RelayFunctions/Users/index.ts @@ -1,28 +1,28 @@ -import moment from 'moment'; -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import RelayPool from '../../../lib/nostr/RelayPool/intex'; -import { getUser, getUsers, User } from '../../DatabaseFunctions/Users'; -import { Event } from '../../../lib/nostr/Events'; +import moment from 'moment' +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import RelayPool from '../../../lib/nostr/RelayPool/intex' +import { getUser, getUsers, User } from '../../DatabaseFunctions/Users' +import { Event } from '../../../lib/nostr/Events' export const usersToTags: (users: User[]) => string[][] = (users) => { return users.map((user): string[] => { - return ['p', user.id, user.main_relay ?? '', user.name ?? '']; - }); -}; + return ['p', user.id, user.main_relay ?? '', user.name ?? ''] + }) +} export const tagsToUsers: (tags: string[][]) => User[] = (tags) => { return tags.map((tag): User => { - return tagToUser(tag); - }); -}; + return tagToUser(tag) + }) +} export const tagToUser: (tag: string[]) => User = (tag) => { return { id: tag[1], main_relay: tag[2], - name: tag[3], - }; -}; + name: tag[3] + } +} export const populatePets: ( relayPool: RelayPool, @@ -36,12 +36,12 @@ export const populatePets: ( created_at: moment().unix(), kind: 3, pubkey: publicKey, - tags: usersToTags(results), - }; - relayPool?.sendEvent(event); + tags: usersToTags(results) + } + relayPool?.sendEvent(event) } - }); -}; + }) +} export const populateProfile: ( relayPool: RelayPool, @@ -54,16 +54,16 @@ export const populateProfile: ( name: result.name, main_relay: result.main_relay, picture: result.picture, - about: result.about, - }; + about: result.about + } const event: Event = { content: JSON.stringify(profile), created_at: moment().unix(), kind: 0, pubkey: publicKey, - tags: usersToTags([result]), - }; - relayPool?.sendEvent(event); + tags: usersToTags([result]) + } + relayPool?.sendEvent(event) } - }); -}; + }) +} diff --git a/frontend/Functions/RelayFunctions/index.ts b/frontend/Functions/RelayFunctions/index.ts index 897ca20..14dc0c6 100644 --- a/frontend/Functions/RelayFunctions/index.ts +++ b/frontend/Functions/RelayFunctions/index.ts @@ -1,12 +1,12 @@ -import { SQLiteDatabase } from 'react-native-sqlite-storage'; -import RelayPool from '../../lib/nostr/RelayPool/intex'; -import { populatePets, populateProfile } from './Users'; +import { SQLiteDatabase } from 'react-native-sqlite-storage' +import RelayPool from '../../lib/nostr/RelayPool/intex' +import { populatePets, populateProfile } from './Users' export const populateRelay: ( relayPool: RelayPool, database: SQLiteDatabase, publicKey: string, ) => void = (relayPool, database, publicKey) => { - populateProfile(relayPool, database, publicKey); - populatePets(relayPool, database, publicKey); -}; + populateProfile(relayPool, database, publicKey) + populatePets(relayPool, database, publicKey) +} diff --git a/frontend/i18n.config.ts b/frontend/i18n.config.ts index 8e05f59..500be18 100644 --- a/frontend/i18n.config.ts +++ b/frontend/i18n.config.ts @@ -1,19 +1,19 @@ -import i18n from 'i18next'; -import { initReactI18next } from 'react-i18next'; -import en from './Locales/en.json'; +import i18n from 'i18next' +import { initReactI18next } from 'react-i18next' +import en from './Locales/en.json' i18n.use(initReactI18next).init({ compatibilityJSON: 'v3', fallbackLng: 'en', resources: { - en, + en }, ns: ['common'], defaultNS: 'common', debug: true, interpolation: { - escapeValue: false, - }, -}); + escapeValue: false + } +}) -export default i18n; +export default i18n diff --git a/frontend/index.tsx b/frontend/index.tsx index c28bca2..a1176a6 100644 --- a/frontend/index.tsx +++ b/frontend/index.tsx @@ -1,21 +1,21 @@ -import React from 'react'; -import { ApplicationProvider, IconRegistry } from '@ui-kitten/components'; -import theme from './theme.json'; -import { EvaIconsPack } from '@ui-kitten/eva-icons'; -import * as eva from '@eva-design/eva'; -import { AppContextProvider } from './Contexts/AppContext'; -import MainLayout from './Components/MainLayout'; -import { RelayPoolContextProvider } from './Contexts/RelayPoolContext'; -import { I18nextProvider } from 'react-i18next'; -import i18n from './i18n.config'; +import React from 'react' +import { ApplicationProvider, IconRegistry } from '@ui-kitten/components' +import theme from './theme.json' +import { EvaIconsPack } from '@ui-kitten/eva-icons' +import * as eva from '@eva-design/eva' +import { AppContextProvider } from './Contexts/AppContext' +import MainLayout from './Components/MainLayout' +import { RelayPoolContextProvider } from './Contexts/RelayPoolContext' +import { I18nextProvider } from 'react-i18next' +import i18n from './i18n.config' export const Frontend: React.FC = () => { const mapping = { strict: { - 'text-font-family': 'OpenSans-Regular', + 'text-font-family': 'OpenSans-Regular' }, - components: {}, - }; + components: {} + } return ( <> @@ -30,7 +30,7 @@ export const Frontend: React.FC = () => { - ); -}; + ) +} -export default Frontend; +export default Frontend diff --git a/frontend/lib/nostr/Bip/index.ts b/frontend/lib/nostr/Bip/index.ts index ef67d3a..52e2e3b 100644 --- a/frontend/lib/nostr/Bip/index.ts +++ b/frontend/lib/nostr/Bip/index.ts @@ -1,12 +1,12 @@ -import ecurve from 'ecurve'; -import BigInteger from 'bigi'; +import ecurve from 'ecurve' +import BigInteger from 'bigi' export const getPublickey: (privateKey: string) => string = (privateKey) => { - const privateKeyBuffer = Buffer.from(privateKey, 'hex'); - const ecparams = ecurve.getCurveByName('secp256k1'); + const privateKeyBuffer = Buffer.from(privateKey, 'hex') + const ecparams = ecurve.getCurveByName('secp256k1') - const curvePt = ecparams.G.multiply(BigInteger.fromBuffer(privateKeyBuffer)); - const publicKey = curvePt.getEncoded(true); + const curvePt = ecparams.G.multiply(BigInteger.fromBuffer(privateKeyBuffer)) + const publicKey = curvePt.getEncoded(true) - return publicKey.toString('hex').slice(2); -}; + return publicKey.toString('hex').slice(2) +} diff --git a/frontend/lib/nostr/Events/index.ts b/frontend/lib/nostr/Events/index.ts index 8856f22..0f016cc 100644 --- a/frontend/lib/nostr/Events/index.ts +++ b/frontend/lib/nostr/Events/index.ts @@ -1,13 +1,13 @@ -import schnorr from 'bip-schnorr'; +import schnorr from 'bip-schnorr' export interface Event { - content: string; - created_at: number; - id?: string; - kind: 0 | 1 | 2 | 3; - pubkey: string; - sig?: string; - tags: string[][]; + content: string + created_at: number + id?: string + kind: 0 | 1 | 2 | 3 + pubkey: string + sig?: string + tags: string[][] } export enum EventKind { @@ -18,56 +18,56 @@ export enum EventKind { } export const serializeEvent: (event: Event) => string = (event) => { - return JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]); -}; + return JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]) +} export const getEventHash: (event: Event) => Buffer = (event) => { - return schnorr.convert.hash(Buffer.from(serializeEvent(event))); -}; + return schnorr.convert.hash(Buffer.from(serializeEvent(event))) +} export const validateEvent: (event: Event) => boolean = (event) => { - if (event.id !== getEventHash(event).toString('hex')) return false; - if (typeof event.content !== 'string') return false; - if (typeof event.created_at !== 'number') return false; + if (event.id !== getEventHash(event).toString('hex')) return false + if (typeof event.content !== 'string') return false + if (typeof event.created_at !== 'number') return false - if (!Array.isArray(event.tags)) return false; + if (!Array.isArray(event.tags)) return false for (let i = 0; i < event.tags.length; i++) { - const tag = event.tags[i]; - if (!Array.isArray(tag)) return false; + const tag = event.tags[i] + if (!Array.isArray(tag)) return false for (let j = 0; j < tag.length; j++) { - if (typeof tag[j] === 'object') return false; + if (typeof tag[j] === 'object') return false } } - return true; -}; + return true +} export const verifySignature: (event: Event) => Promise = async (event) => { try { schnorr.verify( Buffer.from(event.pubkey, 'hex'), Buffer.from(event?.id, 'hex'), - Buffer.from(event?.sig, 'hex'), - ); - return true; + Buffer.from(event?.sig, 'hex') + ) + return true } catch (_e) { - console.error(`The signature verification failed`); - return false; + console.error('The signature verification failed') + return false } -}; +} export const signEvent: (event: Event, privateKey: string) => Promise = async ( event, - privateKey, + privateKey ) => { - const hash = getEventHash(event); + const hash = getEventHash(event) const signature: string = Buffer.from(await schnorr.sign(privateKey, hash), 'hex').toString( - 'hex', - ); + 'hex' + ) - event.id = hash.toString('hex'); - event.sig = signature; + event.id = hash.toString('hex') + event.sig = signature - return event; -}; + return event +} diff --git a/frontend/lib/nostr/Relay/index.ts b/frontend/lib/nostr/Relay/index.ts index 29ac4ff..0936c61 100644 --- a/frontend/lib/nostr/Relay/index.ts +++ b/frontend/lib/nostr/Relay/index.ts @@ -1,141 +1,141 @@ -import { Event } from '../Events'; -import { v5 as uuidv5 } from 'uuid'; +import { Event } from '../Events' +import { v5 as uuidv5 } from 'uuid' export interface RelayFilters { - ids?: string[]; - authors?: string[]; - kinds?: number[]; - '#e'?: string[]; - '#p'?: string[]; - since?: number; - limit?: number; - until?: number; + ids?: string[] + authors?: string[] + kinds?: number[] + '#e'?: string[] + '#p'?: string[] + since?: number + limit?: number + until?: number } export interface RelayMessage { - data: string; + data: string } export interface RelayOptions { - reconnect?: boolean; + reconnect?: boolean } class Relay { - constructor(relayUrl: string, options: RelayOptions = { reconnect: true }) { - this.url = relayUrl; - this.options = options; - this.manualClose = false; - this.socket = new WebSocket(this.url); - this.subscriptions = {}; + constructor (relayUrl: string, options: RelayOptions = { reconnect: true }) { + this.url = relayUrl + this.options = options + this.manualClose = false + this.socket = new WebSocket(this.url) + this.subscriptions = {} - this.onOpen = () => {}; - this.onEvent = () => {}; - this.onEsoe = () => {}; - this.onNotice = () => {}; + this.onOpen = () => {} + this.onEvent = () => {} + this.onEsoe = () => {} + this.onNotice = () => {} - this.initWebsocket(); + this.initWebsocket() } - private readonly options: RelayOptions; - private socket: WebSocket; - private manualClose: boolean; - private subscriptions: { [subId: string]: string[] }; + private readonly options: RelayOptions + private socket: WebSocket + private manualClose: boolean + private subscriptions: { [subId: string]: string[] } private readonly initWebsocket: () => void = () => { this.socket.onmessage = (message) => { - this.handleNostrMessage(message as RelayMessage); - }; - this.socket.onclose = this.onClose; - this.socket.onerror = this.onError; - this.socket.onopen = () => this.onOpen(this); - }; + this.handleNostrMessage(message as RelayMessage) + } + this.socket.onclose = this.onClose + this.socket.onerror = this.onError + this.socket.onopen = () => this.onOpen(this) + } private readonly onClose: () => void = () => { - if (!this.manualClose && this.options.reconnect) this.initWebsocket(); - }; + if (!this.manualClose && this.options.reconnect) this.initWebsocket() + } private readonly onError: () => void = () => { - if (this.options.reconnect) this.initWebsocket(); - }; + if (this.options.reconnect) this.initWebsocket() + } private readonly handleNostrMessage: (message: RelayMessage) => void = (message) => { - const data: any[] = JSON.parse(message.data); + const data: any[] = JSON.parse(message.data) if (data.length >= 2) { - const id: string = data[1]; + const id: string = data[1] if (data[0] === 'EVENT') { - if (data.length < 3) return; - const message: Event = data[2]; - return this.onEvent(this, id, message); + if (data.length < 3) return + const message: Event = data[2] + return this.onEvent(this, id, message) } else if (data[0] === 'EOSE') { - return this.onEsoe(this, id); + return this.onEsoe(this, id) } else if (data[0] === 'NOTICE') { - return this.onNotice(this, [...data.slice(1)]); + return this.onNotice(this, [...data.slice(1)]) } } - }; + } private readonly send: (message: object) => void = async (message) => { - const tosend = JSON.stringify(message); + const tosend = JSON.stringify(message) if (this.socket.readyState !== WebSocket.OPEN) { setTimeout(() => { - this.send(message); - }, 500); + this.send(message) + }, 500) } else { try { - console.log('SEND =====>', tosend); - this.socket.send(tosend); + console.log('SEND =====>', tosend) + this.socket.send(tosend) } catch (e) { - console.log('Failed ot send Event', e); + console.log('Failed ot send Event', e) } } - }; + } - public url: string; - public onOpen: (relay: Relay) => void; - public onEvent: (relay: Relay, subId: string, event: Event) => void; - public onEsoe: (relay: Relay, subId: string) => void; - public onNotice: (relay: Relay, events: Event[]) => void; + public url: string + public onOpen: (relay: Relay) => void + public onEvent: (relay: Relay, subId: string, event: Event) => void + public onEsoe: (relay: Relay, subId: string) => void + public onNotice: (relay: Relay, events: Event[]) => void public readonly close: () => void = () => { if (this.socket) { - this.manualClose = true; - this.socket.close(); + this.manualClose = true + this.socket.close() } - }; + } public readonly sendEvent: (event: Event) => void = (event) => { - this.send(['EVENT', event]); - }; + this.send(['EVENT', event]) + } public readonly subscribe: (subId: string, filters?: RelayFilters) => void = ( subId, - filters = {}, + filters = {} ) => { const uuid = uuidv5( `${subId}${JSON.stringify(filters)}`, - '57003344-b2cb-4b6f-a579-fae9e82c370a', - ); + '57003344-b2cb-4b6f-a579-fae9e82c370a' + ) if (this.subscriptions[subId]?.includes(uuid)) { - console.log('Subscription already done!'); + console.log('Subscription already done!') } else { - this.send(['REQ', subId, filters]); - const newSubscriptions = [...(this.subscriptions[subId] ?? []), uuid]; - this.subscriptions[subId] = newSubscriptions; + this.send(['REQ', subId, filters]) + const newSubscriptions = [...(this.subscriptions[subId] ?? []), uuid] + this.subscriptions[subId] = newSubscriptions } - }; + } public readonly unsubscribe: (subId: string) => void = (subId) => { - this.send(['CLOSE', subId]); - delete this.subscriptions[subId]; - }; + this.send(['CLOSE', subId]) + delete this.subscriptions[subId] + } public readonly unsubscribeAll: () => void = () => { Object.keys(this.subscriptions).forEach((subId: string) => { - this.send(['CLOSE', subId]); - }); - this.subscriptions = {}; - }; + this.send(['CLOSE', subId]) + }) + this.subscriptions = {} + } } -export default Relay; +export default Relay diff --git a/frontend/lib/nostr/RelayPool/intex.ts b/frontend/lib/nostr/RelayPool/intex.ts index 625977f..aae50bc 100644 --- a/frontend/lib/nostr/RelayPool/intex.ts +++ b/frontend/lib/nostr/RelayPool/intex.ts @@ -1,140 +1,140 @@ -import { signEvent, validateEvent, Event } from '../Events'; -import Relay, { RelayFilters, RelayOptions } from '../Relay'; +import { signEvent, validateEvent, Event } from '../Events' +import Relay, { RelayFilters, RelayOptions } from '../Relay' export interface OnFunctions { - open: { [id: string]: (relay: Relay) => void }; - event: { [id: string]: (relay: Relay, subId: string, event: Event) => void }; - esoe: { [id: string]: (relay: Relay, subId: string) => void }; - notice: { [id: string]: (relay: Relay, events: Event[]) => void }; + open: { [id: string]: (relay: Relay) => void } + event: { [id: string]: (relay: Relay, subId: string, event: Event) => void } + esoe: { [id: string]: (relay: Relay, subId: string) => void } + notice: { [id: string]: (relay: Relay, events: Event[]) => void } } class RelayPool { - constructor(relaysUrls: string[], privateKey: string, options: RelayOptions = {}) { - this.relays = {}; - this.privateKey = privateKey; - this.options = options; + constructor (relaysUrls: string[], privateKey: string, options: RelayOptions = {}) { + this.relays = {} + this.privateKey = privateKey + this.options = options this.onFunctions = { open: {}, event: {}, esoe: {}, - notice: {}, - }; + notice: {} + } relaysUrls.forEach((relayUrl) => { - this.add(relayUrl); - }); + this.add(relayUrl) + }) - this.setupHandlers(); + this.setupHandlers() } - private readonly privateKey: string; - private readonly options: RelayOptions; - private readonly onFunctions: OnFunctions; - public relays: { [url: string]: Relay }; + private readonly privateKey: string + private readonly options: RelayOptions + private readonly onFunctions: OnFunctions + public relays: { [url: string]: Relay } private readonly setupHandlers: () => void = () => { Object.keys(this.relays).forEach((relayUrl: string) => { - const relay: Relay = this.relays[relayUrl]; + const relay: Relay = this.relays[relayUrl] relay.onOpen = (openRelay) => { - Object.keys(this.onFunctions.open).forEach((id) => this.onFunctions.open[id](openRelay)); - }; + Object.keys(this.onFunctions.open).forEach((id) => this.onFunctions.open[id](openRelay)) + } relay.onEvent = (eventRelay, subId, event) => { Object.keys(this.onFunctions.event).forEach((id) => - this.onFunctions.event[id](eventRelay, subId, event), - ); - }; + this.onFunctions.event[id](eventRelay, subId, event) + ) + } relay.onEsoe = (eventRelay, subId) => { Object.keys(this.onFunctions.esoe).forEach((id) => - this.onFunctions.esoe[id](eventRelay, subId), - ); - }; + this.onFunctions.esoe[id](eventRelay, subId) + ) + } relay.onNotice = (eventRelay, events) => { Object.keys(this.onFunctions.notice).forEach((id) => - this.onFunctions.notice[id](eventRelay, events), - ); - }; - }); - }; + this.onFunctions.notice[id](eventRelay, events) + ) + } + }) + } public on: ( method: 'open' | 'event' | 'esoe' | 'notice', id: string, fn: (relay: Relay, subId?: string, event?: Event) => void, ) => void = (method, id, fn) => { - this.onFunctions[method][id] = fn; - }; + this.onFunctions[method][id] = fn + } public removeOn: (method: 'open' | 'event' | 'esoe' | 'notice', id: string) => void = ( method, - id, + id ) => { - delete this.onFunctions[method][id]; - }; + delete this.onFunctions[method][id] + } public readonly add: (relayUrl: string) => boolean = (relayUrl) => { - if (this.relays[relayUrl]) return false; + if (this.relays[relayUrl]) return false - this.relays[relayUrl] = new Relay(relayUrl, this.options); - this.setupHandlers(); + this.relays[relayUrl] = new Relay(relayUrl, this.options) + this.setupHandlers() - return true; - }; + return true + } public readonly close: () => void = () => { Object.keys(this.relays).forEach((relayUrl: string) => { - const relay: Relay = this.relays[relayUrl]; - relay.close(); - }); - }; + const relay: Relay = this.relays[relayUrl] + relay.close() + }) + } public readonly remove: (relayUrl: string) => boolean = (relayUrl) => { - const relay: Relay | undefined = this.relays[relayUrl]; + const relay: Relay | undefined = this.relays[relayUrl] if (relay) { - relay.close(); - delete this.relays[relayUrl]; - return true; + relay.close() + delete this.relays[relayUrl] + return true } - return false; - }; + return false + } public readonly sendEvent: (event: Event) => Promise = async (event) => { - const signedEvent: Event = await signEvent(event, this.privateKey); + const signedEvent: Event = await signEvent(event, this.privateKey) if (validateEvent(signedEvent)) { Object.keys(this.relays).forEach((relayUrl: string) => { - const relay: Relay = this.relays[relayUrl]; - relay.sendEvent(signedEvent); - }); + const relay: Relay = this.relays[relayUrl] + relay.sendEvent(signedEvent) + }) - return signedEvent; + return signedEvent } else { - console.log('Not valid event', event); + console.log('Not valid event', event) } - return null; - }; + return null + } public readonly subscribe: (subId: string, filters?: RelayFilters) => void = (subId, filters) => { Object.keys(this.relays).forEach((relayUrl: string) => { - this.relays[relayUrl].subscribe(subId, filters); - }); - }; + this.relays[relayUrl].subscribe(subId, filters) + }) + } public readonly unsubscribe: (subId: string) => void = (subId) => { Object.keys(this.relays).forEach((relayUrl: string) => { - this.relays[relayUrl].unsubscribe(subId); - }); - }; + this.relays[relayUrl].unsubscribe(subId) + }) + } public readonly unsubscribeAll: () => void = () => { Object.keys(this.relays).forEach((relayUrl: string) => { - this.relays[relayUrl].unsubscribeAll(); - }); - }; + this.relays[relayUrl].unsubscribeAll() + }) + } } -export default RelayPool; +export default RelayPool