Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reconnect chat #86

Merged
merged 15 commits into from
Apr 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"icon": "./assets/images/icon.jpg",
"scheme": "arcade",
"userInterfaceStyle": "dark",
"backgroundColor": "#000000",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "cover",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,11 @@
"expo-system-ui": "~2.2.1",
"expo-web-browser": "~12.1.1",
"i18n-js": "3.9.2",
"js-lnurl": "0.5.1",
"lodash": "^4.17.21",
"luxon": "3.3.0",
"moment": "^2.29.4",
"nostr-relaypool": "0.2.1",
"nostr-tools": "1.8.1",
"ramda": "0.29.0",
"react": "18.2.0",
Expand Down
5 changes: 5 additions & 0 deletions src/@types/globals.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { FC, ReactNode } from 'react'

declare global {
type FCC<T = object> = FC<{ children?: ReactNode } & T>
}
8 changes: 8 additions & 0 deletions src/@types/navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Channel } from 'stores/chat'

export type StackNavigatorParams = {
home: undefined
create: undefined
login: undefined
channel: { channel: Channel }
}
31 changes: 31 additions & 0 deletions src/@types/rnw-overrides.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// override react-native types with react-native-web types
import 'react-native'

declare module 'react-native' {
interface PressableStateCallbackType {
hovered?: boolean
focused?: boolean
}
interface ViewStyle {
transitionProperty?: string
transitionDuration?: string
}
interface TextProps {
accessibilityComponentType?: never
accessibilityTraits?: never
href?: string
hrefAttrs?: {
rel: 'noreferrer'
target?: '_blank'
}
}
interface ViewProps {
accessibilityRole?: string
href?: string
hrefAttrs?: {
rel: 'noreferrer'
target?: '_blank'
}
onClick?: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => void
}
}
4 changes: 2 additions & 2 deletions src/lib/api/getApiToken.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// import * as Crypto from 'expo-crypto'
import * as storage from 'app/lib/storage'
import axios from 'axios'
// import * as Crypto from 'expo-crypto'
import * as storage from 'lib/storage'
import { randomFourLetterString } from 'lib/utils'
import * as secp256k1 from '@noble/secp256k1'
import { API_URL } from './url'
Expand Down
1 change: 1 addition & 0 deletions src/lib/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export * from './useAuthed'
export * from './useChannelMessages'
export * from './useInterval'
export * from './useLongPress'
export * from './useNostr'
export * from './useTheme'
17 changes: 17 additions & 0 deletions src/lib/hooks/useNostr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect } from 'react'
import { useStore } from 'stores'
import { Nostr } from '../nostr'

export const useNostr = () => {
const nostr = useStore((s) => s.nostr)

// If nostr is undefined, create new Nostr object. Do it only once.
useEffect(() => {
if (!nostr) {
const newNostr = new Nostr()
useStore.setState({ nostr: newNostr })
}
}, [nostr])

return nostr
}
144 changes: 144 additions & 0 deletions src/lib/hooks/useNostrHook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import {
Event as NostrEvent, Filter, handleEvent, Kind, validateEvent
} from 'lib/nostr'
import { RelayPool, RelayPoolSubscription } from 'nostr-relaypool'
import {
Event, generatePrivateKey, getEventHash, getPublicKey, signEvent
} from 'nostr-tools'
import { useEffect, useState } from 'react'
import { useStore } from 'stores'
import { initialSubscriptions } from 'views/chat/initialSubscriptions'
import { DEFAULT_RELAYS } from '../constants/relays'

export function useNostr(
publicKey: string | undefined = undefined,
privateKey: string | undefined = undefined,
relays: string[] = DEFAULT_RELAYS
) {
const [relayPool, setRelayPool] = useState(new RelayPool(relays))
const [subscription, setSubscription] =
useState<RelayPoolSubscription | null>(null)

const [privateKeyState, setPrivateKey] = useState(
privateKey ?? generatePrivateKey()
)
const [publicKeyState, setPublicKey] = useState(
publicKey ?? getPublicKey(privateKeyState)
)

useEffect(() => {
setRelayPool(new RelayPool(relays))
}, [relays])

const [friendList, setFriendList] = useState<any>([])

// useEffect(() => {
// setFriendList(getFriendList())
// }, [getFriendList])

// function getFriendList(): string[] {
// return useStore.getState().friends
// }

function loadFirstPaint() {
// Grab friendlist and add self to it
const friends = friendList
console.log('friends:', friends)
friends.push(publicKeyState)

// We want contact metadata of our friends
const contactsFilters: Filter[] = [{ kinds: [0], authors: friends }]

// We want our contact list
const ourContactsFilters: Filter[] = [
{
kinds: [Kind.Contacts, Kind.Metadata],
authors: [publicKeyState],
},
]

// We want our DMs
const dmsFilters: Filter[] = [
{
kinds: [Kind.EncryptedDirectMessage],
limit: 500,
authors: [publicKeyState],
},
]

const homeFilters: Filter[] = [
{
kinds: [Kind.Text, Kind.ChannelMessage, Kind.Repost, Kind.Reaction],
authors: friends,
limit: 100,
// limit: 500,
},
]

// TODO add support for throwing these to specific relay
subscribe(contactsFilters)
subscribe(ourContactsFilters)
subscribe(dmsFilters)
subscribe(homeFilters)

return true
}

function setupInitialSubscriptions() {
const sub = relayPool.sub(initialSubscriptions, relays)
const chatActions = useStore.getState().chatActions
const addEvent = useStore.getState().addEvent
sub.onevent((event: NostrEvent) => {
handleEvent(event, {
addChannel: chatActions.addChannel,
addEvent,
addMessage: chatActions.addMessage,
})
})
setSubscription(sub)
return sub
}

function subscribe(filters: Filter[]) {
relayPool.sub(filters, relays)
}

function publish(event: NostrEvent): boolean {
try {
if (!event.id) {
event.id = getEventHash(event as Event)
}
if (!event.sig) {
if (!privateKeyState) {
throw new Error('Cannot sign event, private key not set')
}
event.sig = signEvent(event as Event, privateKeyState)
}
if (!validateEvent(event)) {
throw new Error('Invalid event')
}
relayPool.publish(event as Event, relays)
return true
} catch (e: any) {
console.log(e)
return false
}
}

function setKeys(publicKey: string, privateKey: string) {
setPublicKey(publicKey)
setPrivateKey(privateKey)
}

return {
loadFirstPaint,
setupInitialSubscriptions,
subscribe,
publish,
setKeys,
publicKey: publicKeyState,
privateKey: privateKeyState,
relayPool,
subscription,
}
}
4 changes: 2 additions & 2 deletions src/lib/hooks/useUserMetadataForMessages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ChannelMessage } from 'app/stores/types'
import { SimplePool } from 'nostr-tools'
import { useState, useEffect, useMemo } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { ChannelMessage } from 'stores/types'
import { generateRandomPlacekitten } from '../utils'

const relays = [
Expand Down
Loading