From 7f92204af480546a29cb3f16432eec7e0fc75cd0 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 11:00:21 -0400 Subject: [PATCH 01/15] RootNavigator remove flicker --- src/navigation/RootNavigator.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/navigation/RootNavigator.tsx b/src/navigation/RootNavigator.tsx index 56082016..0bddfdbd 100644 --- a/src/navigation/RootNavigator.tsx +++ b/src/navigation/RootNavigator.tsx @@ -11,6 +11,7 @@ import { chatClient, useStreamChatTheme } from 'lib/hooks' import { useAuthed } from 'lib/hooks/useAuthed' import { Chat, OverlayProvider } from 'stream-chat-expo' +import { YStack } from 'tamagui' import { NavigationContainer } from '@react-navigation/native' import { AuthNavigator } from './AuthNavigator' import { MainNavigator } from './MainNavigator' @@ -19,8 +20,10 @@ import { NavigationProps } from './types' export const RootNavigator = (props: NavigationProps) => { const authed = useAuthed() - console.log('authed:', authed) const theme = useStreamChatTheme() + if (!authed) { + return + } return ( From 97d854b522dbd0efab5be84db4bca47503ebec55 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 11:05:53 -0400 Subject: [PATCH 02/15] Axe stream setup for now --- src/navigation/RootNavigator.tsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/navigation/RootNavigator.tsx b/src/navigation/RootNavigator.tsx index 0bddfdbd..272d41f1 100644 --- a/src/navigation/RootNavigator.tsx +++ b/src/navigation/RootNavigator.tsx @@ -8,9 +8,7 @@ * https://reactnavigation.org/docs/modal */ -import { chatClient, useStreamChatTheme } from 'lib/hooks' import { useAuthed } from 'lib/hooks/useAuthed' -import { Chat, OverlayProvider } from 'stream-chat-expo' import { YStack } from 'tamagui' import { NavigationContainer } from '@react-navigation/native' import { AuthNavigator } from './AuthNavigator' @@ -20,17 +18,12 @@ import { NavigationProps } from './types' export const RootNavigator = (props: NavigationProps) => { const authed = useAuthed() - const theme = useStreamChatTheme() - if (!authed) { + if (authed === null) { return } return ( - - - {authed ? : } - - + {authed ? : } ) } From 3071206ce5c6a4a860a34cf26f638791ece28efb Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 11:05:58 -0400 Subject: [PATCH 03/15] bg color black --- app.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app.json b/app.json index 64989977..c0bb01a1 100644 --- a/app.json +++ b/app.json @@ -7,6 +7,7 @@ "icon": "./assets/images/icon.jpg", "scheme": "arcade", "userInterfaceStyle": "dark", + "backgroundColor": "#000000", "splash": { "image": "./assets/images/splash.png", "resizeMode": "cover", From 84a1a2c1d48bece3d7002362b7698b738c1c407f Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 11:07:20 -0400 Subject: [PATCH 04/15] disable spellcheck etc on signup fields --- src/views/auth/CreateAccountScreen.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/views/auth/CreateAccountScreen.tsx b/src/views/auth/CreateAccountScreen.tsx index 2c4d25f5..124b3044 100644 --- a/src/views/auth/CreateAccountScreen.tsx +++ b/src/views/auth/CreateAccountScreen.tsx @@ -56,6 +56,9 @@ export const CreateAccountScreen = () => { onChangeText={(text) => { setUsername(text) }} + spellCheck={false} + autoCorrect={false} + autoCapitalize="none" /> @@ -74,6 +77,9 @@ export const CreateAccountScreen = () => { width="100%" value={displayName} onChangeText={(text) => setDisplayName(text)} + spellCheck={false} + autoCorrect={false} + autoCapitalize="none" /> @@ -92,6 +98,9 @@ export const CreateAccountScreen = () => { width="100%" value={about} onChangeText={(text) => setAbout(text)} + spellCheck={false} + autoCorrect={false} + autoCapitalize="none" /> + ) +} diff --git a/src/views/chat/ChannelScreen.tsx b/src/views/chat/ChannelScreen.tsx new file mode 100644 index 00000000..faf547c5 --- /dev/null +++ b/src/views/chat/ChannelScreen.tsx @@ -0,0 +1,46 @@ +import { RouteProp, useNavigation } from '@react-navigation/native' +import { NativeStackNavigationProp } from '@react-navigation/native-stack' +import { Channel } from 'app/stores/chat' +import { Screen } from 'views/shared' +import { useEffect } from 'react' +import { ActivityIndicator } from 'react-native' + +import { MessageInput } from './MessageInput' +import { MessageList } from './MessageList' +import { useUserMetadataForChannel } from './useUserMetadataForChannel' + +type ChannelScreenProps = { + navigation: NativeStackNavigationProp + route: RouteProp +} + +export const ChannelScreen = ({ navigation, route }: ChannelScreenProps) => { + const { channel } = route.params + const { setOptions } = useNavigation() + + useUserMetadataForChannel(channel?.id ?? '') + + useEffect(() => { + setOptions({ title: channel?.metadata.name ?? 'Unnamed Channel' }) + }, [channel]) + + if (!channel) + return ( + + + + ) + return ( + + + + + ) +} + +export type StackNavigatorParams = { + home: undefined + create: undefined + login: undefined + channel: { channel: Channel } +} diff --git a/src/views/chat/ChannelsScreen.tsx b/src/views/chat/ChannelsScreen.tsx new file mode 100644 index 00000000..0cc298aa --- /dev/null +++ b/src/views/chat/ChannelsScreen.tsx @@ -0,0 +1,13 @@ +import { Separator, YGroup, YStack } from 'tamagui' + +import { ChannelList } from './ChannelList' + +export function ChannelsScreen() { + return ( + + }> + + + + ) +} diff --git a/src/views/chat/Message.tsx b/src/views/chat/Message.tsx new file mode 100644 index 00000000..567f12cc --- /dev/null +++ b/src/views/chat/Message.tsx @@ -0,0 +1,88 @@ +import { formatTimestamp, truncateString } from 'app/lib/utils' +import { useStore } from 'app/stores' +import { ChatMessage } from 'app/stores/chat' +import { Image, View } from 'react-native' +import { Paragraph, XStack, YStack } from 'tamagui' +import { useUserMetadata } from './useUserMetadata' + +type Props = { + message: ChatMessage +} + +export const Message: React.FC = ({ message }) => { + const currentUser = useStore((state) => state.user.publicKey) + const userMetadata = useUserMetadata(message.sender) + const align = message.sender === currentUser ? 'flex-end' : 'flex-start' + const isCurrentUser = message.sender === currentUser + const pic = isCurrentUser + ? 'https://placekitten.com/201/201' + : 'https://placekitten.com/200/200' + return ( + + {isCurrentUser ? ( + + ) : ( + + )} + + + {userMetadata?.name ?? truncateString(message.sender, 10)} + + + {message.text} + + + {formatTimestamp(message.timestamp)} + + + {isCurrentUser ? ( + + ) : ( + <> + )} + + ) +} diff --git a/src/views/chat/MessageInput.tsx b/src/views/chat/MessageInput.tsx new file mode 100644 index 00000000..eb56be92 --- /dev/null +++ b/src/views/chat/MessageInput.tsx @@ -0,0 +1,43 @@ +import { Send } from '@tamagui/lucide-icons' +import { useStore } from 'app/stores' +import { useRef, useState } from 'react' +import { Alert, TextInput, TouchableOpacity } from 'react-native' +import { Input, XStack } from 'tamagui' + +export const MessageInput = ({ channelId }) => { + const [text, setText] = useState('') + const inputBoxRef = useRef(null) + const actions = useStore((state) => state.chatActions) + + const submitInput = () => { + if (text.length < 1) { + Alert.alert('Message too short', 'What is that, a message for ants?') + return + } + inputBoxRef.current?.clear() + inputBoxRef.current?.blur() + setText('') + setTimeout(() => { + actions.sendMessage(text, channelId) + }, 100) + } + + return ( + + setText(text)} + ref={inputBoxRef} + spellCheck={false} + fg={1} + fs={1} + /> + + + + + ) +} diff --git a/src/views/chat/MessageList.tsx b/src/views/chat/MessageList.tsx new file mode 100644 index 00000000..afb6f754 --- /dev/null +++ b/src/views/chat/MessageList.tsx @@ -0,0 +1,22 @@ +import * as React from 'react' +import { FlatList, View } from 'react-native' + +import { Message } from './Message' +import { useMessagesForChannel } from './useMessagesForChannel' + +type Props = { + channelId: string +} + +export const MessageList: React.FC = ({ channelId }) => { + const messages = useMessagesForChannel(channelId) + return ( + } + keyExtractor={(item) => item.id} + style={{ paddingHorizontal: 10 }} + ListFooterComponent={} + /> + ) +} diff --git a/src/views/chat/index.ts b/src/views/chat/index.ts new file mode 100644 index 00000000..31691590 --- /dev/null +++ b/src/views/chat/index.ts @@ -0,0 +1,3 @@ +export * from './ChannelPreview' +export * from './ChannelScreen' +export * from './ChannelsScreen' diff --git a/src/views/chat/useChannels.ts b/src/views/chat/useChannels.ts new file mode 100644 index 00000000..a81087af --- /dev/null +++ b/src/views/chat/useChannels.ts @@ -0,0 +1,6 @@ +import { useStore } from 'app/stores' + +export const useChannels = () => { + const channels = useStore((s) => s.channels) + return channels +} diff --git a/src/views/chat/useMessages.ts b/src/views/chat/useMessages.ts new file mode 100644 index 00000000..e19a09e4 --- /dev/null +++ b/src/views/chat/useMessages.ts @@ -0,0 +1,6 @@ +import { useStore } from 'app/stores' + +export const useMessages = () => { + const messages = useStore((s) => s.messages) + return messages +} diff --git a/src/views/chat/useMessagesForChannel.ts b/src/views/chat/useMessagesForChannel.ts new file mode 100644 index 00000000..893598c9 --- /dev/null +++ b/src/views/chat/useMessagesForChannel.ts @@ -0,0 +1,23 @@ +/* eslint-disable radix */ +import { useNostr } from 'lib/hooks' +import { useEffect } from 'react' +import { useStore } from 'stores' + +export const useMessagesForChannel = (channelId: string) => { + const nostr = useNostr() + const messages = useStore((state) => state.messages) + + useEffect(() => { + if (!nostr) return + const sub = nostr.subscribeToChannel(channelId) + + return () => { + console.log(`closing subscriptions for ${channelId}`) + sub.unsub() + } + }, [channelId, nostr]) + + return messages + .filter((message) => message.channelId === channelId) + .sort((a, b) => parseInt(a.timestamp) - parseInt(b.timestamp)) +} diff --git a/src/views/chat/useUserMetadata.ts b/src/views/chat/useUserMetadata.ts new file mode 100644 index 00000000..69ea9506 --- /dev/null +++ b/src/views/chat/useUserMetadata.ts @@ -0,0 +1,10 @@ +import { useStore } from 'app/stores' + +export const useUserMetadata = (pubkey: string) => { + const userMetadata = useStore((s) => s.userMetadata) + let metadata: any = null + if (userMetadata.has(pubkey)) { + metadata = userMetadata.get(pubkey) + } + return metadata +} diff --git a/src/views/chat/useUserMetadataForChannel.ts b/src/views/chat/useUserMetadataForChannel.ts new file mode 100644 index 00000000..7a0923af --- /dev/null +++ b/src/views/chat/useUserMetadataForChannel.ts @@ -0,0 +1,22 @@ +import { useStore } from 'app/stores' +import { useEffect } from 'react' +import { useMessagesForChannel } from './useMessagesForChannel' + +export const useUserMetadataForChannel = (channelId: string) => { + if (channelId === '') return + const { fetchUser } = useStore((s) => s.chatActions) + const userMetadata = useStore((s) => s.userMetadata) + const messages = useMessagesForChannel(channelId) + + useEffect(() => { + // Extract the list of unique public keys of the senders of the messages + const uniquePubkeys = [...new Set(messages.map((message) => message.sender))] + + // Now fetch metadata for each pubkey + uniquePubkeys.forEach((pubkey) => { + if (!userMetadata.has(pubkey)) { + fetchUser(pubkey) + } + }) + }, [messages]) +} diff --git a/src/views/shared/NavHeader.tsx b/src/views/shared/NavHeader.tsx new file mode 100644 index 00000000..b2964190 --- /dev/null +++ b/src/views/shared/NavHeader.tsx @@ -0,0 +1,61 @@ +import { Button, Paragraph, Stack, XStack, YStack } from 'tamagui' +import { useNavigation, useRoute } from '@react-navigation/native' +import { ChevronLeft, Settings } from '@tamagui/lucide-icons' + +export const NavHeader = ({ title, options, ...props }) => { + const { canGoBack, goBack } = useNavigation() + const { name } = useRoute() + + return ( + + + {canGoBack() && name !== 'tabs' ? ( + + ) : ( + + )} + + + {options?.title ?? title} + + + {/* If current title is not Settings */} + {name !== 'settifsdfsdngs' ? ( + + ) : ( + + )} + + + ) +} diff --git a/src/views/shared/index.ts b/src/views/shared/index.ts index 52608415..3905a22a 100644 --- a/src/views/shared/index.ts +++ b/src/views/shared/index.ts @@ -2,6 +2,7 @@ export * from './BackButton' export * from './Button' export * from './LogoutDialog' export * from './NavButton' +export * from './NavHeader' export * from './Screen' export * from './TabBar' export * from './TerminalText' From 20e5d824eb0ee8491bd8171c8f0dab0d7cb2e143 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 12:09:32 -0400 Subject: [PATCH 09/15] axe old navigators --- src/navigation/old/MainNavigator.tsx | 16 --------- src/navigation/old/RootStack.tsx | 17 ---------- src/navigation/old/StreamNavigator.tsx | 23 ------------- src/navigation/old/TabNavigator.tsx | 47 -------------------------- 4 files changed, 103 deletions(-) delete mode 100644 src/navigation/old/MainNavigator.tsx delete mode 100644 src/navigation/old/RootStack.tsx delete mode 100644 src/navigation/old/StreamNavigator.tsx delete mode 100644 src/navigation/old/TabNavigator.tsx diff --git a/src/navigation/old/MainNavigator.tsx b/src/navigation/old/MainNavigator.tsx deleted file mode 100644 index 4a4ef6f4..00000000 --- a/src/navigation/old/MainNavigator.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack' -import { TabNavigator } from './TabNavigator' - -const Stack = createNativeStackNavigator() - -export function MainNavigator() { - return ( - - - - ) -} diff --git a/src/navigation/old/RootStack.tsx b/src/navigation/old/RootStack.tsx deleted file mode 100644 index 238c7203..00000000 --- a/src/navigation/old/RootStack.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack' -import { MainNavigator } from './MainNavigator' -import { hideHeaderOptions } from './navigation-utilities' - -const Stack = createNativeStackNavigator() - -export const RootStack = () => { - return ( - - - - ) -} diff --git a/src/navigation/old/StreamNavigator.tsx b/src/navigation/old/StreamNavigator.tsx deleted file mode 100644 index bcb963f8..00000000 --- a/src/navigation/old/StreamNavigator.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ChannelScreen } from 'views/chat/channel-screen' -import { StreamHome } from 'views/chat/stream-home' -import { createNativeStackNavigator } from '@react-navigation/native-stack' -import { stackOptions } from './stackOptions' -import { StreamNavigatorParamList } from './types' - -const Stack = createNativeStackNavigator() - -export const StreamNavigator = () => ( - - - - {/* */} - -) diff --git a/src/navigation/old/TabNavigator.tsx b/src/navigation/old/TabNavigator.tsx deleted file mode 100644 index 9f37125f..00000000 --- a/src/navigation/old/TabNavigator.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { BlankScreen } from 'views/dev' -import { FeedScreen } from 'views/feed/FeedScreen' -import { ProfileScreen } from 'views/profile/ProfileScreen' -import { TabBar } from 'views/shared' -import { WalletScreen } from 'views/wallet/WalletScreen' -import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' -import { hideHeaderOptions } from './navigation-utilities' -import { stackOptions } from './stackOptions' -import { StreamNavigator } from './StreamNavigator' - -const Tab = createBottomTabNavigator() - -export const TabNavigator = () => { - return ( - } - > - - - - - - - ) -} From 756ecf0e6e1903e3752ed350980b3cc24d75198b Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 12:22:04 -0400 Subject: [PATCH 10/15] rename stuff and black backgrounds --- src/lib/api/getApiToken.ts | 4 ++-- src/lib/hooks/useUserMetadataForMessages.ts | 4 ++-- src/lib/nostr/handleEvent.ts | 2 +- src/navigation/ChatNavigator.tsx | 7 ++++--- src/navigation/TabNavigator.tsx | 7 ++++++- src/views/chat/ChannelList.tsx | 9 ++++----- src/views/chat/ChannelPreview.tsx | 4 ++-- src/views/chat/ChannelScreen.tsx | 9 ++++----- src/views/chat/ChannelsScreen.tsx | 11 ++++++++--- src/views/chat/Message.tsx | 6 +++--- src/views/chat/MessageInput.tsx | 4 ++-- src/views/chat/useChannels.ts | 2 +- src/views/chat/useMessages.ts | 2 +- src/views/chat/useUserMetadata.ts | 2 +- src/views/chat/useUserMetadataForChannel.ts | 6 ++++-- src/views/dev/BlankScreen.tsx | 2 +- 16 files changed, 46 insertions(+), 35 deletions(-) diff --git a/src/lib/api/getApiToken.ts b/src/lib/api/getApiToken.ts index f646f633..f244fcf0 100644 --- a/src/lib/api/getApiToken.ts +++ b/src/lib/api/getApiToken.ts @@ -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' diff --git a/src/lib/hooks/useUserMetadataForMessages.ts b/src/lib/hooks/useUserMetadataForMessages.ts index e37eef81..632c8643 100644 --- a/src/lib/hooks/useUserMetadataForMessages.ts +++ b/src/lib/hooks/useUserMetadataForMessages.ts @@ -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 = [ diff --git a/src/lib/nostr/handleEvent.ts b/src/lib/nostr/handleEvent.ts index 7a56d9ba..353c6ddf 100644 --- a/src/lib/nostr/handleEvent.ts +++ b/src/lib/nostr/handleEvent.ts @@ -1,6 +1,6 @@ /* eslint-disable no-case-declarations */ -import { Channel, ChatMessage } from 'app/stores/chat' +import { Channel, ChatMessage } from 'stores/chat' export const handleEvent = ( event: any, diff --git a/src/navigation/ChatNavigator.tsx b/src/navigation/ChatNavigator.tsx index 3a285e11..6b19b418 100644 --- a/src/navigation/ChatNavigator.tsx +++ b/src/navigation/ChatNavigator.tsx @@ -19,9 +19,10 @@ export function ChatNavigator() { options={{ title: 'Channels', animation: 'slide_from_right', - header: ({ options }) => ( - - ), + headerShown: false, + // header: ({ options }) => ( + // + // ), }} /> { component={BlankScreen} options={stackOptions} /> - + - }> + + } + backgroundColor="#000" + > diff --git a/src/views/chat/Message.tsx b/src/views/chat/Message.tsx index 567f12cc..d1d0e43b 100644 --- a/src/views/chat/Message.tsx +++ b/src/views/chat/Message.tsx @@ -1,7 +1,7 @@ -import { formatTimestamp, truncateString } from 'app/lib/utils' -import { useStore } from 'app/stores' -import { ChatMessage } from 'app/stores/chat' +import { formatTimestamp, truncateString } from 'lib/utils' import { Image, View } from 'react-native' +import { useStore } from 'stores' +import { ChatMessage } from 'stores/chat' import { Paragraph, XStack, YStack } from 'tamagui' import { useUserMetadata } from './useUserMetadata' diff --git a/src/views/chat/MessageInput.tsx b/src/views/chat/MessageInput.tsx index eb56be92..25a748fd 100644 --- a/src/views/chat/MessageInput.tsx +++ b/src/views/chat/MessageInput.tsx @@ -1,8 +1,8 @@ -import { Send } from '@tamagui/lucide-icons' -import { useStore } from 'app/stores' import { useRef, useState } from 'react' import { Alert, TextInput, TouchableOpacity } from 'react-native' +import { useStore } from 'stores' import { Input, XStack } from 'tamagui' +import { Send } from '@tamagui/lucide-icons' export const MessageInput = ({ channelId }) => { const [text, setText] = useState('') diff --git a/src/views/chat/useChannels.ts b/src/views/chat/useChannels.ts index a81087af..a90e9bda 100644 --- a/src/views/chat/useChannels.ts +++ b/src/views/chat/useChannels.ts @@ -1,4 +1,4 @@ -import { useStore } from 'app/stores' +import { useStore } from 'stores' export const useChannels = () => { const channels = useStore((s) => s.channels) diff --git a/src/views/chat/useMessages.ts b/src/views/chat/useMessages.ts index e19a09e4..7b3fb66a 100644 --- a/src/views/chat/useMessages.ts +++ b/src/views/chat/useMessages.ts @@ -1,4 +1,4 @@ -import { useStore } from 'app/stores' +import { useStore } from 'stores' export const useMessages = () => { const messages = useStore((s) => s.messages) diff --git a/src/views/chat/useUserMetadata.ts b/src/views/chat/useUserMetadata.ts index 69ea9506..47b88929 100644 --- a/src/views/chat/useUserMetadata.ts +++ b/src/views/chat/useUserMetadata.ts @@ -1,4 +1,4 @@ -import { useStore } from 'app/stores' +import { useStore } from 'stores' export const useUserMetadata = (pubkey: string) => { const userMetadata = useStore((s) => s.userMetadata) diff --git a/src/views/chat/useUserMetadataForChannel.ts b/src/views/chat/useUserMetadataForChannel.ts index 7a0923af..ce039374 100644 --- a/src/views/chat/useUserMetadataForChannel.ts +++ b/src/views/chat/useUserMetadataForChannel.ts @@ -1,5 +1,5 @@ -import { useStore } from 'app/stores' import { useEffect } from 'react' +import { useStore } from 'stores' import { useMessagesForChannel } from './useMessagesForChannel' export const useUserMetadataForChannel = (channelId: string) => { @@ -10,7 +10,9 @@ export const useUserMetadataForChannel = (channelId: string) => { useEffect(() => { // Extract the list of unique public keys of the senders of the messages - const uniquePubkeys = [...new Set(messages.map((message) => message.sender))] + const uniquePubkeys = [ + ...new Set(messages.map((message) => message.sender)), + ] // Now fetch metadata for each pubkey uniquePubkeys.forEach((pubkey) => { diff --git a/src/views/dev/BlankScreen.tsx b/src/views/dev/BlankScreen.tsx index 491dfaf5..863ed497 100644 --- a/src/views/dev/BlankScreen.tsx +++ b/src/views/dev/BlankScreen.tsx @@ -2,5 +2,5 @@ import { View } from 'react-native' import { palette } from 'views/theme' export const BlankScreen = () => { - return + return } From c878778b5565b582066fe51fe1883fe5d1d50034 Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 12:54:26 -0400 Subject: [PATCH 11/15] initial channels show --- package.json | 2 + src/@types/globals.d.ts | 5 ++ src/@types/navigation.d.ts | 8 ++ src/@types/rnw-overrides.d.ts | 31 +++++++ src/lib/hooks/index.ts | 1 + src/lib/hooks/useNostr.ts | 17 ++++ src/lib/hooks/useNostrHook.ts | 144 ++++++++++++++++++++++++++++++ src/lib/nostr/Nostr.ts | 153 ++++++++++++++++++++++++++++++++ src/lib/nostr/NostrEvent.ts | 120 +++++++++++++++++++++++++ src/lib/nostr/NostrLink.ts | 133 +++++++++++++++++++++++++++ src/lib/nostr/index.ts | 4 + src/navigation/TabNavigator.tsx | 7 ++ src/stores/event.ts | 20 +++++ src/stores/index.ts | 10 ++- tsconfig.json | 1 + yarn.lock | 98 ++++++++++++++++++-- 16 files changed, 746 insertions(+), 8 deletions(-) create mode 100644 src/@types/globals.d.ts create mode 100644 src/@types/navigation.d.ts create mode 100644 src/@types/rnw-overrides.d.ts create mode 100644 src/lib/hooks/useNostr.ts create mode 100644 src/lib/hooks/useNostrHook.ts create mode 100644 src/lib/nostr/Nostr.ts create mode 100644 src/lib/nostr/NostrEvent.ts create mode 100644 src/lib/nostr/NostrLink.ts create mode 100644 src/stores/event.ts diff --git a/package.json b/package.json index a33a3163..2bd7b277 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/@types/globals.d.ts b/src/@types/globals.d.ts new file mode 100644 index 00000000..0c26dd8a --- /dev/null +++ b/src/@types/globals.d.ts @@ -0,0 +1,5 @@ +import { FC, ReactNode } from 'react' + +declare global { + type FCC = FC<{ children?: ReactNode } & T> +} diff --git a/src/@types/navigation.d.ts b/src/@types/navigation.d.ts new file mode 100644 index 00000000..abdf49ac --- /dev/null +++ b/src/@types/navigation.d.ts @@ -0,0 +1,8 @@ +import { Channel } from 'app/stores/chat' + +type StackNavigatorParams = { + home: undefined + create: undefined + login: undefined + channel: { channel: Channel } +} diff --git a/src/@types/rnw-overrides.d.ts b/src/@types/rnw-overrides.d.ts new file mode 100644 index 00000000..6d2767cd --- /dev/null +++ b/src/@types/rnw-overrides.d.ts @@ -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) => void + } +} diff --git a/src/lib/hooks/index.ts b/src/lib/hooks/index.ts index a0637913..184cb6e7 100644 --- a/src/lib/hooks/index.ts +++ b/src/lib/hooks/index.ts @@ -5,4 +5,5 @@ export * from './useAuthed' export * from './useChannelMessages' export * from './useInterval' export * from './useLongPress' +export * from './useNostr' export * from './useTheme' diff --git a/src/lib/hooks/useNostr.ts b/src/lib/hooks/useNostr.ts new file mode 100644 index 00000000..e30611b6 --- /dev/null +++ b/src/lib/hooks/useNostr.ts @@ -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 +} diff --git a/src/lib/hooks/useNostrHook.ts b/src/lib/hooks/useNostrHook.ts new file mode 100644 index 00000000..665bd24c --- /dev/null +++ b/src/lib/hooks/useNostrHook.ts @@ -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(null) + + const [privateKeyState, setPrivateKey] = useState( + privateKey ?? generatePrivateKey() + ) + const [publicKeyState, setPublicKey] = useState( + publicKey ?? getPublicKey(privateKeyState) + ) + + useEffect(() => { + setRelayPool(new RelayPool(relays)) + }, [relays]) + + const [friendList, setFriendList] = useState([]) + + // 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, + } +} diff --git a/src/lib/nostr/Nostr.ts b/src/lib/nostr/Nostr.ts new file mode 100644 index 00000000..b037fb20 --- /dev/null +++ b/src/lib/nostr/Nostr.ts @@ -0,0 +1,153 @@ +import { RelayPool, RelayPoolSubscription } from 'nostr-relaypool' +import { + Event, generatePrivateKey, getEventHash, getPublicKey, signEvent +} from 'nostr-tools' +import { useStore } from 'stores' +import { initialSubscriptions } from 'views/chat/initialSubscriptions' +import { DEFAULT_RELAYS } from '../constants/relays' +import { handleEvent } from './handleEvent' +import { + Event as NostrEvent, Filter, Kind, validateEvent +} from './nip01_events' + +export class Nostr { + private relayPool: RelayPool + private relays: string[] + public publicKey: string + public privateKey: string + + constructor( + publicKey: string | undefined = undefined, + privateKey: string | undefined = undefined, + relays: string[] = DEFAULT_RELAYS + ) { + this.relays = relays + this.relayPool = new RelayPool(this.relays) + this.privateKey = privateKey ?? generatePrivateKey() + this.publicKey = publicKey ?? getPublicKey(this.privateKey) + } + + public getFriendList(): string[] { + return useStore.getState().friends + } + + public loadFirstPaint() { + // Grab friendlist and add self to it + const friends = this.getFriendList() + console.log('friends:', friends) + friends.push(this.publicKey) + + // 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: [this.publicKey], + }, + ] + + // We want our DMs + const dmsFilters: Filter[] = [ + { + kinds: [Kind.EncryptedDirectMessage], + limit: 500, + authors: [this.publicKey], + }, + ] + + const homeFilters: Filter[] = [ + { + kinds: [Kind.Text, Kind.ChannelMessage, Kind.Repost, Kind.Reaction], + authors: friends, + limit: 10, + // limit: 500, + }, + ] + + // TODO add support for throwing these to specific relay + this.subscribe(contactsFilters) + this.subscribe(ourContactsFilters) + this.subscribe(dmsFilters) + this.subscribe(homeFilters) + + return true + } + + public setupInitialSubscriptions() { + console.log('Trying to set up...') + const sub = this.relayPool.sub(initialSubscriptions, this.relays) + const chatActions = useStore.getState().chatActions + const addEvent = useStore.getState().addEvent + // console.log('Where we is?', addEvent) + + sub.onevent((event: NostrEvent) => { + console.log('An event.', event.id) + handleEvent(event, { + addChannel: chatActions.addChannel, + addEvent, + addMessage: chatActions.addMessage, + }) + }) + return sub + } + + public publish(event: NostrEvent): boolean { + try { + if (!event.id) { + event.id = getEventHash(event as Event) + } + if (!event.sig) { + if (!this.privateKey) { + throw new Error('Cannot sign event, private key not set') + } + event.sig = signEvent(event as Event, this.privateKey) + } + if (!validateEvent(event)) { + throw new Error('Invalid event') + } + this.relayPool.publish(event as Event, this.relays) + return true + } catch (e: any) { + console.log(e) + return false + } + } + + public setKeys(publicKey: string, privateKey: string): void { + this.publicKey = publicKey + this.privateKey = privateKey + } + + public subscribeToChannel(channelId: string): RelayPoolSubscription { + return this.subscribe([ + { + kinds: [42], + limit: 30, + '#e': [channelId], + }, + ]) + } + + public subscribe(filters: Filter[]): RelayPoolSubscription { + const sub = this.relayPool.sub(filters, this.relays) + const chatActions = useStore.getState().chatActions + const addEvent = useStore.getState().addEvent + sub.onevent((event: NostrEvent) => { + handleEvent(event, { + addChannel: chatActions.addChannel, + addEvent, + addMessage: chatActions.addMessage, + }) + }) + return sub + } + + public close(): void { + try { + this.relayPool.close() + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (e: any) {} + } +} diff --git a/src/lib/nostr/NostrEvent.ts b/src/lib/nostr/NostrEvent.ts new file mode 100644 index 00000000..2aa35c09 --- /dev/null +++ b/src/lib/nostr/NostrEvent.ts @@ -0,0 +1,120 @@ +// @ts-nocheck + +import { getEventHash, signEvent } from 'nostr-tools' + +enum ValidationResult { + ok, + bad_id, + bad_sig, +} + +interface OtherEvent { + event_id: string + relay_url: string +} + +interface KeyEvent { + key: string + relay_url: string +} + +interface ReferencedId { + ref_id: string + relay_id?: string + key: string +} + +interface EventId { + id: string +} + +export class NostrEvent { + id: string + sig: string + tags: string[][] + pubkey: string + created_at: number + kind: number + content: string + + boosted_by?: string + + constructor(content: string, pubkey: string, kind: number, tags: string[][]) { + this.content = content + this.pubkey = pubkey + this.kind = kind + this.tags = tags + this.created_at = Math.floor(Date.now() / 1000) // no + } + + sign(privkey: string) { + this.sig = signEvent(this, privkey) + } + + static equals(lhs: NostrEvent, rhs: NostrEvent): boolean { + return lhs.id === rhs.id + } + + static lessThan(lhs: NostrEvent, rhs: NostrEvent): boolean { + return lhs.created_at < rhs.created_at + } + + calculateId() { + this.id = getEventHash(this) + } + + // custom flags for internal use + flags: number = 0 + + get is_textlike(): boolean { + return this.kind === 1 || this.kind === 42 + } + + get too_big(): boolean { + return this.content.length > 32000 + } + + get should_show_event(): boolean { + return !this.too_big + } + + get is_valid_id(): boolean { + return calculate_event_id(this) === this.id + } + + get is_valid(): boolean { + return this.validity === ValidationResult.ok + } + + get validity(): ValidationResult { + return ValidationResult.ok //validate_event(this); + } + + private _blocks?: Block[] + blocks(privkey?: string): Block[] { + if (this._blocks) { + return this._blocks + } + const blocks = parse_mentions(this.get_content(privkey), this.tags) + this._blocks = blocks + return blocks + } + + get inner_event(): NostrEvent | undefined { + // don't try to deserialize an inner event if we know there won't be one + if (this.known_kind === NostrKind.boost) { + return event_from_json(this.content) + } + return undefined + } + + _event_refs?: EventRef[] + private event_refs(privkey?: string): EventRef[] { + if (this._event_refs) { + return this._event_refs + } + const refs = interpret_event_refs(this.blocks(privkey), this.tags) + this._event_refs = refs + return refs + } +} diff --git a/src/lib/nostr/NostrLink.ts b/src/lib/nostr/NostrLink.ts new file mode 100644 index 00000000..6352fd2f --- /dev/null +++ b/src/lib/nostr/NostrLink.ts @@ -0,0 +1,133 @@ +// @ts-nocheck + +import { parseChar, Parser, parseStr } from '../util/parser' + +type ReferencedId = { + refId: string + relayId: string | null + key: string +} + +export type NostrLink = { + type: string + value: ReferencedId | NostrFilter +} + +export const encodePubkeyUri = (ref: ReferencedId): string => { + return `p:${ref.refId}` +} + +export const encodeEventIdUri = (ref: ReferencedId): string => { + return `e:${ref.refId}` +} + +export const parseNostrRefUriType = (p: string): string | null => { + if (p[0] === 'p') { + return 'p' + } + + if (p[0] === 'e') { + return 'e' + } + + return null +} + +export const parseHexstr = (p: string, len: number): string | null => { + let i = 0 + + if (len % 2 !== 0) { + return null + } + + const start = p.pos + + while (i < len) { + if (!parseHexChar(p)) { + p.pos = start + return null + } + i += 1 + } + + return p.substring(start, p.pos) +} + +export const parseNostrRefUri = (p: string): ReferencedId | null => { + const start = p.pos + + if (!parseStr(p, 'nostr:')) { + return null + } + + const typ = parseNostrRefUriType(p) + if (!typ) { + p.pos = start + return null + } + + if (!parseChar(p, ':')) { + p.pos = start + return null + } + + const pk = parseHexstr(p, 64) + if (!pk) { + p.pos = start + return null + } + + return { refId: pk, relayId: null, key: typ } +} + +export const decodeUniversalLink = (s: string): NostrLink | null => { + let uri = s.replace('https://thearcapp.com/r/', '') + uri = uri.replace('https://thearcapp.com/', '') + uri = uri.replace('/', '') + + const decoded = bech32Decode(uri) + if (!decoded) { + return null + } + + const h = hexEncode(decoded.data) + + if (decoded.hrp === 'note') { + return { type: 'ref', value: { refId: h, relayId: null, key: 'e' } } + } else if (decoded.hrp === 'npub') { + return { type: 'ref', value: { refId: h, relayId: null, key: 'p' } } + } + // TODO: handle nprofile, etc + + return null +} + +export function decodeNostrUri(s: string): NostrLink | null { + if (s.startsWith('https://damus.io/')) { + return decodeUniversalLink(s) + } + + const uri = s.replace('nostr://', '').replace('nostr:', '') + + const parts = uri.split(':').reduce((acc, str) => { + const decoded = str.removingPercentEncoding + if (decoded) { + acc.push(decoded) + } + return acc + }, []) + + if (parts.length === 0) { + return null + } + + switch (parts[0]) { + case 'ref': + const p = new Parser(0, parts[1]) + return NostrLink.ref(parseNostrRefUri(p)) + case 'filter': + return NostrLink.filter(decodeNostrFilter(parts[1])) + default: + return null + } +} diff --git a/src/lib/nostr/index.ts b/src/lib/nostr/index.ts index b340594c..76073c27 100644 --- a/src/lib/nostr/index.ts +++ b/src/lib/nostr/index.ts @@ -1,6 +1,10 @@ export * from './checkRelayForEvent' export * from './dummydata' +export * from './handleEvent' export * from './lnurl' +export * from './nip01_events' +export * from './Nostr' +// export * from './NostrEvent' export * from './saveNewChannel' export * from './saveNewChannelMessage' export * from './saveZap' diff --git a/src/navigation/TabNavigator.tsx b/src/navigation/TabNavigator.tsx index 3e356462..402cd890 100644 --- a/src/navigation/TabNavigator.tsx +++ b/src/navigation/TabNavigator.tsx @@ -1,3 +1,5 @@ +import { useNostr } from 'lib/hooks' +import { useEffect } from 'react' import { BlankScreen } from 'views/dev' import { MainFeedScreen } from 'views/feed/MainFeedScreen' import { NotificationsScreen } from 'views/notifications/NotificationsScreen' @@ -15,6 +17,11 @@ import { stackOptions } from './stackOptions' const Tab = createBottomTabNavigator() export const TabNavigator = () => { + const nostr = useNostr() + useEffect(() => { + if (!nostr) return + nostr.setupInitialSubscriptions() + }, [nostr]) return ( ({ + events: initialEventsState.events, + addEvent: (event: NostrEvent) => { + set((state) => { + return { + events: [...state.events, event], + } + }) + }, +}) diff --git a/src/stores/index.ts b/src/stores/index.ts index 45bed632..e8275be3 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -1,18 +1,24 @@ +import { Nostr } from 'lib/nostr' import { create } from 'zustand' import { createAuthStore } from './auth' import { createChatStore } from './chat' +import { createEventsStore } from './event' import { createRelayStore } from './relay' import { createUiStore } from './ui' -export type UseStore = object & - ReturnType & +export type UseStore = { + nostr: Nostr | undefined +} & ReturnType & ReturnType & + ReturnType & ReturnType & ReturnType export const useStore = create((set, get) => ({ + nostr: undefined, ...createAuthStore(set, get), ...createChatStore(set, get), + ...createEventsStore(set, get), ...createRelayStore(set, get), ...createUiStore(set), })) diff --git a/tsconfig.json b/tsconfig.json index 85951cc3..647c5188 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "noImplicitAny": false, "baseUrl": "./", "paths": { + "@types/*": ["src/@types/*"], "i18n": ["src/i18n"], "i18n/*": ["src/i18n/*"], "lib": ["src/lib"], diff --git a/yarn.lock b/yarn.lock index 13e3d25d..91263967 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1923,12 +1923,17 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + "@noble/hashes@1.3.0", "@noble/hashes@~1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== -"@noble/secp256k1@1.7.0", "@noble/secp256k1@^1.7.1": +"@noble/secp256k1@1.7.0", "@noble/secp256k1@1.7.1", "@noble/secp256k1@^1.7.1", "@noble/secp256k1@~1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1" integrity sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw== @@ -2285,11 +2290,20 @@ resolved "https://registry.yarnpkg.com/@react-spring/types/-/types-9.7.2.tgz#e04dd72755d88b0e3163ba143ecd8ba78b68a5b0" integrity sha512-GEflx2Ex/TKVMHq5g5MxQDNNPNhqg+4Db9m7+vGTm8ttZiyga7YQUF24shgRNebKIjahqCuei16SZga8h1pe4g== -"@scure/base@^1.1.1", "@scure/base@~1.1.0": +"@scure/base@1.1.1", "@scure/base@^1.1.1", "@scure/base@~1.1.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/bip32@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.4.tgz#2c91a7be0156b15f26dd0c843a06a1917f129efd" + integrity sha512-m925ACYK0wPELsF7Z/VdLGmKj1StIeHraPMYB9xiAFiq/PnvqWd/99I0TQ2OZhjjlMDsDJeZlyXMWi0beaA7NA== + dependencies: + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" + "@scure/bip32@^1.1.5": version "1.3.0" resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" @@ -2299,6 +2313,14 @@ "@noble/hashes" "~1.3.0" "@scure/base" "~1.1.0" +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== + dependencies: + "@noble/hashes" "~1.2.0" + "@scure/base" "~1.1.0" + "@scure/bip39@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" @@ -3297,6 +3319,11 @@ resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== +"@types/aes-js@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/aes-js/-/aes-js-3.1.1.tgz#34b3978122310c135de4b377270d1d65676fae28" + integrity sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw== + "@types/babel__core@^7.1.14": version "7.20.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" @@ -3330,6 +3357,11 @@ dependencies: "@babel/types" "^7.3.0" +"@types/base64-js@^1.2.5": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/base64-js/-/base64-js-1.3.0.tgz#c939fdba49846861caf5a246b165dbf5698a317c" + integrity sha512-ZmI0sZGAUNXUfMWboWwi4LcfpoVUYldyN6Oe0oJ5cCsHDU/LlRq8nQKPXhYLOx36QYSW9bNIb1vvRrD6K7Llgw== + "@types/fs-extra@^9.0.13": version "9.0.13" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" @@ -3399,6 +3431,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== +"@types/node@^17.0.0": + version "17.0.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" + integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -3555,6 +3592,11 @@ acorn@^8.1.0, acorn@^8.5.0, acorn@^8.8.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +aes-js@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -3976,7 +4018,7 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" -bech32@1.1.4: +bech32@1.1.4, bech32@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== @@ -4137,7 +4179,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.4.3, buffer@^5.5.0: +buffer@^5.4.3, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -4627,7 +4669,7 @@ create-react-class@^15.7.0: loose-envify "^1.3.1" object-assign "^4.1.1" -cross-fetch@^3.1.5: +cross-fetch@^3.0.4, cross-fetch@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== @@ -6993,6 +7035,22 @@ join-component@^1.1.0: resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" integrity sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ== +js-lnurl@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/js-lnurl/-/js-lnurl-0.5.1.tgz#d755a2fbefa5dfd2764d65addee64253c60c6108" + integrity sha512-nNGiRInXfjw9EnzF/nfzqMCOeT2fqgyzB3iXuPSxktPa3UGV3HRHuhTSUid+G/GwblZj1o9y27gi6sdZTNO+7Q== + dependencies: + "@types/aes-js" "^3.1.1" + "@types/base64-js" "^1.2.5" + "@types/node" "^17.0.0" + aes-js "^3.1.2" + base64-js "^1.3.1" + bech32 "^1.1.4" + buffer "^5.6.0" + cross-fetch "^3.0.4" + query-string "^6.12.1" + safe-buffer "^5.2.1" + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -8038,6 +8096,13 @@ normalize-url@^2.0.1: query-string "^5.0.1" sort-keys "^2.0.0" +nostr-relaypool@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nostr-relaypool/-/nostr-relaypool-0.2.1.tgz#9dc4ce576199b922a7cdda05e20c8385992253eb" + integrity sha512-6clmhxkjxQ5gcKNR4uXGGO9h5VCcapgOgi8xfPUNQokAsoiTp5Jfet6eiBuMTG/ATzSKJ5jUP44mrq5vxNo0Xg== + dependencies: + nostr-tools "^1.1.0" + nostr-tools@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.8.1.tgz#4e54a354cc88ea0200634da3ee5a1c3466e1794c" @@ -8050,6 +8115,17 @@ nostr-tools@1.8.1: "@scure/bip39" "^1.1.1" prettier "^2.8.4" +nostr-tools@^1.1.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.1.tgz#b52043b3031f4314478d0a3bfaa8ffb9cc4f98a0" + integrity sha512-zgTYJeuZQ3CDASsmBEcB5i6V6l0IaA6cjnll6OVik3FoZcvbCaL7yP8I40hYnOIi3KlJykV7jEF9fn8h1NzMnA== + dependencies: + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@scure/base" "1.1.1" + "@scure/bip32" "1.1.4" + "@scure/bip39" "1.1.1" + npm-package-arg@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-7.0.0.tgz#52cdf08b491c0c59df687c4c925a89102ef794a5" @@ -8624,6 +8700,16 @@ query-string@^5.0.1: object-assign "^4.1.0" strict-uri-encode "^1.0.0" +query-string@^6.12.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + query-string@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" @@ -9288,7 +9374,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== From 0ea6ae4fa703474c73213e57cc25ea7d9feb5cbd Mon Sep 17 00:00:00 2001 From: Christopher David Date: Fri, 28 Apr 2023 13:05:53 -0400 Subject: [PATCH 12/15] ugly channels but yeah works --- src/@types/{navigation.d.ts => navigation.ts} | 4 ++-- src/lib/nostr/Nostr.ts | 4 ---- src/navigation/TabNavigator.tsx | 2 +- src/navigation/types.ts | 7 +++++++ src/views/chat/ChannelList.tsx | 2 +- 5 files changed, 11 insertions(+), 8 deletions(-) rename src/@types/{navigation.d.ts => navigation.ts} (55%) diff --git a/src/@types/navigation.d.ts b/src/@types/navigation.ts similarity index 55% rename from src/@types/navigation.d.ts rename to src/@types/navigation.ts index abdf49ac..f8e83573 100644 --- a/src/@types/navigation.d.ts +++ b/src/@types/navigation.ts @@ -1,6 +1,6 @@ -import { Channel } from 'app/stores/chat' +import { Channel } from 'stores/chat' -type StackNavigatorParams = { +export type StackNavigatorParams = { home: undefined create: undefined login: undefined diff --git a/src/lib/nostr/Nostr.ts b/src/lib/nostr/Nostr.ts index b037fb20..b31c0d02 100644 --- a/src/lib/nostr/Nostr.ts +++ b/src/lib/nostr/Nostr.ts @@ -76,14 +76,10 @@ export class Nostr { } public setupInitialSubscriptions() { - console.log('Trying to set up...') const sub = this.relayPool.sub(initialSubscriptions, this.relays) const chatActions = useStore.getState().chatActions const addEvent = useStore.getState().addEvent - // console.log('Where we is?', addEvent) - sub.onevent((event: NostrEvent) => { - console.log('An event.', event.id) handleEvent(event, { addChannel: chatActions.addChannel, addEvent, diff --git a/src/navigation/TabNavigator.tsx b/src/navigation/TabNavigator.tsx index 402cd890..c51e8835 100644 --- a/src/navigation/TabNavigator.tsx +++ b/src/navigation/TabNavigator.tsx @@ -25,7 +25,7 @@ export const TabNavigator = () => { return ( } > Date: Fri, 28 Apr 2023 13:12:59 -0400 Subject: [PATCH 13/15] chats and darker, fix topnavs --- src/navigation/ChatNavigator.tsx | 31 +++++++++++++++++-------------- src/navigation/TabNavigator.tsx | 2 +- src/navigation/stackOptions.tsx | 4 +++- src/views/chat/ChannelPreview.tsx | 2 +- src/views/chat/ChannelScreen.tsx | 4 ++-- src/views/chat/ChannelsScreen.tsx | 6 ++++++ 6 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/navigation/ChatNavigator.tsx b/src/navigation/ChatNavigator.tsx index 6b19b418..f0501681 100644 --- a/src/navigation/ChatNavigator.tsx +++ b/src/navigation/ChatNavigator.tsx @@ -2,6 +2,7 @@ import { Channel } from 'stores/chat' import { ChannelScreen, ChannelsScreen } from 'views/chat' import { NavHeader } from 'views/shared' import { createNativeStackNavigator } from '@react-navigation/native-stack' +import { stackOptions } from './stackOptions' const Stack = createNativeStackNavigator<{ // todo: fix the dupe w @types @@ -16,24 +17,26 @@ export function ChatNavigator() { ( - // - // ), - }} + options={stackOptions} + // options={{ + // title: 'Channels', + // animation: 'slide_from_right', + // // headerShown: false, + // header: ({ options }) => ( + // + // ), + // }} /> ( - - ), - }} + options={stackOptions} + // options={{ + // animation: 'slide_from_right', + // header: ({ options }) => ( + // + // ), + // }} /> ) diff --git a/src/navigation/TabNavigator.tsx b/src/navigation/TabNavigator.tsx index c51e8835..2afa4bd0 100644 --- a/src/navigation/TabNavigator.tsx +++ b/src/navigation/TabNavigator.tsx @@ -36,7 +36,7 @@ export const TabNavigator = () => { // ), headerLeft: () => - route.name == 'Relays' && , + (route.name == 'channel' || route.name == 'Relays') && ( + + ), } } diff --git a/src/views/chat/ChannelPreview.tsx b/src/views/chat/ChannelPreview.tsx index 607dfd60..3735be18 100644 --- a/src/views/chat/ChannelPreview.tsx +++ b/src/views/chat/ChannelPreview.tsx @@ -22,7 +22,7 @@ export const ChannelPreview = ({ channel, onPress }: ChannelPreviewProps) => {