Connection to contacts relays

This commit is contained in:
KoalaSat 2023-03-10 19:27:20 +01:00
parent 4466d7d80f
commit 42022a5c82
No known key found for this signature in database
GPG Key ID: 2F7F61C6146AB157
78 changed files with 681 additions and 364 deletions

View File

@ -27,6 +27,7 @@ module.exports = {
'@typescript-eslint/no-misused-promises': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/restrict-plus-operands': 'off',
'@typescript-eslint/key-spacing': 'off',
},
settings: {
'import/resolver': {

View File

@ -261,8 +261,6 @@ dependencies {
implementation 'com.facebook.fresco:webpsupport:2.6.0'
implementation 'com.facebook.fresco:animated-gif:2.6.0'
// implementation 'com.github.Samourai-Wallet:ExtLibJ:0.0.11'
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'

View File

@ -269,7 +269,7 @@ public class Database {
String relayUrl = cursor.getString(0);
int active = cursor.getInt(1);
int globalFeed = cursor.getInt(2);
Relay relay = new Relay(relayUrl, active, globalFeed,this, reactContext);
Relay relay = new Relay(relayUrl, active, globalFeed,0, this, reactContext);
relayList.add(relay);
} catch (IOException e) {
Log.d("WebSocket", e.toString());

View File

@ -10,8 +10,6 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
//import com.samourai.wallet.segwit.bech32.Bech32;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

View File

@ -16,12 +16,14 @@ public class Relay {
public int active;
public int globalFeed;
public int paid;
public int resilient;
public Relay(String serverUrl, int isActive, int showGlobalFeed, Database database, ReactApplicationContext reactContext) throws IOException {
public Relay(String serverUrl, int isActive, int showGlobalFeed, int isResilient, Database database, ReactApplicationContext reactContext) throws IOException {
webSocket = new Websocket(serverUrl, database, reactContext);
url = serverUrl;
active = isActive;
globalFeed = showGlobalFeed;
resilient = isResilient;
}
public int active() {
@ -58,6 +60,7 @@ public class Relay {
values.put("paid", paid);
values.put("active", active);
values.put("global_feed", globalFeed);
values.put("resilient", resilient);
values.put("deleted_at", 0);
database.instance.replace("nostros_relays", null, values);
}

View File

@ -31,16 +31,9 @@ public class RelayPoolModule extends ReactContextBaseJavaModule {
return "RelayPoolModule";
}
@ReactMethod
public void add(String url, Callback callback) {
add(url);
callback.invoke();
}
@ReactMethod
public void add(String url) {
private void add(String url, int resilient, int showGlobalFeed) {
try {
Relay relay = new Relay(url, 1, 1, database, context);
Relay relay = new Relay(url, 1, showGlobalFeed, resilient, database, context);
relay.connect(userPubKey);
relays.add(relay);
database.saveRelay(relay);
@ -49,6 +42,12 @@ public class RelayPoolModule extends ReactContextBaseJavaModule {
}
}
@ReactMethod
public void add(String url, int resilient, int showGlobalFeed, Callback callback) {
this.add(url, resilient, showGlobalFeed);
callback.invoke();
}
@ReactMethod
public void remove(String url, Callback callback) {
ListIterator<Relay> iterator = relays.listIterator();
@ -94,7 +93,7 @@ public class RelayPoolModule extends ReactContextBaseJavaModule {
}
if (!relayExists) {
this.add(url);
this.add(url, 0, 1);
}
callback.invoke();

View File

@ -11,7 +11,7 @@ import {
TouchableRipple,
useTheme,
} from 'react-native-paper'
import { getGroup, Group } from '../../Functions/DatabaseFunctions/Groups'
import { getGroup, type Group } from '../../Functions/DatabaseFunctions/Groups'
import { AppContext } from '../../Contexts/AppContext'
import { validImageUrl } from '../../Functions/NativeFunctions'
import FastImage from 'react-native-fast-image'
@ -22,9 +22,9 @@ import { UserContext } from '../../Contexts/UserContext'
import { getUnixTime } from 'date-fns'
import { Kind } from 'nostr-tools'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { goBack } from '../../lib/Navigation'
import { getUser, User } from '../../Functions/DatabaseFunctions/Users'
import { getUser, type User } from '../../Functions/DatabaseFunctions/Users'
import ProfileData from '../ProfileData'
import GroupShare from '../GroupShare'
import DatabaseModule from '../../lib/Native/DatabaseModule'

View File

@ -4,10 +4,10 @@ import { Dimensions, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import { IconButton, Snackbar, Text, TouchableRipple } from 'react-native-paper'
import Share from 'react-native-share'
import RBSheet from 'react-native-raw-bottom-sheet'
import type RBSheet from 'react-native-raw-bottom-sheet'
import { getNevent } from '../../lib/nostr/Nip19'
import QRCode from 'react-native-qrcode-svg'
import { Group } from '../../Functions/DatabaseFunctions/Groups'
import { type Group } from '../../Functions/DatabaseFunctions/Groups'
import { getNoteRelays } from '../../Functions/DatabaseFunctions/Notes'
import { AppContext } from '../../Contexts/AppContext'

View File

@ -1,15 +1,15 @@
import React, { useEffect, useMemo, useState } from 'react'
import { User } from '../../Functions/DatabaseFunctions/Users'
import { ListRenderItem, StyleSheet, View } from 'react-native'
import { type User } from '../../Functions/DatabaseFunctions/Users'
import { type ListRenderItem, StyleSheet, View } from 'react-native'
import { useTranslation } from 'react-i18next'
import RBSheet from 'react-native-raw-bottom-sheet'
import { Button, Divider, Text, TextInput, TouchableRipple, useTheme } from 'react-native-paper'
import { AppContext } from '../../Contexts/AppContext'
import LnPreview from '../LnPreview'
import { Note } from '../../Functions/DatabaseFunctions/Notes'
import { type Note } from '../../Functions/DatabaseFunctions/Notes'
import { getNpub } from '../../lib/nostr/Nip19'
import { formatPubKey } from '../../Functions/RelayFunctions/Users'
import { getZaps, Zap } from '../../Functions/DatabaseFunctions/Zaps'
import { getZaps, type Zap } from '../../Functions/DatabaseFunctions/Zaps'
import { FlatList, ScrollView } from 'react-native-gesture-handler'
import ProfileData from '../ProfileData'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import RBSheet from 'react-native-raw-bottom-sheet'
import { Card, IconButton, Text, useTheme } from 'react-native-paper'
import { AppContext } from '../../Contexts/AppContext'
import { decode, PaymentRequestObject, TagsObject } from 'bolt11'
import { decode, type PaymentRequestObject, type TagsObject } from 'bolt11'
interface LnPreviewProps {
setOpen?: (open: boolean) => void

View File

@ -4,7 +4,12 @@ import { FlatList, StyleSheet, TouchableNativeFeedback, View } from 'react-nativ
import { Divider, IconButton, List, Text, useTheme } from 'react-native-paper'
import { AppContext } from '../../Contexts/AppContext'
import RBSheet from 'react-native-raw-bottom-sheet'
import { getNoteRelays, getNotes, Note, NoteRelay } from '../../Functions/DatabaseFunctions/Notes'
import {
getNoteRelays,
getNotes,
type Note,
type NoteRelay,
} from '../../Functions/DatabaseFunctions/Notes'
import Clipboard from '@react-native-clipboard/clipboard'
import NoteShare from '../NoteShare'
import { navigate } from '../../lib/Navigation'

View File

@ -5,17 +5,17 @@ import {
getRepliesCount,
getRepostCount,
isUserReposted,
Note,
NoteRelay,
type Note,
type NoteRelay,
} from '../../Functions/DatabaseFunctions/Notes'
import { StyleSheet, View } from 'react-native'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { AppContext } from '../../Contexts/AppContext'
import { t } from 'i18next'
import { isContentWarning } from '../../Functions/RelayFunctions/Events'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { getUnixTime } from 'date-fns'
import { Relay, searchRelays } from '../../Functions/DatabaseFunctions/Relays'
import { type Relay, searchRelays } from '../../Functions/DatabaseFunctions/Relays'
import TextContent from '../../Components/TextContent'
import { formatPubKey } from '../../Functions/RelayFunctions/Users'
import { getReactions } from '../../Functions/DatabaseFunctions/Reactions'

View File

@ -4,11 +4,11 @@ import { Dimensions, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import { IconButton, Snackbar, Text, TouchableRipple } from 'react-native-paper'
import Share from 'react-native-share'
import RBSheet from 'react-native-raw-bottom-sheet'
import type RBSheet from 'react-native-raw-bottom-sheet'
import QRCode from 'react-native-qrcode-svg'
import { useContext } from 'react'
import { AppContext } from '../../Contexts/AppContext'
import { getNoteRelays, Note } from '../../Functions/DatabaseFunctions/Notes'
import { getNoteRelays, type Note } from '../../Functions/DatabaseFunctions/Notes'
import { getNevent } from '../../lib/nostr/Nip19'
interface NoteShareProps {

View File

@ -1,19 +1,19 @@
import { t } from 'i18next'
import * as React from 'react'
import { StyleSheet, View, ListRenderItem, Switch, FlatList } from 'react-native'
import { StyleSheet, View, type ListRenderItem, Switch, FlatList } from 'react-native'
import { Button, IconButton, List, Snackbar, Text, useTheme } from 'react-native-paper'
import { AppContext } from '../../Contexts/AppContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { UserContext } from '../../Contexts/UserContext'
import { getUser, User } from '../../Functions/DatabaseFunctions/Users'
import { getUser, type User } from '../../Functions/DatabaseFunctions/Users'
import { populatePets, username } from '../../Functions/RelayFunctions/Users'
import LnPayment from '../LnPayment'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { navigate } from '../../lib/Navigation'
import RBSheet from 'react-native-raw-bottom-sheet'
import { getUserRelays, NoteRelay } from '../../Functions/DatabaseFunctions/NotesRelays'
import { getUserRelays, type NoteRelay } from '../../Functions/DatabaseFunctions/NotesRelays'
import { relayToColor } from '../../Functions/NativeFunctions'
import { Relay } from '../../Functions/DatabaseFunctions/Relays'
import { type Relay } from '../../Functions/DatabaseFunctions/Relays'
import ProfileShare from '../ProfileShare'
import { ScrollView } from 'react-native-gesture-handler'
import { Kind } from 'nostr-tools'

View File

@ -3,12 +3,12 @@ import * as React from 'react'
import { StyleSheet, View } from 'react-native'
import { Divider, Snackbar, TouchableRipple, useTheme } from 'react-native-paper'
import { AppContext } from '../../Contexts/AppContext'
import { getUser, User } from '../../Functions/DatabaseFunctions/Users'
import { getUser, type User } from '../../Functions/DatabaseFunctions/Users'
import { usernamePubKey } from '../../Functions/RelayFunctions/Users'
import LnPayment from '../LnPayment'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { push } from '../../lib/Navigation'
import RBSheet from 'react-native-raw-bottom-sheet'
import type RBSheet from 'react-native-raw-bottom-sheet'
import { getNpub } from '../../lib/nostr/Nip19'
import ProfileData from '../ProfileData'
import ProfileActions from '../ProfileActions'

View File

@ -3,9 +3,9 @@ import * as React from 'react'
import { Dimensions, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import { IconButton, Snackbar, Text, TouchableRipple } from 'react-native-paper'
import { User } from '../../Functions/DatabaseFunctions/Users'
import { type User } from '../../Functions/DatabaseFunctions/Users'
import Share from 'react-native-share'
import RBSheet from 'react-native-raw-bottom-sheet'
import type RBSheet from 'react-native-raw-bottom-sheet'
import { getNprofile } from '../../lib/nostr/Nip19'
import QRCode from 'react-native-qrcode-svg'
import { useContext } from 'react'

View File

@ -14,7 +14,7 @@ import {
import { AppContext } from '../../Contexts/AppContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import RBSheet from 'react-native-raw-bottom-sheet'
import { getRelay, Relay, RelayInfo } from '../../Functions/DatabaseFunctions/Relays'
import { getRelay, type Relay, type RelayInfo } from '../../Functions/DatabaseFunctions/Relays'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { relayToColor } from '../../Functions/NativeFunctions'
import axios from 'axios'
@ -26,7 +26,7 @@ import { UserContext } from '../../Contexts/UserContext'
import { getRawUserReactions } from '../../Functions/DatabaseFunctions/Reactions'
import { getRawUserConversation } from '../../Functions/DatabaseFunctions/DirectMessages'
import { getUsers } from '../../Functions/DatabaseFunctions/Users'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { getUnixTime } from 'date-fns'
import { Kind } from 'nostr-tools'
import { usersToTags } from '../../Functions/RelayFunctions/Users'

View File

@ -4,7 +4,7 @@ import { Avatar, Card, Text, useTheme } from 'react-native-paper'
import FastImage from 'react-native-fast-image'
import { useTranslation } from 'react-i18next'
import LnPreview from '../../LnPreview'
import { decode, PaymentRequestObject, TagsObject } from 'bolt11'
import { decode, type PaymentRequestObject, type TagsObject } from 'bolt11'
import { AppContext } from '../../../Contexts/AppContext'
import { navigate } from '../../../lib/Navigation'

View File

@ -1,9 +1,9 @@
import React, { useContext, useEffect, useState } from 'react'
import ParsedText from 'react-native-parsed-text'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { Clipboard, Linking, StyleSheet, View } from 'react-native'
import { AppContext } from '../../Contexts/AppContext'
import { getUser, User } from '../../Functions/DatabaseFunctions/Users'
import { getUser, type User } from '../../Functions/DatabaseFunctions/Users'
import { formatPubKey } from '../../Functions/RelayFunctions/Users'
import getUnixTime from 'date-fns/getUnixTime'
import { useTheme } from 'react-native-paper'

View File

@ -3,7 +3,7 @@ import { StyleSheet, View } from 'react-native'
import { Button, Card, Snackbar, Text, useTheme } from 'react-native-paper'
import { useTranslation } from 'react-i18next'
import RBSheet from 'react-native-raw-bottom-sheet'
import { Asset, launchImageLibrary } from 'react-native-image-picker'
import { type Asset, launchImageLibrary } from 'react-native-image-picker'
import { imageHostingServices } from '../../Constants/Services'
import { AppContext } from '../../Contexts/AppContext'

View File

@ -1,10 +1,10 @@
import React, { useEffect, useRef, useState } from 'react'
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { initDatabase } from '../Functions/DatabaseFunctions'
import SInfo from 'react-native-sensitive-info'
import { AppState, Linking, NativeModules, Platform, StyleSheet } from 'react-native'
import { Text } from 'react-native-paper'
import { Config } from '../Pages/ConfigPage'
import { type Config } from '../Pages/ConfigPage'
import { imageHostingServices } from '../Constants/Services'
import { randomInt, validNip21 } from '../Functions/NativeFunctions'
import Clipboard from '@react-native-clipboard/clipboard'

View File

@ -3,10 +3,10 @@ import RelayPool, { fallbackRelays } from '../lib/nostr/RelayPool/intex'
import { AppContext } from './AppContext'
import { DeviceEventEmitter } from 'react-native'
import debounce from 'lodash.debounce'
import { getActiveRelays, getRelays, Relay } from '../Functions/DatabaseFunctions/Relays'
import { getActiveRelays, getRelays, type Relay } from '../Functions/DatabaseFunctions/Relays'
import { UserContext } from './UserContext'
import { getUnixTime } from 'date-fns'
import { Event } from '../lib/nostr/Events'
import { type Event } from '../lib/nostr/Events'
import { randomInt } from '../Functions/NativeFunctions'
export interface RelayPoolContextProps {
@ -99,7 +99,6 @@ export const RelayPoolContextProvider = ({
if (database && publicKey) {
const initRelayPool = new RelayPool(privateKey)
initRelayPool.connect(publicKey, () => {
initRelayPool.resilientMode(database, publicKey)
setRelayPool(initRelayPool)
})
}
@ -143,11 +142,11 @@ export const RelayPoolContextProvider = ({
})
}
const addRelayItem: (relay: Relay) => Promise<void> = async (relay, send = true) => {
const addRelayItem: (relay: Relay) => Promise<void> = async (relay) => {
setRelays((prev) => [...prev, relay])
return await new Promise((resolve, _reject) => {
if (relayPool && database && publicKey) {
relayPool.add(relay.url, () => {
relayPool.add(relay.url, relay.resilient ?? 0, relay.global_feed ?? 1, () => {
loadRelays().then(() => {
resolve()
})

View File

@ -1,6 +1,6 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
import { type Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
export interface DirectMessage extends Event {
conversation_id: string

View File

@ -1,6 +1,6 @@
import { QueryResult, QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QueryResult, type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Event } from '../../../lib/nostr/Events'
import { type Event } from '../../../lib/nostr/Events'
export interface Group extends Event {
name?: string

View File

@ -1,6 +1,6 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
import { type Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
export interface List extends Event {}

View File

@ -1,6 +1,6 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
import { type Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
export interface Note extends Event {
name: string

View File

@ -1,6 +1,6 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Relay } from '../Relays'
import { type Relay } from '../Relays'
export interface NoteRelay extends Relay {
relay_url: string

View File

@ -1,6 +1,6 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
import { type Event, evetDatabaseToEntity } from '../../../lib/nostr/Events'
export interface Reaction extends Event {
positive: boolean

View File

@ -1,6 +1,6 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Event } from '../../../lib/nostr/Events'
import { type Event } from '../../../lib/nostr/Events'
export interface RelayMetadata extends Event {
url: string
@ -26,7 +26,7 @@ export const getRawRelayMetadata: (
publicKey: string,
) => Promise<Event[]> = async (db, publicKey) => {
const notesQuery = 'SELECT * FROM nostros_relay_metadata WHERE pubkey = ?;'
const resultSet = await db.execute(notesQuery, [publicKey])
const resultSet = db.execute(notesQuery, [publicKey])
const items: object[] = getItems(resultSet)
const relays: Event[] = items.map((object) => databaseToRawEntity(object))
return relays
@ -38,8 +38,18 @@ export const getRelayMetadata: (
) => Promise<RelayMetadata> = async (db, publicKey) => {
const notesQuery =
'SELECT * FROM nostros_relay_metadata WHERE pubkey = ? ORDER BY created_at DESC;'
const resultSet = await db.execute(notesQuery, [publicKey])
const resultSet = db.execute(notesQuery, [publicKey])
const items: object[] = getItems(resultSet)
const relays: RelayMetadata[] = items.map((object) => databaseToEntity(object))
return relays[0]
}
export const getAllRelayMetadata: (db: QuickSQLiteConnection) => Promise<RelayMetadata[]> = async (
db,
) => {
const notesQuery = 'SELECT * FROM nostros_relay_metadata;'
const resultSet = db.execute(notesQuery)
const items: object[] = getItems(resultSet)
const relays: RelayMetadata[] = items.map((object) => databaseToEntity(object))
return relays
}

View File

@ -1,4 +1,4 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { median } from '../../NativeFunctions'
import { getNoteRelaysUsage } from '../NotesRelays'
@ -33,7 +33,7 @@ export const searchRelays: (
db: QuickSQLiteConnection,
) => Promise<Relay[]> = async (relayUrl, db) => {
const searchQuery = `SELECT * FROM nostros_relays WHERE url = '${relayUrl}' AND deleted_at = 0;`
const results = await db.execute(searchQuery)
const results = db.execute(searchQuery)
const items: object[] = getItems(results)
const relays: Relay[] = items.map((object) => databaseToEntity(object))
return relays
@ -41,15 +41,16 @@ export const searchRelays: (
export const getRelays: (db: QuickSQLiteConnection) => Promise<Relay[]> = async (db) => {
const notesQuery = 'SELECT * FROM nostros_relays WHERE deleted_at = 0;'
const resultSet = await db.execute(notesQuery)
const resultSet = db.execute(notesQuery)
const items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object))
return relays
}
export const getActiveRelays: (db: QuickSQLiteConnection) => Promise<Relay[]> = async (db) => {
const notesQuery = 'SELECT * FROM nostros_relays WHERE active = 1 AND deleted_at = 0;'
const resultSet = await db.execute(notesQuery)
const notesQuery =
'SELECT * FROM nostros_relays WHERE active = 1 AND deleted_at = 0 AND resilient != 0;'
const resultSet = db.execute(notesQuery)
const items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object))
return relays
@ -60,7 +61,7 @@ export const getRelay: (db: QuickSQLiteConnection, url: string) => Promise<Relay
url,
) => {
const notesQuery = 'SELECT * FROM nostros_relays WHERE url = ? AND deleted_at = 0;'
const resultSet = await db.execute(notesQuery, [url])
const resultSet = db.execute(notesQuery, [url])
const items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object))
return relays[0]

View File

@ -1,5 +1,5 @@
import { getItems } from '..'
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
export interface User {
id: string

View File

@ -1,6 +1,6 @@
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getItems } from '..'
import { Event } from '../../../lib/nostr/Events'
import { type Event } from '../../../lib/nostr/Events'
export interface Zap extends Event {
amount: boolean

View File

@ -1,8 +1,8 @@
import {
open,
QuickSQLiteConnection,
BatchQueryResult,
QueryResult,
type QuickSQLiteConnection,
type BatchQueryResult,
type QueryResult,
} from 'react-native-quick-sqlite'
export const initDatabase: () => QuickSQLiteConnection = () => {

View File

@ -1,4 +1,4 @@
import { DirectMessage } from '../../DatabaseFunctions/DirectMessages'
import { type DirectMessage } from '../../DatabaseFunctions/DirectMessages'
export const getOtherPubKey: (message: DirectMessage, ownPubKey: string) => string = (
message,

View File

@ -1,4 +1,4 @@
import { Event } from '../../../lib/nostr/Events'
import { type Event } from '../../../lib/nostr/Events'
export const getReplyEventId: (event: Event) => string | null = (event) => {
const eTags = getETags(event)
@ -43,6 +43,10 @@ export const getTaggedPubKeys: (event: Event) => string[] = (event) => {
return mainEventETags.map((item) => item[1] ?? '')
}
export const getRTags: (event: Event) => string[][] = (event) => {
return event?.tags.filter((tag) => tag[0] === 'r') || []
}
export const getETags: (event: Event) => string[][] = (event) => {
return event?.tags.filter((tag) => tag[0] === 'e') || []
}

View File

@ -1,7 +1,7 @@
import getUnixTime from 'date-fns/getUnixTime'
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import RelayPool from '../../../lib/nostr/RelayPool/intex'
import { Event } from '../../../lib/nostr/Events'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import type RelayPool from '../../../lib/nostr/RelayPool/intex'
import { type Event } from '../../../lib/nostr/Events'
import { getList } from '../../DatabaseFunctions/Lists'
import { decrypt, encrypt } from '../../../lib/nostr/Nip04'

View File

@ -0,0 +1,70 @@
import { type RelayMetadata } from '../../DatabaseFunctions/RelayMetadatas'
import { type Relay } from '../../DatabaseFunctions/Relays'
import { median } from '../../NativeFunctions'
import { getRTags } from '../Events'
export interface ResilientAssignation {
resilientRelays: string[]
smallRelays: string[]
centralizedRelays: string[]
}
export const getContactsRelays: (
userRelays: Relay[],
relayMetadada: RelayMetadata[],
) => Promise<string[]> = async (userRelays, relayMetadada) => {
const localhostRegExp = /.*:4848\/?.*$/
const pubKeys: string[] = []
// Url => pubkey[]
const relaysPresence: Record<string, string[]> = {}
relayMetadada.forEach((metadata) => {
const rTags: string[][] = getRTags(metadata)
pubKeys.push(metadata.pubkey)
const urls = rTags.map((tags) => tags[1])
urls.forEach((url) => {
if (!localhostRegExp.test(url)) {
relaysPresence[url] = [...(relaysPresence[url] ?? []), metadata.pubkey]
}
})
})
// Median of users per relay
const medianUsage = median(
Object.keys(relaysPresence).map((relay) => relaysPresence[relay].length),
)
// Sort relays by abs distance from the mediam
const relaysByPresence = Object.keys(relaysPresence).sort((n1: string, n2: string) => {
return (
Math.abs(relaysPresence[n1].length - medianUsage) -
Math.abs(relaysPresence[n2].length - medianUsage)
)
})
// Set helpers
const userRelayUrls = userRelays
.map((relay) => relay.url)
.filter((url) => !localhostRegExp.test(url))
// Allocate contacts to user's relays
let allocatedUsers: string[] = []
userRelayUrls.forEach((url) => {
allocatedUsers = [...allocatedUsers, ...relaysPresence[url]]
})
// Iterate over remaining sorted relays and assigns as much users as possible
const resilientAssignation: string[] = []
const remainingUsers = pubKeys.filter((pubKey) => !allocatedUsers.includes(pubKey))
relaysByPresence.forEach((relayUrl) => {
remainingUsers.forEach((pubKey) => {
if (!allocatedUsers.includes(pubKey) && relaysPresence[relayUrl].includes(pubKey)) {
allocatedUsers.push(pubKey)
if (!resilientAssignation.includes(relayUrl)) {
resilientAssignation.push(relayUrl)
}
}
})
})
return resilientAssignation
}

View File

@ -1,8 +1,8 @@
import getUnixTime from 'date-fns/getUnixTime'
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import RelayPool from '../../../lib/nostr/RelayPool/intex'
import { getUser, getUsers, User } from '../../DatabaseFunctions/Users'
import { Event } from '../../../lib/nostr/Events'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import type RelayPool from '../../../lib/nostr/RelayPool/intex'
import { getUser, getUsers, type User } from '../../DatabaseFunctions/Users'
import { type Event } from '../../../lib/nostr/Events'
import { getNpub } from '../../../lib/nostr/Nip19'
import { Kind } from 'nostr-tools'

View File

@ -1,10 +1,10 @@
// Thanks to v0l/snort for the nice code!
// https://github.com/v0l/snort/blob/39fbe3b10f94b7542df01fb085e4f164aab15fca/src/Feed/VoidUpload.ts
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getRelays, Relay } from '../../DatabaseFunctions/Relays'
import { type QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { getRelays, type Relay } from '../../DatabaseFunctions/Relays'
import { getUnixTime } from 'date-fns'
import { Event, signEvent } from '../../../lib/nostr/Events'
import { type Event, signEvent } from '../../../lib/nostr/Events'
import { requestInvoiceWithServiceParams, requestPayServiceParams } from 'lnurl-pay'
import axios from 'axios'

View File

@ -252,7 +252,9 @@
},
"relaysPage": {
"pushListTitle": "Push relay list to the network",
"pushListDescription": "Only relays on \"My relays\" list will be pushed.",
"pushList": "Push list",
"contactsList": "Contact's relays",
"freeAccess": "Free access",
"paid": "Paid",
"paidRelay": "Paid relay",

View File

@ -41,6 +41,7 @@
}
},
"profileLoadPage": {
"connect": "Connect and go home",
"foundRelays": "Relays found",
"searchingContacts": "Searching for your contacts",
"retry": "Retry",
@ -48,6 +49,8 @@
"nextStep": "Next step",
"searchingRelays": "Searching for your relays",
"reconnectOther": "Reconnect to other relays",
"contactRelays": "Contact's relays",
"contactRelaysDescription": "Searching for relays where your contacts can be found",
"relaysDescripion": "Connect with other relays if you have problems finding your data.",
"relays": "See relays",
"home": "Go Home",
@ -252,7 +255,9 @@
},
"relaysPage": {
"pushListTitle": "Push relay list to the network",
"pushListDescription": "Only relays on \"My relays\" list will be pushed.",
"pushList": "Push list",
"contactsList": "Contact's relays",
"freeAccess": "Free access",
"paid": "Paid",
"paidRelay": "Paid relay",

View File

@ -303,9 +303,11 @@
},
"relaysPage": {
"pushListTitle": "Subir lista de relays",
"pushListDescription": "Solo se subirán los relays en la lista \"Mis relays\".",
"pushList": "Subir lista",
"freeAccess": "Acceso libre",
"paid": "Pago",
"contactsList": "Relays de tus contactos",
"paidRelay": "Relay de pago",
"resilienceTitle": "Resilience (experimental)",
"resilienceDescription": "The Nostr protocol provides a good number of tools to build a decentralized network, but unlike other decentralized protocols such as Bitcoin, it is not inherently bound to its use. To achieve the goal of a decentralized network, clients (e.g., Nostros) and relays (content servers) should actively cooperate. The Nostros resilience mode seeks first to raise awareness of this among users, and second to test new patterns and methods to improve the resilience of the network.\nA working implementation for this will soon be applied in Nostros.",

View File

@ -300,7 +300,9 @@
},
"relaysPage": {
"pushListTitle": "Push relay list to the network",
"pushListDescription": "Only relays on \"My relays\" list will be pushed.",
"pushList": "Push list",
"contactsList": "Contact's relays",
"freeAccess": "Free access",
"paid": "Paid",
"paidRelay": "Paid relay",

View File

@ -295,7 +295,9 @@
},
"relaysPage": {
"pushListTitle": "Push relay list to the network",
"pushListDescription": "Only relays on \"My relays\" list will be pushed.",
"pushList": "Push list",
"contactsList": "Contact's relays",
"freeAccess": "Free access",
"paid": "Paid",
"paidRelay": "Paid relay",

View File

@ -271,9 +271,11 @@
},
"relaysPage": {
"pushListTitle": "Push relay list to the network",
"pushListDescription": "Only relays on \"My relays\" list will be pushed.",
"pushList": "Push list",
"freeAccess": "Free access",
"paid": "Paid",
"contactsList": "Contact's relays",
"paidRelay": "Paid relay",
"resilienceTitle": "弹性模式 (试验性)",
"resilienceDescription": "Nostr 协议提供了大量工具来构建去中心化网络,但与比特币等其他去中心化协议不同,它并不是默认使用的。 \n\n为了实现这样的目标客户端和中继应该积极协作。 Nostros 弹性模式首先尝试将这些问题暴露给用户,其次开始尝试新的模式和方法来帮助提高网络的弹性。 \n\n该功能的实现将很快默认出现在 Nostros 上",

View File

@ -1,6 +1,6 @@
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, Linking, ListRenderItem, StyleSheet } from 'react-native'
import { FlatList, Linking, type ListRenderItem, StyleSheet } from 'react-native'
import { Divider, List, Text, useTheme } from 'react-native-paper'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import VersionNumber from 'react-native-version-number'

View File

@ -1,14 +1,14 @@
import React, { useContext, useEffect, useState } from 'react'
import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { type NativeScrollEvent, type NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import { AppContext } from '../../Contexts/AppContext'
import { Kind } from 'nostr-tools'
import { useTranslation } from 'react-i18next'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import {
getBlocked,
getFollowersAndFollowing,
User,
type User,
getUsers,
} from '../../Functions/DatabaseFunctions/Users'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'

View File

@ -2,18 +2,21 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'
import {
FlatList,
Animated,
ListRenderItem,
NativeScrollEvent,
NativeSyntheticEvent,
ScrollView,
type ListRenderItem,
type NativeScrollEvent,
type NativeSyntheticEvent,
type ScrollView,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../Contexts/AppContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Event } from '../../lib/nostr/Events'
import { DirectMessage, getDirectMessages } from '../../Functions/DatabaseFunctions/DirectMessages'
import { getUser, User } from '../../Functions/DatabaseFunctions/Users'
import { type Event } from '../../lib/nostr/Events'
import {
type DirectMessage,
getDirectMessages,
} from '../../Functions/DatabaseFunctions/DirectMessages'
import { getUser, type User } from '../../Functions/DatabaseFunctions/Users'
import { useTranslation } from 'react-i18next'
import { username, usernamePubKey, usersToTags } from '../../Functions/RelayFunctions/Users'
import { getUnixTime } from 'date-fns'

View File

@ -2,9 +2,9 @@ import React, { useContext, useEffect, useState } from 'react'
import {
Dimensions,
FlatList,
ListRenderItem,
NativeScrollEvent,
NativeSyntheticEvent,
type ListRenderItem,
type NativeScrollEvent,
type NativeSyntheticEvent,
StyleSheet,
View,
} from 'react-native'
@ -13,11 +13,11 @@ import { AppContext } from '../../Contexts/AppContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import {
DirectMessage,
type DirectMessage,
getGroupedDirectMessages,
getUserLastDirectMessages,
} from '../../Functions/DatabaseFunctions/DirectMessages'
import { getUsers, User } from '../../Functions/DatabaseFunctions/Users'
import { getUsers, type User } from '../../Functions/DatabaseFunctions/Users'
import { getOtherPubKey } from '../../Functions/RelayFunctions/DirectMessages'
import { username } from '../../Functions/RelayFunctions/Users'
import {

View File

@ -2,15 +2,15 @@ import React, { useContext, useEffect, useState } from 'react'
import {
Animated,
FlatList,
ListRenderItem,
NativeScrollEvent,
NativeSyntheticEvent,
type ListRenderItem,
type NativeScrollEvent,
type NativeSyntheticEvent,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../Contexts/AppContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { useTranslation } from 'react-i18next'
import { formatPubKey, username, usernamePubKey } from '../../Functions/RelayFunctions/Users'
import { getUnixTime } from 'date-fns'
@ -31,9 +31,9 @@ import { Kind } from 'nostr-tools'
import { formatDate, handleInfinityScroll } from '../../Functions/NativeFunctions'
import NostrosAvatar from '../../Components/NostrosAvatar'
import UploadImage from '../../Components/UploadImage'
import { getGroupMessages, GroupMessage } from '../../Functions/DatabaseFunctions/Groups'
import { RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { getUsers, User } from '../../Functions/DatabaseFunctions/Users'
import { getGroupMessages, type GroupMessage } from '../../Functions/DatabaseFunctions/Groups'
import { type RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { getUsers, type User } from '../../Functions/DatabaseFunctions/Users'
import ProfileData from '../../Components/ProfileData'
import { ScrollView, Swipeable } from 'react-native-gesture-handler'
import { getETags } from '../../Functions/RelayFunctions/Events'

View File

@ -17,7 +17,7 @@ import RBSheet from 'react-native-raw-bottom-sheet'
import UploadImage from '../../Components/UploadImage'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { Kind } from 'nostr-tools'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { UserContext } from '../../Contexts/UserContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { getUnixTime } from 'date-fns'
@ -27,13 +27,13 @@ import {
getGroupMessagesCount,
getGroupMessagesMentionsCount,
getGroups,
Group,
type Group,
} from '../../Functions/DatabaseFunctions/Groups'
import { formatId } from '../../Functions/RelayFunctions/Users'
import NostrosAvatar from '../../Components/NostrosAvatar'
import { navigate } from '../../lib/Navigation'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { type RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { validNip21 } from '../../Functions/NativeFunctions'
import { getNip19Key } from '../../lib/nostr/Nip19'
import DatabaseModule from '../../lib/Native/DatabaseModule'

View File

@ -1,22 +1,22 @@
import React, { useCallback, useContext, useState, useEffect } from 'react'
import {
NativeScrollEvent,
NativeSyntheticEvent,
type NativeScrollEvent,
type NativeSyntheticEvent,
RefreshControl,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getNotes, Note } from '../../../Functions/DatabaseFunctions/Notes'
import { getNotes, type Note } from '../../../Functions/DatabaseFunctions/Notes'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'
import { UserContext } from '../../../Contexts/UserContext'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { ActivityIndicator, Button, Text } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { useTheme } from '@react-navigation/native'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { useTranslation } from 'react-i18next'
import { getContactsCount } from '../../../Functions/DatabaseFunctions/Users'

View File

@ -1,25 +1,29 @@
import React, { useCallback, useContext, useState, useEffect } from 'react'
import {
ActivityIndicator,
NativeScrollEvent,
NativeSyntheticEvent,
type NativeScrollEvent,
type NativeSyntheticEvent,
RefreshControl,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getMainNotes, getMainNotesCount, Note } from '../../../Functions/DatabaseFunctions/Notes'
import {
getMainNotes,
getMainNotesCount,
type Note,
} from '../../../Functions/DatabaseFunctions/Notes'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'
import { UserContext } from '../../../Contexts/UserContext'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { Chip, Button, Text } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { useTheme } from '@react-navigation/native'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { t } from 'i18next'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
interface GlobalFeedProps {
navigation: any

View File

@ -1,25 +1,25 @@
import React, { useCallback, useContext, useState, useEffect } from 'react'
import {
NativeScrollEvent,
NativeSyntheticEvent,
type NativeScrollEvent,
type NativeSyntheticEvent,
RefreshControl,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getMainNotes, Note } from '../../../Functions/DatabaseFunctions/Notes'
import { getMainNotes, type Note } from '../../../Functions/DatabaseFunctions/Notes'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'
import { UserContext } from '../../../Contexts/UserContext'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { ActivityIndicator, Button, Text } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { useTheme } from '@react-navigation/native'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { useTranslation } from 'react-i18next'
import { getContactsCount, getUsers, User } from '../../../Functions/DatabaseFunctions/Users'
import { getContactsCount, getUsers, type User } from '../../../Functions/DatabaseFunctions/Users'
interface MyFeedProps {
navigation: any

View File

@ -1,27 +1,27 @@
import React, { useCallback, useContext, useState, useEffect } from 'react'
import {
NativeScrollEvent,
NativeSyntheticEvent,
type NativeScrollEvent,
type NativeSyntheticEvent,
RefreshControl,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getNotes, Note } from '../../../Functions/DatabaseFunctions/Notes'
import { getNotes, type Note } from '../../../Functions/DatabaseFunctions/Notes'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'
import { UserContext } from '../../../Contexts/UserContext'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { ActivityIndicator, Button, Text } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { useTheme } from '@react-navigation/native'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { useTranslation } from 'react-i18next'
import { getMostZapedNotesContacts } from '../../../Functions/DatabaseFunctions/Zaps'
import { getUnixTime } from 'date-fns'
import { getUsers, User } from '../../../Functions/DatabaseFunctions/Users'
import { getUsers, type User } from '../../../Functions/DatabaseFunctions/Users'
interface ZapsFeedProps {
navigation: any

View File

@ -8,7 +8,7 @@ import RBSheet from 'react-native-raw-bottom-sheet'
import AboutPage from '../../Pages/AboutPage'
import { useTranslation } from 'react-i18next'
import ProfileCreatePage from '../../Pages/ProfileCreatePage'
import { DrawerNavigationProp } from '@react-navigation/drawer'
import { type DrawerNavigationProp } from '@react-navigation/drawer'
import RelaysPage from '../RelaysPage'
import ConfigPage from '../ConfigPage'
import QrReaderPage from '../QrReaderPage'

View File

@ -1,11 +1,11 @@
import React, { useCallback, useContext, useEffect, useState } from 'react'
import { AppContext } from '../../Contexts/AppContext'
import { getNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
import { getNotes, type Note } from '../../Functions/DatabaseFunctions/Notes'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import NoteCard from '../../Components/NoteCard'
import { Kind } from 'nostr-tools'
import { Dimensions, RefreshControl, StyleSheet, View } from 'react-native'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { getDirectReplies } from '../../Functions/RelayFunctions/Events'
import { AnimatedFAB, useTheme } from 'react-native-paper'
import { UserContext } from '../../Contexts/UserContext'
@ -13,7 +13,7 @@ import { navigate } from '../../lib/Navigation'
import { useFocusEffect } from '@react-navigation/native'
import { SkeletonNote } from '../../Components/SkeletonNote/SkeletonNote'
import { ScrollView } from 'react-native-gesture-handler'
import { RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../lib/nostr/RelayPool/intex'
interface NotePageProps {
route: { params: { noteId: string } }

View File

@ -1,14 +1,14 @@
import React, { useCallback, useContext, useEffect, useState } from 'react'
import {
NativeScrollEvent,
NativeSyntheticEvent,
type NativeScrollEvent,
type NativeSyntheticEvent,
RefreshControl,
StyleSheet,
View,
} from 'react-native'
import { AppContext } from '../../Contexts/AppContext'
import SInfo from 'react-native-sensitive-info'
import { getMentionNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
import { getMentionNotes, type Note } from '../../Functions/DatabaseFunctions/Notes'
import NoteCard from '../../Components/NoteCard'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
@ -20,8 +20,8 @@ import { useTranslation } from 'react-i18next'
import { navigate } from '../../lib/Navigation'
import { useFocusEffect } from '@react-navigation/native'
import { getUnixTime } from 'date-fns'
import { Config } from '../../Functions/DatabaseFunctions/Config'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { type Config } from '../../Functions/DatabaseFunctions/Config'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { getETags } from '../../Functions/RelayFunctions/Events'
export const NotificationsFeed: React.FC = () => {

View File

@ -4,13 +4,13 @@ import { Keyboard, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import { Button, Checkbox, Snackbar, Text, TextInput } from 'react-native-paper'
import { useTranslation } from 'react-i18next'
import { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types'
import { type DrawerNavigationHelpers } from '@react-navigation/drawer/lib/typescript/src/types'
import { UserContext } from '../../Contexts/UserContext'
import { privateKeyFromSeedWords } from 'nostr-tools/nip06'
import { nsecEncode } from 'nostr-tools/nip19'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { getUnixTime } from 'date-fns'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
interface ProfileCreatePageProps {
navigation: DrawerNavigationHelpers

View File

@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next'
import { AppContext } from '../../../Contexts/AppContext'
import {
getRelayMetadata,
RelayMetadata,
type RelayMetadata,
} from '../../../Functions/DatabaseFunctions/RelayMetadatas'
interface FirstStepProps {
@ -115,12 +115,12 @@ export const FirstStep: React.FC<FirstStepProps> = ({ nextStep }) => {
</View>
</View>
<View style={styles.buttons}>
<Button mode='contained' onPress={finish} disabled={!profileFound}>
{t('profileLoadPage.nextStep')}
</Button>
<Button mode='outlined' onPress={() => reconnectRelays()}>
{t('profileLoadPage.reconnectOther')}
</Button>
<Button mode='contained' onPress={finish} disabled={!profileFound}>
{t('profileLoadPage.nextStep')}
</Button>
<Button mode='outlined' onPress={() => setUserState('ready')}>
{t('profileLoadPage.home')}
</Button>

View File

@ -1,14 +1,23 @@
import { getUnixTime } from 'date-fns'
import { Kind } from 'nostr-tools'
import React, { useContext, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ActivityIndicator, StyleSheet, View } from 'react-native'
import { Button, Text } from 'react-native-paper'
import { type Relay } from '../../../Functions/DatabaseFunctions/Relays'
import {
ActivityIndicator,
FlatList,
type ListRenderItem,
ScrollView,
StyleSheet,
View,
} from 'react-native'
import { Button, Divider, List, Text, useTheme } from 'react-native-paper'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import Logo from '../../../Components/Logo'
import { AppContext } from '../../../Contexts/AppContext'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { UserContext } from '../../../Contexts/UserContext'
import { getUsers, User } from '../../../Functions/DatabaseFunctions/Users'
import { getUsers, type User } from '../../../Functions/DatabaseFunctions/Users'
import { relayToColor } from '../../../Functions/NativeFunctions'
interface SecondStepProps {
nextStep: () => void
@ -16,8 +25,9 @@ interface SecondStepProps {
export const SecondStep: React.FC<SecondStepProps> = ({ nextStep }) => {
const { t } = useTranslation('common')
const theme = useTheme()
const { database } = useContext(AppContext)
const { relayPool, relayPoolReady, lastEventId } = useContext(RelayPoolContext)
const { relayPool, relayPoolReady, lastEventId, relays } = useContext(RelayPoolContext)
const { publicKey, setUserState } = useContext(UserContext)
const [contactsCount, setContactsCount] = useState<number>()
@ -38,17 +48,10 @@ export const SecondStep: React.FC<SecondStepProps> = ({ nextStep }) => {
kinds: [Kind.Contacts],
authors: [publicKey],
},
])
relayPool?.subscribe('profile-load-others', [
{
kinds: [Kind.ChannelCreation, Kind.ChannelMetadata],
authors: [publicKey],
},
{
kinds: [1002],
authors: [publicKey],
limit: 1,
},
{
kinds: [10000],
authors: [publicKey],
@ -60,7 +63,7 @@ export const SecondStep: React.FC<SecondStepProps> = ({ nextStep }) => {
limit: 1,
},
{
kinds: [3001],
kinds: [30001],
authors: [publicKey],
limit: 1,
},
@ -73,26 +76,55 @@ export const SecondStep: React.FC<SecondStepProps> = ({ nextStep }) => {
getUsers(database, {}).then((results) => {
if (results.length > 0) {
setContactsCount(results.filter((user) => user.contact).length)
const authors = [...results.map((user: User) => user.id), publicKey]
const authors = [...results.map((user: User) => user.id)]
relayPool?.subscribe('profile-load-notes', [
{
kinds: [Kind.Metadata, 10002],
authors,
},
{
kinds: [Kind.Text],
authors,
since: getUnixTime(new Date()) - 43200,
},
])
}
})
}
}
const renderItem: ListRenderItem<Relay> = ({ item, index }) => {
return (
<View style={styles.relayItem}>
<List.Item
key={index}
title={item.url.replace('wss://', '').replace('ws://', '')}
right={() => {
if (!item?.paid) return <></>
return (
<MaterialCommunityIcons
name='wallet-outline'
size={16}
color={theme.colors.onPrimaryContainer}
/>
)
}}
left={() => (
<MaterialCommunityIcons
style={styles.relayColor}
name='circle'
color={relayToColor(item.url)}
/>
)}
/>
</View>
)
}
return (
<View style={styles.container}>
<View>
<View style={styles.loadingProfile}>
<Text variant='titleMedium'>{t('profileLoadPage.connectedRelays')}</Text>
<Text variant='titleMedium' style={{ color: '#7ADC70' }}>
{relays.length}
</Text>
</View>
<View style={styles.logo}>
<Logo onlyIcon size='medium' />
</View>
@ -106,12 +138,28 @@ export const SecondStep: React.FC<SecondStepProps> = ({ nextStep }) => {
: t('profileLoadPage.searchingContacts')}
</Text>
</View>
<View style={styles.list}>
<View style={styles.titleWrapper}>
<Text style={styles.title} variant='titleMedium'>
{t('relaysPage.myList')}
</Text>
<Divider />
</View>
<ScrollView horizontal={false}>
<FlatList
showsVerticalScrollIndicator={false}
data={relays}
renderItem={renderItem}
ItemSeparatorComponent={Divider}
/>
</ScrollView>
</View>
</View>
<View style={styles.buttons}>
<Button mode='outlined' onPress={() => {}}>
{t('profileLoadPage.retry')}
<Button mode='contained' onPress={nextStep} disabled={contactsCount === undefined}>
{t('profileLoadPage.nextStep')}
</Button>
<Button mode='contained' onPress={() => setUserState('ready')}>
<Button mode='outlined' onPress={() => setUserState('ready')}>
{t('profileLoadPage.home')}
</Button>
</View>
@ -126,7 +174,7 @@ const styles = StyleSheet.create({
flex: 1,
},
buttons: {
height: 120,
height: 100,
justifyContent: 'space-between',
},
logo: {
@ -134,6 +182,9 @@ const styles = StyleSheet.create({
alignContent: 'center',
flexDirection: 'row',
},
list: {
maxHeight: 400,
},
center: {
alignContent: 'center',
textAlign: 'center',
@ -146,6 +197,35 @@ const styles = StyleSheet.create({
activityIndicator: {
paddingRight: 16,
},
titleWrapper: {
marginBottom: 4,
marginTop: 24,
},
title: {
paddingLeft: 16,
paddingRight: 16,
marginBottom: 8,
flexDirection: 'row',
justifyContent: 'space-between',
},
relayItem: {
paddingLeft: 16,
paddingRight: 16,
},
relayButtons: {
paddingBottom: 16,
flexDirection: 'row',
justifyContent: 'space-between',
},
relayButton: {
marginRight: 16,
},
relayActionButtons: {
flexDirection: 'row',
},
relayColor: {
paddingTop: 9,
},
})
export default SecondStep

View File

@ -0,0 +1,187 @@
import React, { useContext, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList, type ListRenderItem, ScrollView, StyleSheet, View } from 'react-native'
import { Button, Divider, List, Text } from 'react-native-paper'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import Logo from '../../../Components/Logo'
import { AppContext } from '../../../Contexts/AppContext'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { UserContext } from '../../../Contexts/UserContext'
import { getUsers, type User } from '../../../Functions/DatabaseFunctions/Users'
import { relayToColor } from '../../../Functions/NativeFunctions'
import { getAllRelayMetadata } from '../../../Functions/DatabaseFunctions/RelayMetadatas'
import { getContactsRelays } from '../../../Functions/RelayFunctions/Metadata'
interface ThirdStepProps {
nextStep: () => void
}
export const ThirdStep: React.FC<ThirdStepProps> = ({ nextStep }) => {
const { t } = useTranslation('common')
const { database } = useContext(AppContext)
const { relayPool, relayPoolReady, lastEventId, relays, addRelayItem } =
useContext(RelayPoolContext)
const { publicKey, setUserState } = useContext(UserContext)
const [asignation, setAsignation] = useState<string[]>()
React.useEffect(() => {
loadPetsRelays()
}, [])
React.useEffect(() => {
if (database) {
getAllRelayMetadata(database).then((relayMetadata) => {
getContactsRelays(relays, relayMetadata).then(setAsignation)
})
}
}, [lastEventId])
const loadPetsRelays: () => void = () => {
if (database && publicKey && relayPoolReady) {
getUsers(database, {}).then((results) => {
if (results.length > 0) {
const authors = [...results.map((user: User) => user.id), publicKey]
relayPool?.subscribe('profile-load-relays', [
{
kinds: [10002],
authors,
},
])
}
})
}
}
const renderItem: ListRenderItem<string> = ({ item, index }) => {
return (
<View style={styles.relayItem}>
<List.Item
key={index}
title={item.replace('wss://', '').replace('ws://', '')}
left={() => (
<MaterialCommunityIcons
style={styles.relayColor}
name='circle'
color={relayToColor(item)}
/>
)}
/>
</View>
)
}
const connect: () => void = () => {
if (asignation) {
asignation.forEach(async (url) => await addRelayItem({ url, resilient: 1, global_feed: 0 }))
}
setUserState('ready')
}
return (
<View style={styles.container}>
<View>
<View style={styles.loadingProfile}>
<Text variant='titleMedium'>{t('profileLoadPage.connectedRelays')}</Text>
<Text variant='titleMedium' style={{ color: '#7ADC70' }}>
{relays.length}
</Text>
</View>
<View style={styles.logo}>
<Logo onlyIcon size='medium' />
</View>
<View style={styles.loadingProfile}>
<Text variant='titleMedium' style={styles.center}>
{t('profileLoadPage.contactRelaysDescription')}
</Text>
</View>
<View style={styles.list}>
<View style={styles.titleWrapper}>
<Text style={styles.title} variant='titleMedium'>
{t('profileLoadPage.contactRelays')}
</Text>
<Divider />
</View>
<ScrollView horizontal={false}>
<FlatList
showsVerticalScrollIndicator={false}
data={asignation}
renderItem={renderItem}
ItemSeparatorComponent={Divider}
/>
</ScrollView>
</View>
</View>
<View style={styles.buttons}>
<Button mode='contained' onPress={connect}>
{t('profileLoadPage.connect')}
</Button>
<Button mode='outlined' onPress={() => setUserState('ready')}>
{t('profileLoadPage.home')}
</Button>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
padding: 16,
justifyContent: 'space-between',
flex: 1,
},
buttons: {
height: 100,
justifyContent: 'space-between',
},
logo: {
justifyContent: 'center',
alignContent: 'center',
flexDirection: 'row',
},
list: {
maxHeight: 450,
},
center: {
alignContent: 'center',
textAlign: 'center',
},
loadingProfile: {
justifyContent: 'center',
alignContent: 'center',
flexDirection: 'row',
},
activityIndicator: {
paddingRight: 16,
},
titleWrapper: {
marginBottom: 4,
marginTop: 24,
},
title: {
paddingLeft: 16,
paddingRight: 16,
marginBottom: 8,
flexDirection: 'row',
justifyContent: 'space-between',
},
relayItem: {
paddingLeft: 16,
paddingRight: 16,
},
relayButtons: {
paddingBottom: 16,
flexDirection: 'row',
justifyContent: 'space-between',
},
relayButton: {
marginRight: 16,
},
relayActionButtons: {
flexDirection: 'row',
},
relayColor: {
paddingTop: 9,
},
})
export default ThirdStep

View File

@ -4,6 +4,7 @@ import { StyleSheet, View } from 'react-native'
import { useFocusEffect } from '@react-navigation/native'
import FirstStep from './FirstStep'
import SecondStep from './SecondStep'
import ThirdStep from './ThirdStep'
export const ProfileLoadPage: React.FC = () => {
const { relayPool } = useContext(RelayPoolContext)
@ -15,8 +16,8 @@ export const ProfileLoadPage: React.FC = () => {
relayPool?.unsubscribe([
'profile-load-meta',
'profile-load-contacts',
'profile-load-others',
'profile-load-notes',
'profile-load-relays',
])
}, []),
)
@ -29,6 +30,7 @@ export const ProfileLoadPage: React.FC = () => {
<View style={styles.container}>
{step === 0 && <FirstStep nextStep={nextStep} />}
{step === 1 && <SecondStep nextStep={nextStep} />}
{step === 2 && <ThirdStep nextStep={nextStep} />}
</View>
)
}

View File

@ -1,18 +1,18 @@
import React, { useContext, useState, useEffect } from 'react'
import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { type NativeScrollEvent, type NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getNotes, Note } from '../../../Functions/DatabaseFunctions/Notes'
import { getNotes, type Note } from '../../../Functions/DatabaseFunctions/Notes'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { ActivityIndicator, Text, useTheme } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'
import { getETags } from '../../../Functions/RelayFunctions/Events'
import { getList } from '../../../Functions/DatabaseFunctions/Lists'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { useTranslation } from 'react-i18next'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../../lib/nostr/RelayPool/intex'
interface BookmarksFeedProps {
publicKey: string

View File

@ -1,14 +1,14 @@
import React, { useContext, useState, useEffect } from 'react'
import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { type NativeScrollEvent, type NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getMainNotes, Note } from '../../../Functions/DatabaseFunctions/Notes'
import { getMainNotes, type Note } from '../../../Functions/DatabaseFunctions/Notes'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { ActivityIndicator } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../../lib/nostr/RelayPool/intex'
interface NotesFeedProps {
publicKey: string

View File

@ -1,14 +1,14 @@
import React, { useContext, useState, useEffect } from 'react'
import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { type NativeScrollEvent, type NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getReplyNotes, Note } from '../../../Functions/DatabaseFunctions/Notes'
import { getReplyNotes, type Note } from '../../../Functions/DatabaseFunctions/Notes'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { ActivityIndicator } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'
import { RelayFilters } from '../../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../../lib/nostr/RelayPool/intex'
interface RepliesFeedProps {
publicKey: string

View File

@ -1,12 +1,12 @@
import React, { useContext, useState, useEffect } from 'react'
import { NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { type NativeScrollEvent, type NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import { AppContext } from '../../../Contexts/AppContext'
import { getNotes, Note } from '../../../Functions/DatabaseFunctions/Notes'
import { getNotes, type Note } from '../../../Functions/DatabaseFunctions/Notes'
import { RelayPoolContext } from '../../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { ActivityIndicator } from 'react-native-paper'
import NoteCard from '../../../Components/NoteCard'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { getMostZapedNotes } from '../../../Functions/DatabaseFunctions/Zaps'
import { getUnixTime } from 'date-fns'
import { handleInfinityScroll } from '../../../Functions/NativeFunctions'

View File

@ -4,7 +4,7 @@ import { Surface, Text, Snackbar } from 'react-native-paper'
import { AppContext } from '../../Contexts/AppContext'
import { UserContext } from '../../Contexts/UserContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { getUser, User } from '../../Functions/DatabaseFunctions/Users'
import { getUser, type User } from '../../Functions/DatabaseFunctions/Users'
import { Kind } from 'nostr-tools'
import { useTranslation } from 'react-i18next'
import { useFocusEffect } from '@react-navigation/native'

View File

@ -1,19 +1,19 @@
import React, { useCallback, useContext, useState, useEffect } from 'react'
import {
NativeScrollEvent,
NativeSyntheticEvent,
type NativeScrollEvent,
type NativeSyntheticEvent,
RefreshControl,
StyleSheet,
View,
} from 'react-native'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { AppContext } from '../../Contexts/AppContext'
import { getReactedNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
import { getReactedNotes, type Note } from '../../Functions/DatabaseFunctions/Notes'
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
import { UserContext } from '../../Contexts/UserContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { ActivityIndicator, Text } from 'react-native-paper'
import NoteCard from '../../Components/NoteCard'
import { useTheme } from '@react-navigation/native'

View File

@ -1,8 +1,15 @@
import React, { useContext, useState } from 'react'
import { Clipboard, FlatList, ListRenderItem, ScrollView, StyleSheet, View } from 'react-native'
import {
Clipboard,
FlatList,
type ListRenderItem,
ScrollView,
StyleSheet,
View,
} from 'react-native'
import { useTranslation } from 'react-i18next'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Relay } from '../../Functions/DatabaseFunctions/Relays'
import { type Relay } from '../../Functions/DatabaseFunctions/Relays'
import { REGEX_SOCKET_LINK } from '../../Constants/Relay'
import {
List,
@ -21,7 +28,7 @@ import { relayToColor } from '../../Functions/NativeFunctions'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { useFocusEffect } from '@react-navigation/native'
import { UserContext } from '../../Contexts/UserContext'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { getUnixTime } from 'date-fns'
export const RelaysPage: React.FC = () => {
@ -126,12 +133,14 @@ export const RelaysPage: React.FC = () => {
const onPressPushRelay: () => void = () => {
if (publicKey) {
const activeRelays = relays.filter((relay) => relay?.active)
const activeRelays = relays.filter(
(relay) => relay?.active && (!relay.resilient || relay.resilient < 0),
)
const tags: string[][] = activeRelays.map((relay) => [
'r',
relay.url ?? '',
relay.mode ?? '',
relay.paid ? 'paid' : 'free',
relay.paid ? 'paid' : '',
])
const event: Event = {
content: '',
@ -174,14 +183,16 @@ export const RelaysPage: React.FC = () => {
key={index}
title={item.url.replace('wss://', '').replace('ws://', '')}
right={() => {
if (!item?.paid) return <></>
return (
const icons: string[] = []
if (item?.paid) icons.push('wallet-outline')
return icons.map((icon) => (
<MaterialCommunityIcons
name='wallet-outline'
key={icon}
name={icon}
size={16}
color={theme.colors.onPrimaryContainer}
/>
)
))
}}
left={() => (
<MaterialCommunityIcons
@ -258,12 +269,31 @@ export const RelaysPage: React.FC = () => {
showsVerticalScrollIndicator={false}
data={relays.filter((relay) => {
return (
((relay.paid === undefined || relay.paid < 1) && showFreeRelays) ||
(relay.paid !== undefined && relay.paid > 0 && showPaidRelays)
(!relay.resilient &&
(relay.paid === undefined || relay.paid < 1) &&
showFreeRelays) ||
(!relay.resilient && relay.paid !== undefined && relay.paid > 0 && showPaidRelays)
)
})}
renderItem={renderItem}
ItemSeparatorComponent={Divider}
/>
<View style={styles.titleWrapper}>
<Text style={styles.title} variant='titleMedium'>
{t('relaysPage.contactsList')}
</Text>
<Divider />
</View>
<FlatList
showsVerticalScrollIndicator={false}
style={styles.relayList}
data={relays.filter((relay) => {
return (
(relay.resilient && (relay.paid === undefined || relay.paid < 1) && showFreeRelays) ??
(relay.resilient && relay.paid !== undefined && relay.paid > 0 && showPaidRelays)
)
})}
renderItem={renderItem}
ItemSeparatorComponent={Divider}
/>
</ScrollView>
@ -300,6 +330,7 @@ export const RelaysPage: React.FC = () => {
<View style={styles.addRelay}>
<View style={styles.bottomDrawerButton}>
<Text variant='titleLarge'>{t('relaysPage.pushListTitle')}</Text>
<Text variant='bodyMedium'>{t('relaysPage.pushListDescription')}</Text>
</View>
<View style={styles.bottomDrawerButton}>
<Button mode='contained' onPress={onPressPushRelay}>

View File

@ -1,19 +1,19 @@
import React, { useCallback, useContext, useState, useEffect } from 'react'
import {
NativeScrollEvent,
NativeSyntheticEvent,
type NativeScrollEvent,
type NativeSyntheticEvent,
RefreshControl,
StyleSheet,
View,
} from 'react-native'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import { FlashList, type ListRenderItem } from '@shopify/flash-list'
import { AppContext } from '../../Contexts/AppContext'
import { getRepostedNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
import { getRepostedNotes, type Note } from '../../Functions/DatabaseFunctions/Notes'
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
import { UserContext } from '../../Contexts/UserContext'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { Kind } from 'nostr-tools'
import { RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { type RelayFilters } from '../../lib/nostr/RelayPool/intex'
import { ActivityIndicator, Text } from 'react-native-paper'
import NoteCard from '../../Components/NoteCard'
import { useTheme } from '@react-navigation/native'

View File

@ -1,13 +1,13 @@
import React, { useContext, useEffect, useState } from 'react'
import { Dimensions, ScrollView, StyleSheet, View } from 'react-native'
import { AppContext } from '../../Contexts/AppContext'
import { Event } from '../../lib/nostr/Events'
import { type Event } from '../../lib/nostr/Events'
import { useTranslation } from 'react-i18next'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import getUnixTime from 'date-fns/getUnixTime'
import { Note } from '../../Functions/DatabaseFunctions/Notes'
import { type Note } from '../../Functions/DatabaseFunctions/Notes'
import { getETags } from '../../Functions/RelayFunctions/Events'
import { getUsers, User } from '../../Functions/DatabaseFunctions/Users'
import { getUsers, type User } from '../../Functions/DatabaseFunctions/Users'
import { formatPubKey } from '../../Functions/RelayFunctions/Users'
import { Button, IconButton, Switch, Text, TextInput, useTheme } from 'react-native-paper'
import { UserContext } from '../../Contexts/UserContext'

View File

@ -5,7 +5,7 @@ interface RelayPoolInterface {
sendAll: (message: string, globalFeed: boolean) => void
sendRelay: (message: string, relayUrl: string) => void
connect: (pubKey: string, callback: (eventId: string) => void) => void
add: (url: string, callback: () => void) => void
add: (url: string, resilient: number, globalFeed: number, callback: () => void) => void
remove: (url: string, callback: () => void) => void
removeAll: (callback?: () => void) => void
update: (

View File

@ -1,5 +1,5 @@
import schnorr from 'bip-schnorr'
import { Kind } from 'nostr-tools'
import { type Kind } from 'nostr-tools'
export interface Event {
content: string

View File

@ -1,10 +1,10 @@
import {
decode,
EventPointer,
type EventPointer,
neventEncode,
nprofileEncode,
npubEncode,
ProfilePointer,
type ProfilePointer,
} from 'nostr-tools/nip19'
export function getNpub(key: string | undefined): string {

View File

@ -1,10 +1,5 @@
// import { spawnThread } from 'react-native-multithreading'
import { signEvent, validateEvent, Event } from '../Events'
import { signEvent, validateEvent, type Event } from '../Events'
import RelayPoolModule from '../../Native/WebsocketModule'
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { median, randomInt } from '../../../Functions/NativeFunctions'
import { getNoteRelaysPresence } from '../../../Functions/DatabaseFunctions/NotesRelays'
import DatabaseModule from '../../Native/DatabaseModule'
export interface RelayFilters {
ids?: string[]
@ -21,13 +16,6 @@ export interface RelayMessage {
data: string
}
export interface ResilientAssignation {
resilientRelays: Record<string, string[]>
smallRelays: Record<string, string[]>
centralizedRelays: Record<string, string[]>
fallback: Record<string, string[]>
}
export const fallbackRelays = [
'wss://brb.io',
'wss://nostr-pub.wellorder.net',
@ -78,17 +66,10 @@ class RelayPool {
constructor(privateKey?: string) {
this.privateKey = privateKey
this.subscriptions = {}
this.resilientAssignation = {
resilientRelays: {},
smallRelays: {},
centralizedRelays: {},
fallback: {},
}
}
private readonly privateKey?: string
private subscriptions: Record<string, string[]>
public resilientAssignation: ResilientAssignation
private readonly sendAll: (message: object, globalFeed?: boolean) => void = async (
message,
@ -111,94 +92,13 @@ class RelayPool {
RelayPoolModule.connect(publicKey, onEventId)
}
public readonly resilientMode: (db: QuickSQLiteConnection, publicKey: string) => void = async (
db,
) => {
await DatabaseModule.desactivateResilientRelays()
// Get relays with contacts' pubkeys with at least one event found, randomly sorted
const relaysPresence: Record<string, string[]> = await getNoteRelaysPresence(db)
// Median of users per relay
const medianUsage = median(
Object.keys(relaysPresence).map((relay) => relaysPresence[relay].length),
)
// Sort relays by abs distance from the mediam
const relaysByPresence = Object.keys(relaysPresence).sort((n1: string, n2: string) => {
return (
Math.abs(relaysPresence[n1].length - medianUsage) -
Math.abs(relaysPresence[n2].length - medianUsage)
)
})
// Get top5 relays closer to the mediam
const medianRelays = relaysByPresence.slice(0, 5)
// Set helpers
let biggestRelayLenght = 0
this.resilientAssignation.resilientRelays = {}
const allocatedUsers: string[] = []
medianRelays.forEach((relayUrl) => {
this.resilientAssignation.resilientRelays[relayUrl] = []
const length = relaysPresence[relayUrl].length
if (length > biggestRelayLenght) biggestRelayLenght = length
})
// Iterate over the N index of top5 relay list removing identical pubkey from others
for (let index = 0; index < biggestRelayLenght - 1; index++) {
medianRelays.forEach((relayUrl) => {
const pubKey = relaysPresence[relayUrl][index]
if (pubKey && !allocatedUsers.includes(pubKey)) {
allocatedUsers.push(pubKey)
this.resilientAssignation.resilientRelays[relayUrl].push(pubKey)
}
})
}
// Iterate over remaining relays and assigns as much remaining users as possible
relaysByPresence.slice(5, relaysByPresence.length).forEach((relayUrl) => {
relaysPresence[relayUrl].forEach((pubKey) => {
if (!allocatedUsers.includes(pubKey)) {
allocatedUsers.push(pubKey)
if (relaysPresence[relayUrl].length > medianUsage) {
if (!this.resilientAssignation.centralizedRelays[relayUrl])
this.resilientAssignation.centralizedRelays[relayUrl] = []
this.resilientAssignation.centralizedRelays[relayUrl].push(pubKey)
} else {
if (!this.resilientAssignation.smallRelays[relayUrl])
this.resilientAssignation.smallRelays[relayUrl] = []
this.resilientAssignation.smallRelays[relayUrl].push(pubKey)
}
}
})
})
// Target list size is 5, adds random relays from a fallback list
const resilientUrls = [
...Object.keys(this.resilientAssignation.resilientRelays),
...Object.keys(this.resilientAssignation.centralizedRelays),
...Object.keys(this.resilientAssignation.smallRelays),
]
while (resilientUrls.length < 5) {
let fallbackRelay = ''
while (fallbackRelay === '') {
const randomRelayIndex = randomInt(0, fallbackRelays.length - 1)
if (!resilientUrls.includes(fallbackRelays[randomRelayIndex])) {
fallbackRelay = fallbackRelays[randomRelayIndex]
}
}
resilientUrls.push(fallbackRelay)
this.resilientAssignation.centralizedRelays[fallbackRelay] = []
}
// Stores in DB
// resilientUrls.forEach((url) => DatabaseModule.createResilientRelay(url))
// resilientUrls.forEach((url) => DatabaseModule.activateResilientRelay(url))
}
public readonly add: (relayUrl: string, callback?: () => void) => void = async (
relayUrl,
callback = () => {},
) => {
RelayPoolModule.add(relayUrl, callback)
public readonly add: (
relayUrl: string,
resilient: number,
globalFeed: number,
callback?: () => void,
) => void = async (relayUrl, resilient, globalFeed, callback = () => {}) => {
RelayPoolModule.add(relayUrl, resilient, globalFeed, callback)
}
public readonly remove: (relayUrl: string, callback?: () => void) => void = async (

View File

@ -29,20 +29,20 @@
"cryptr": "^6.1.0",
"date-fns": "^2.29.3",
"events": "^3.3.0",
"i18next": "^22.4.9",
"i18next": "^22.4.10",
"lnurl-pay": "^2.2.0",
"lodash.debounce": "^4.0.8",
"nostr-tools": "^1.2.1",
"react": "18.2.0",
"react-content-loader": "^6.2.0",
"react-i18next": "^12.1.5",
"react-i18next": "^12.2.0",
"react-native": "0.70.6",
"react-native-action-button": "^2.8.5",
"react-native-bidirectional-infinite-scroll": "^0.3.3",
"react-native-blob-util": "^0.17.1",
"react-native-fast-image": "^8.6.3",
"react-native-gesture-handler": "^2.8.0",
"react-native-image-picker": "^5.0.1",
"react-native-image-picker": "^5.1.0",
"react-native-multithreading": "^1.1.1",
"react-native-pager-view": "^6.1.4",
"react-native-paper": "^5.1.3",
@ -81,7 +81,7 @@
"@types/react-native": "^0.70.8",
"@types/react-native-sqlite-storage": "^5.0.2",
"@types/react-native-vector-icons": "^6.4.13",
"@types/react-test-renderer": "^16.9.2",
"@types/react-test-renderer": "^18.0.0",
"@types/sqlstring": "^2.3.0",
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.43.0",
@ -100,7 +100,7 @@
"jest": "^29.4.3",
"metro-react-native-babel-preset": "^0.75.0",
"prettier": "^2.8.4",
"react-test-renderer": "18.1.0",
"react-test-renderer": "18.2.0",
"typescript": "^4.9.4"
},
"resolutions": {

View File

@ -1795,14 +1795,14 @@
dependencies:
"@types/react" "*"
"@types/react-test-renderer@^16.9.2":
version "16.9.5"
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.5.tgz#edab67da470f7c3e997f58d55dcfe2643cc30a68"
integrity sha512-C4cN7C2uSSGOYelp2XfdtJb5TsCP+QiZ+0Bm4U3ZfUswN8oN9O/l86XO/OvBSFCmWY7w75fzsQvZ50eGkFN34A==
"@types/react-test-renderer@^18.0.0":
version "18.0.0"
resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz#7b7f69ca98821ea5501b21ba24ea7b6139da2243"
integrity sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ==
dependencies:
"@types/react" "^16"
"@types/react" "*"
"@types/react@*", "@types/react@^16", "@types/react@^17":
"@types/react@*", "@types/react@^17":
version "17.0.52"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.52.tgz#10d8b907b5c563ac014a541f289ae8eaa9bf2e9b"
integrity sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==
@ -4601,10 +4601,10 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
i18next@^22.4.9:
version "22.4.9"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.9.tgz#98c8384c6bd41ff937da98b1e809ba03d3b41053"
integrity sha512-8gWMmUz460KJDQp/ob3MNUX84cVuDRY9PLFPnV8d+Qezz/6dkjxwOaH70xjrCNDO+JrUL25iXfAIN9wUkInNZw==
i18next@^22.4.10:
version "22.4.11"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-22.4.11.tgz#8b6c9be95176de90d3f10a78af125d95d3a3258d"
integrity sha512-ShfTzXVMjXdF2iPiT/wbizOrssLh9Ab6VpuVROihLCAu+u25KbZiEYVgsA0W6g0SgjPa/JmGWcUEV/g6cKzEjQ==
dependencies:
"@babel/runtime" "^7.20.6"
@ -6944,15 +6944,15 @@ react-freeze@^1.0.0:
resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.3.tgz#5e3ca90e682fed1d73a7cb50c2c7402b3e85618d"
integrity sha512-ZnXwLQnGzrDpHBHiC56TXFXvmolPeMjTn1UOm610M4EXGzbEDR7oOIyS2ZiItgbs6eZc4oU/a0hpk8PrcKvv5g==
react-i18next@^12.1.5:
version "12.1.5"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.1.5.tgz#b65f5733dd2f96188a9359c009b7dbe27443f009"
integrity sha512-7PQAv6DA0TcStG96fle+8RfTwxVbHVlZZJPoEszwUNvDuWpGldJmNWa3ZPesEsZQZGF6GkzwvEh6p57qpFD2gQ==
react-i18next@^12.2.0:
version "12.2.0"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-12.2.0.tgz#010e3f6070b8d700442947233352ebe4b252d7a1"
integrity sha512-5XeVgSygaGfyFmDd2WcXvINRw2WEC1XviW1LXY/xLOEMzsCFRwKqfnHN+hUjla8ZipbVJR27GCMSuTr0BhBBBQ==
dependencies:
"@babel/runtime" "^7.20.6"
html-parse-stringify "^3.0.1"
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.1.0:
"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
@ -7018,10 +7018,10 @@ react-native-gradle-plugin@^0.70.3:
resolved "https://registry.yarnpkg.com/react-native-gradle-plugin/-/react-native-gradle-plugin-0.70.3.tgz#cbcf0619cbfbddaa9128701aa2d7b4145f9c4fc8"
integrity sha512-oOanj84fJEXUg9FoEAQomA8ISG+DVIrTZ3qF7m69VQUJyOGYyDZmPqKcjvRku4KXlEH6hWO9i4ACLzNBh8gC0A==
react-native-image-picker@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-5.0.1.tgz#c9e99217396bc82a977785e39e14afb4819e8448"
integrity sha512-+poQTHOnEGrbxJnut591XA9006svFOyfPg/i5bv+fLuwoSHh5HW0E/PVhvT8lbX0Z5C108vh3DAsnrfFFnPBGw==
react-native-image-picker@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/react-native-image-picker/-/react-native-image-picker-5.1.0.tgz#78b84533a52a8e9704bacf6d18e7c979d9766271"
integrity sha512-p506trG3PDSrdm+Yd0QolsnR3FtSe37gkP7OVsMa9UYwn1teaLGzhmx3QMZ4ZBwdV1AKBUFA5HF1ECAVSDTdQQ==
react-native-multithreading@^1.1.1:
version "1.1.1"
@ -7199,14 +7199,14 @@ react-shallow-renderer@^16.15.0:
object-assign "^4.1.1"
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
react-test-renderer@18.1.0:
version "18.1.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.1.0.tgz#35b75754834cf9ab517b6813db94aee0a6b545c3"
integrity sha512-OfuueprJFW7h69GN+kr4Ywin7stcuqaYAt1g7airM5cUgP0BoF5G5CXsPGmXeDeEkncb2fqYNECO4y18sSqphg==
react-test-renderer@18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
dependencies:
react-is "^18.1.0"
react-is "^18.2.0"
react-shallow-renderer "^16.15.0"
scheduler "^0.22.0"
scheduler "^0.23.0"
react@18.2.0:
version "18.2.0"
@ -7511,6 +7511,13 @@ scheduler@^0.22.0:
dependencies:
loose-envify "^1.1.0"
scheduler@^0.23.0:
version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies:
loose-envify "^1.1.0"
secp256k1@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303"