Merge branch 'main' into dependabot/npm_and_yarn/types/react-native-vector-icons-6.4.13

This commit is contained in:
KoalaSat 2023-02-24 16:01:13 +00:00 committed by GitHub
commit 3d10ae6ae8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1085 additions and 736 deletions

View File

@ -18,15 +18,47 @@ jobs:
go install github.com/fiatjaf/noscl@latest go install github.com/fiatjaf/noscl@latest
~/go/bin/noscl setprivate '${{ secrets.NOSTROS_PRIVATE_KEY }}' ~/go/bin/noscl setprivate '${{ secrets.NOSTROS_PRIVATE_KEY }}'
~/go/bin/noscl relay add "wss://brb.io" ~/go/bin/noscl relay add "wss://brb.io"
~/go/bin/noscl relay add "wss://damus.io"
~/go/bin/noscl relay add "wss://nostr-pub.wellorder.net" ~/go/bin/noscl relay add "wss://nostr-pub.wellorder.net"
~/go/bin/noscl relay add "wss://nostr.swiss-enigma.ch"
~/go/bin/noscl relay add "wss://nostr.onsats.org"
~/go/bin/noscl relay add "wss://nostr-pub.semisol.dev"
~/go/bin/noscl relay add "wss://nostr.openchain.fr"
~/go/bin/noscl relay add "wss://relay.nostr.info"
~/go/bin/noscl relay add "wss://nostr.oxtr.dev"
~/go/bin/noscl relay add "wss://nostr.ono.re"
~/go/bin/noscl relay add "wss://relay.grunch.dev"
~/go/bin/noscl relay add "wss://nostr.developer.li" ~/go/bin/noscl relay add "wss://nostr.developer.li"
~/go/bin/noscl relay add "wss://nostr.oxtr.dev"
~/go/bin/noscl relay add "wss://nostr.swiss-enigma.ch"
~/go/bin/noscl relay add "wss://relay.nostr.snblago.com"
~/go/bin/noscl relay add "wss://nos.lol"
~/go/bin/noscl relay add "wss://relay.austrich.net"
~/go/bin/noscl relay add "wss://nostr.cro.social"
~/go/bin/noscl relay add "wss://relay.koreus.social"
~/go/bin/noscl relay add "wss://spore.ws"
~/go/bin/noscl relay add "wss://nostr.web3infra.xyz"
~/go/bin/noscl relay add "wss://nostr.snblago.com"
~/go/bin/noscl relay add "wss://relay.nostrified.org"
~/go/bin/noscl relay add "wss://relay.ryzizub.com"
~/go/bin/noscl relay add "wss://relay.wellorder.net"
~/go/bin/noscl relay add "wss://nostr.btcmp.com"
~/go/bin/noscl relay add "wss://relay.nostromo.social"
~/go/bin/noscl relay add "wss://relay.stoner.com"
~/go/bin/noscl relay add "wss://nostr.massmux.com"
~/go/bin/noscl relay add "wss://nostr.robotesc.ro"
~/go/bin/noscl relay add "wss://relay.humanumest.social"
~/go/bin/noscl relay add "wss://relay-local.cowdle.gg"
~/go/bin/noscl relay add "wss://nostr-2.afarazit.eu"
~/go/bin/noscl relay add "wss://nostr.data.haus"
~/go/bin/noscl relay add "wss://nostr-pub.wellorder.net"
~/go/bin/noscl relay add "wss://nostr.thank.eu"
~/go/bin/noscl relay add "wss://relay-dev.cowdle.gg"
~/go/bin/noscl relay add "wss://nostrsxz4lbwe-nostr.functions.fnc.fr-par.scw.cloud"
~/go/bin/noscl relay add "wss://relay.nostrcheck.me"
~/go/bin/noscl relay add "wss://relay.nostrich.de"
~/go/bin/noscl relay add "wss://nostr.com.de"
~/go/bin/noscl relay add "wss://relay.nostr.scot"
~/go/bin/noscl relay add "wss://nostr.8e23.net"
~/go/bin/noscl relay add "wss://nostr.mouton.dev"
~/go/bin/noscl relay add "wss://nostr.l00p.org"
~/go/bin/noscl relay add "wss://nostr.island.network"
~/go/bin/noscl relay add "wss://nostr.handyjunky.com"
~/go/bin/noscl relay add "wss://relay.valera.co"
~/go/bin/noscl relay add "wss://relay.nostr.vet"
~/go/bin/noscl relay add "wss://tmp-relay.cesc.trade"
~/go/bin/noscl relay add "wss://relay.dwadziesciajeden.pl"
~/go/bin/noscl relay add "wss://nostr-1.afarazit.eu"
~/go/bin/noscl relay add "wss://lbrygen.xyz"
~/go/bin/noscl publish "${{ github.event.release.body }}" ~/go/bin/noscl publish "${{ github.event.release.body }}"

View File

@ -44,6 +44,7 @@ Nostros chat
- [x] Global feed filters and blocked users - [x] Global feed filters and blocked users
- [x] Relay colouring - [x] Relay colouring
- [x] Push events to private relay - [x] Push events to private relay
- [x] Zaps
# Kudos # Kudos

View File

@ -139,8 +139,8 @@ android {
applicationId "com.nostros" applicationId "com.nostros"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 51 versionCode 53
versionName "v0.3.0.4-alpha" versionName "v0.3.0.6-alpha"
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
if (isNewArchitectureEnabled()) { if (isNewArchitectureEnabled()) {

View File

@ -183,7 +183,7 @@ public class Event {
String name = parts[0]; String name = parts[0];
String domain = parts[1]; String domain = parts[1];
if (!name.matches("^[a-z0-9-_]+$")) return false; if (!name.matches("^[a-zA-Z0-9-_]+$")) return false;
try { try {
String url = "https://" + domain + "/.well-known/nostr.json?name=" + name; String url = "https://" + domain + "/.well-known/nostr.json?name=" + name;
@ -575,6 +575,7 @@ public class Event {
values.put("active", 0); values.put("active", 0);
values.put("global_feed", 0); values.put("global_feed", 0);
values.put("manual", 1); values.put("manual", 1);
values.put("deleted_at", 0);
database.insert("nostros_relays", null, values); database.insert("nostros_relays", null, values);
} else if (cursor.moveToFirst() && created_at > cursor.getInt(0)) { } else if (cursor.moveToFirst() && created_at > cursor.getInt(0)) {
values.put("updated_at", created_at); values.put("updated_at", created_at);

View File

@ -52,14 +52,17 @@ public class Relay {
values.put("url", url); values.put("url", url);
values.put("active", active); values.put("active", active);
values.put("global_feed", globalFeed); values.put("global_feed", globalFeed);
values.put("deleted_at", 0);
database.replace("nostros_relays", null, values); database.replace("nostros_relays", null, values);
} }
public void destroy(SQLiteDatabase database) { public void delete(SQLiteDatabase database) {
String whereClause = "url = ?"; String whereClause = "url = ?";
String[] whereArgs = new String[] { String[] whereArgs = new String[] {
url url
}; };
database.delete ("nostros_relays", whereClause, whereArgs); ContentValues values = new ContentValues();
values.put("deleted_at", System.currentTimeMillis() / 1000L);
database.update ("nostros_relays", values, whereClause, whereArgs);
} }
} }

View File

@ -196,6 +196,9 @@ public class DatabaseModule {
" );"); " );");
database.execSQL("CREATE INDEX nostros_nostros_zaps_zapped_event_id_index ON nostros_zaps(zapped_event_id);"); database.execSQL("CREATE INDEX nostros_nostros_zaps_zapped_event_id_index ON nostros_zaps(zapped_event_id);");
} catch (SQLException e) { } } catch (SQLException e) { }
try {
database.execSQL("ALTER TABLE nostros_relays ADD COLUMN deleted_at INT DEFAULT 0;");
} catch (SQLException e) { }
} }
public void saveEvent(JSONObject data, String userPubKey, String relayUrl) throws JSONException { public void saveEvent(JSONObject data, String userPubKey, String relayUrl) throws JSONException {
@ -207,13 +210,13 @@ public class DatabaseModule {
relay.save(database); relay.save(database);
} }
public void destroyRelay(Relay relay) { public void deleteRelay(Relay relay) {
relay.destroy(database); relay.delete(database);
} }
public List<Relay> getRelays(ReactApplicationContext reactContext) { public List<Relay> getRelays(ReactApplicationContext reactContext) {
List<Relay> relayList = new ArrayList<>(); List<Relay> relayList = new ArrayList<>();
String query = "SELECT url, active, global_feed FROM nostros_relays;"; String query = "SELECT url, active, global_feed FROM nostros_relays WHERE deleted_at = 0 AND active = 1;";
@SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {}); @SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {});
if (cursor.getCount() > 0) { if (cursor.getCount() > 0) {
cursor.moveToFirst(); cursor.moveToFirst();

View File

@ -55,7 +55,7 @@ public class RelayPoolModule extends ReactContextBaseJavaModule {
if(url.equals(relay.url)){ if(url.equals(relay.url)){
relay.disconnect(); relay.disconnect();
iterator.remove(); iterator.remove();
database.destroyRelay(relay); database.deleteRelay(relay);
} }
} }

View File

@ -1,6 +1,6 @@
import { t } from 'i18next' import { t } from 'i18next'
import * as React from 'react' import * as React from 'react'
import { StyleSheet, View } from 'react-native' import { Dimensions, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
import { IconButton, Snackbar, Text, TouchableRipple } from 'react-native-paper' import { IconButton, Snackbar, Text, TouchableRipple } from 'react-native-paper'
import Share from 'react-native-share' import Share from 'react-native-share'
@ -8,16 +8,28 @@ import RBSheet from 'react-native-raw-bottom-sheet'
import { getNevent } from '../../lib/nostr/Nip19' import { getNevent } from '../../lib/nostr/Nip19'
import QRCode from 'react-native-qrcode-svg' import QRCode from 'react-native-qrcode-svg'
import { Group } from '../../Functions/DatabaseFunctions/Groups' import { Group } from '../../Functions/DatabaseFunctions/Groups'
import { getNoteRelays } from '../../Functions/DatabaseFunctions/Notes'
import { AppContext } from '../../Contexts/AppContext'
interface GroupShareProps { interface GroupShareProps {
group: Group group: Group
} }
export const GroupShare: React.FC<GroupShareProps> = ({ group }) => { export const GroupShare: React.FC<GroupShareProps> = ({ group }) => {
const { database } = React.useContext(AppContext)
const bottomSheetShareRef = React.useRef<RBSheet>(null) const bottomSheetShareRef = React.useRef<RBSheet>(null)
const [qrCode, setQrCode] = React.useState<any>() const [qrCode, setQrCode] = React.useState<any>()
const [showNotification, setShowNotification] = React.useState<undefined | string>() const [showNotification, setShowNotification] = React.useState<undefined | string>()
const nEvent = React.useMemo(() => getNevent(group.id), [group]) const [nEvent, setNevent] = React.useState<undefined | string>()
React.useEffect(() => {
if (database && group.id) {
getNoteRelays(database, group.id).then((results) => {
const urls = results.map((item) => item.relay_url)
setNevent(getNevent(group.id, [...new Set(urls)]))
})
}
}, [group, database])
return ( return (
<View style={styles.mainLayout}> <View style={styles.mainLayout}>
@ -37,7 +49,7 @@ export const GroupShare: React.FC<GroupShareProps> = ({ group }) => {
<QRCode <QRCode
quietZone={8} quietZone={8}
value={`nostr:${nEvent}`} value={`nostr:${nEvent}`}
size={350} size={Dimensions.get('window').width - 64}
logoBorderRadius={50} logoBorderRadius={50}
logoSize={100} logoSize={100}
logo={{ uri: group?.picture }} logo={{ uri: group?.picture }}

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import QRCode from 'react-native-qrcode-svg' import QRCode from 'react-native-qrcode-svg'
import { Linking, StyleSheet, View } from 'react-native' import { Dimensions, Linking, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard' import Clipboard from '@react-native-clipboard/clipboard'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import RBSheet from 'react-native-raw-bottom-sheet' import RBSheet from 'react-native-raw-bottom-sheet'
@ -76,7 +76,7 @@ export const LnPreview: React.FC<LnPreviewProps> = ({
<Card style={styles.qrContainer}> <Card style={styles.qrContainer}>
<Card.Content> <Card.Content>
<View style={styles.qr}> <View style={styles.qr}>
<QRCode value={invoice} size={300} quietZone={8} /> <QRCode value={invoice} quietZone={8} size={Dimensions.get('window').width - 64} />
</View> </View>
<View style={styles.qrText}> <View style={styles.qrText}>
<Text>{decodedLnUrl?.satoshis} </Text> <Text>{decodedLnUrl?.satoshis} </Text>

View File

@ -0,0 +1,185 @@
import { t } from 'i18next'
import * as React from 'react'
import { StyleSheet, TouchableNativeFeedback, View } from 'react-native'
import { IconButton, 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 Clipboard from '@react-native-clipboard/clipboard'
import NoteShare from '../NoteShare'
import { navigate } from '../../lib/Navigation'
import { relayToColor } from '../../Functions/NativeFunctions'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import { formatId } from '../../Functions/RelayFunctions/Users'
interface NoteActionsProps {
bottomSheetRef: React.RefObject<RBSheet>
}
export const NoteActions: React.FC<NoteActionsProps> = ({ bottomSheetRef }) => {
const theme = useTheme()
const { database, displayNoteDrawer, relayColouring } = React.useContext(AppContext)
const { setDisplayrelayDrawer, lastEventId } = React.useContext(RelayPoolContext)
const [note, setNote] = React.useState<Note>()
const [relays, setRelays] = React.useState<NoteRelay[]>([])
const bottomSheetShareRef = React.useRef<RBSheet>(null)
React.useEffect(() => {
loadNote()
}, [displayNoteDrawer, lastEventId])
const loadNote: () => void = () => {
if (database && displayNoteDrawer) {
getNotes(database, { filters: { id: displayNoteDrawer } }).then((results) => {
if (results.length > 0) {
setNote(results[0])
}
})
getNoteRelays(database, displayNoteDrawer).then(setRelays)
}
}
const bottomSheetStyles = React.useMemo(() => {
return {
container: {
backgroundColor: theme.colors.background,
paddingTop: 16,
paddingRight: 16,
paddingBottom: 32,
paddingLeft: 16,
borderTopRightRadius: 28,
borderTopLeftRadius: 28,
height: 'auto',
},
}
}, [])
return (
<View style={styles.container}>
<Text variant='titleMedium'>{formatId(note?.id)}</Text>
<View style={styles.mainLayout}>
<View style={styles.actionButton}>
<IconButton
icon='eye'
size={28}
onPress={() => {
bottomSheetRef.current?.close()
navigate('Note', { noteId: note?.id })
}}
/>
<Text>{t('noteActions.view')}</Text>
</View>
<View style={styles.actionButton}>
<IconButton
icon='content-copy'
size={28}
onPress={() => {
note?.content && Clipboard.setString(note?.content)
bottomSheetRef.current?.close()
}}
/>
<Text>{t('noteActions.copy')}</Text>
</View>
<View style={styles.actionButton}>
<IconButton
icon='share-variant-outline'
size={28}
onPress={() => bottomSheetShareRef.current?.open()}
/>
<Text>{t('noteActions.share')}</Text>
</View>
</View>
<View style={styles.relayList}>
{relayColouring &&
relays.map((relay, index) => (
<TouchableNativeFeedback
onPress={() => setDisplayrelayDrawer(relay.relay_url)}
key={relay.relay_url}
>
<View
style={[
styles.relay,
{ backgroundColor: relayToColor(relay.relay_url) },
index === 0 ? { borderBottomLeftRadius: 50, borderTopLeftRadius: 50 } : {},
index === relays.length - 1
? { borderBottomRightRadius: 50, borderTopRightRadius: 50 }
: {},
]}
/>
</TouchableNativeFeedback>
))}
</View>
{note && (
<RBSheet
ref={bottomSheetShareRef}
closeOnDragDown={true}
customStyles={bottomSheetStyles}
onClose={() => bottomSheetRef.current?.close()}
>
<NoteShare note={note} />
</RBSheet>
)}
</View>
)
}
const styles = StyleSheet.create({
relayList: {
flexDirection: 'row',
marginTop: 16,
},
container: {
width: '100%',
paddingRight: 16,
paddingLeft: 16,
},
relayColor: {
paddingTop: 9,
},
mainLayout: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 16,
marginBottom: 16,
},
actionButton: {
alignItems: 'center',
},
snackbar: {
marginLeft: 16,
bottom: 16,
},
switch: {
marginLeft: 32,
},
listHeader: {
paddingRight: 5,
paddingLeft: 16,
textAlign: 'center',
},
warning: {
borderRadius: 4,
padding: 16,
marginTop: 16,
marginBottom: 16,
},
warningTitle: {
marginBottom: 8,
},
buttonSpacer: {
marginTop: 16,
marginBottom: 16,
},
muteContainer: {
paddingRight: 16,
},
relaysList: {
maxHeight: '90%',
},
relay: {
flex: 1,
height: 10,
},
})
export default NoteActions

View File

@ -15,8 +15,7 @@ import { t } from 'i18next'
import { isContentWarning } from '../../Functions/RelayFunctions/Events' import { isContentWarning } from '../../Functions/RelayFunctions/Events'
import { Event } from '../../lib/nostr/Events' import { Event } from '../../lib/nostr/Events'
import { getUnixTime } from 'date-fns' import { getUnixTime } from 'date-fns'
import { populateRelay } from '../../Functions/RelayFunctions' import { Relay, searchRelays } from '../../Functions/DatabaseFunctions/Relays'
import { searchRelays } from '../../Functions/DatabaseFunctions/Relays'
import TextContent from '../../Components/TextContent' import TextContent from '../../Components/TextContent'
import { formatPubKey } from '../../Functions/RelayFunctions/Users' import { formatPubKey } from '../../Functions/RelayFunctions/Users'
import { getReactions } from '../../Functions/DatabaseFunctions/Reactions' import { getReactions } from '../../Functions/DatabaseFunctions/Reactions'
@ -30,6 +29,7 @@ import {
TouchableRipple, TouchableRipple,
Chip, Chip,
Surface, Surface,
IconButton,
} from 'react-native-paper' } from 'react-native-paper'
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons' import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
import { REGEX_SOCKET_LINK } from '../../Constants/Relay' import { REGEX_SOCKET_LINK } from '../../Constants/Relay'
@ -55,13 +55,11 @@ interface NoteCardProps {
showPreview?: boolean showPreview?: boolean
showRepostPreview?: boolean showRepostPreview?: boolean
numberOfLines?: number numberOfLines?: number
copyOnPress?: boolean
mode?: 'elevated' | 'outlined' | 'contained' mode?: 'elevated' | 'outlined' | 'contained'
} }
export const NoteCard: React.FC<NoteCardProps> = ({ export const NoteCard: React.FC<NoteCardProps> = ({
note, note,
copyOnPress = true,
showRelayColors = true, showRelayColors = true,
showAvatarImage = true, showAvatarImage = true,
showAnswerData = true, showAnswerData = true,
@ -74,9 +72,15 @@ export const NoteCard: React.FC<NoteCardProps> = ({
}) => { }) => {
const theme = useTheme() const theme = useTheme()
const { publicKey, privateKey } = React.useContext(UserContext) const { publicKey, privateKey } = React.useContext(UserContext)
const { relayPool, lastEventId, setDisplayrelayDrawer } = useContext(RelayPoolContext) const { relayPool, lastEventId, addRelayItem } = useContext(RelayPoolContext)
const { database, showSensitive, setDisplayUserDrawer, relayColouring, longPressZap } = const {
useContext(AppContext) database,
showSensitive,
setDisplayUserDrawer,
setDisplayNoteDrawer,
relayColouring,
longPressZap,
} = useContext(AppContext)
const [relayAdded, setRelayAdded] = useState<boolean>(false) const [relayAdded, setRelayAdded] = useState<boolean>(false)
const [positiveReactions, setPositiveReactions] = useState<number>(0) const [positiveReactions, setPositiveReactions] = useState<number>(0)
const [negativeReactions, setNegativeReactions] = useState<number>(0) const [negativeReactions, setNegativeReactions] = useState<number>(0)
@ -187,7 +191,6 @@ export const NoteCard: React.FC<NoteCardProps> = ({
onPressUser={(user) => setDisplayUserDrawer(user.id)} onPressUser={(user) => setDisplayUserDrawer(user.id)}
showPreview={showPreview} showPreview={showPreview}
numberOfLines={numberOfLines} numberOfLines={numberOfLines}
copyOnPress={copyOnPress}
/> />
)} )}
{note?.repost_id && ( {note?.repost_id && (
@ -199,7 +202,6 @@ export const NoteCard: React.FC<NoteCardProps> = ({
showRepostPreview={false} showRepostPreview={false}
showAction={false} showAction={false}
showRelayColors={false} showRelayColors={false}
copyOnPress={false}
/> />
) : ( ) : (
<Chip <Chip
@ -228,15 +230,8 @@ export const NoteCard: React.FC<NoteCardProps> = ({
} }
const recommendServer: () => JSX.Element = () => { const recommendServer: () => JSX.Element = () => {
const relayName = note?.content const relay: Relay = {
url: note?.content ?? '',
const addRelayItem: () => void = () => {
if (relayPool && database && publicKey && note) {
relayPool.add(note.content, () => {
populateRelay(relayPool, database, publicKey)
setRelayAdded(true)
})
}
} }
return ( return (
@ -244,7 +239,7 @@ export const NoteCard: React.FC<NoteCardProps> = ({
<Card> <Card>
<Card.Title <Card.Title
title={t('noteCard.recommendation')} title={t('noteCard.recommendation')}
subtitle={relayName} subtitle={relay.url}
left={(props) => ( left={(props) => (
<Avatar.Icon <Avatar.Icon
{...props} {...props}
@ -257,7 +252,7 @@ export const NoteCard: React.FC<NoteCardProps> = ({
/> />
{!relayAdded && note && REGEX_SOCKET_LINK.test(note.content) && ( {!relayAdded && note && REGEX_SOCKET_LINK.test(note.content) && (
<Card.Content style={[styles.bottomActions, { borderColor: theme.colors.onSecondary }]}> <Card.Content style={[styles.bottomActions, { borderColor: theme.colors.onSecondary }]}>
<Button mode='contained' onPress={addRelayItem}> <Button mode='contained' onPress={async () => await addRelayItem(relay)}>
{t('noteCard.addRelay')} {t('noteCard.addRelay')}
</Button> </Button>
</Card.Content> </Card.Content>
@ -404,6 +399,7 @@ export const NoteCard: React.FC<NoteCardProps> = ({
return note ? ( return note ? (
<Card mode={mode}> <Card mode={mode}>
<Card.Content style={styles.title}> <Card.Content style={styles.title}>
<View>
<TouchableRipple onPress={() => setDisplayUserDrawer(note.pubkey)}> <TouchableRipple onPress={() => setDisplayUserDrawer(note.pubkey)}>
<ProfileData <ProfileData
username={note?.name} username={note?.name}
@ -416,6 +412,14 @@ export const NoteCard: React.FC<NoteCardProps> = ({
timestamp={note?.created_at} timestamp={note?.created_at}
/> />
</TouchableRipple> </TouchableRipple>
</View>
<View style={styles.noteOptionsIcon}>
<IconButton
icon='dots-vertical'
size={28}
onPress={() => setDisplayNoteDrawer(note.id)}
/>
</View>
</Card.Content> </Card.Content>
{getNoteContent()} {getNoteContent()}
{showAction && ( {showAction && (
@ -478,14 +482,11 @@ export const NoteCard: React.FC<NoteCardProps> = ({
{relayColouring && {relayColouring &&
showRelayColors && showRelayColors &&
relays.map((relay, index) => ( relays.map((relay, index) => (
<TouchableNativeFeedback <TouchableNativeFeedback key={relay.relay_url}>
onPress={() => setDisplayrelayDrawer(relay.relay_url)}
key={relay.relay_url}
>
<View <View
style={[ style={[
styles.relay, styles.relay,
{ backgroundColor: relayToColor(relay.relay_url) }, { borderBottomColor: relayToColor(relay.relay_url) },
index === 0 ? { borderBottomLeftRadius: 50 } : {}, index === 0 ? { borderBottomLeftRadius: 50 } : {},
index === relays.length - 1 ? { borderBottomRightRadius: 50 } : {}, index === relays.length - 1 ? { borderBottomRightRadius: 50 } : {},
]} ]}
@ -502,7 +503,7 @@ export const NoteCard: React.FC<NoteCardProps> = ({
const styles = StyleSheet.create({ const styles = StyleSheet.create({
relayList: { relayList: {
flexDirection: 'row', flexDirection: 'row',
marginTop: 8, bottom: 2,
marginBottom: -16, marginBottom: -16,
marginLeft: -16, marginLeft: -16,
marginRight: -16, marginRight: -16,
@ -510,6 +511,7 @@ const styles = StyleSheet.create({
relay: { relay: {
flex: 1, flex: 1,
height: 10, height: 10,
borderBottomWidth: 2,
}, },
titleUsername: { titleUsername: {
fontWeight: 'bold', fontWeight: 'bold',
@ -529,6 +531,7 @@ const styles = StyleSheet.create({
}, },
title: { title: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between',
paddingBottom: 16, paddingBottom: 16,
}, },
userBlockedWrapper: { userBlockedWrapper: {
@ -580,6 +583,16 @@ const styles = StyleSheet.create({
paddingTop: 4, paddingTop: 4,
paddingLeft: 5, paddingLeft: 5,
}, },
snackbar: {
marginBottom: 50,
marginLeft: 0,
width: '100%',
},
noteOptionsIcon: {
marginBottom: -16,
marginRight: -16,
marginTop: -8,
},
}) })
export default NoteCard export default NoteCard

View File

@ -0,0 +1,111 @@
import { t } from 'i18next'
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 Share from 'react-native-share'
import 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 { getNevent } from '../../lib/nostr/Nip19'
interface NoteShareProps {
note: Note
}
export const NoteShare: React.FC<NoteShareProps> = ({ note }) => {
const { database } = useContext(AppContext)
const bottomSheetShareRef = React.useRef<RBSheet>(null)
const [qrCode, setQrCode] = React.useState<any>()
const [showNotification, setShowNotification] = React.useState<undefined | string>()
const [nEvent, setNevent] = React.useState<undefined | string>()
React.useEffect(() => {
if (database && note.id) {
getNoteRelays(database, note.id).then((results) => {
const urls = results.map((item) => item.relay_url)
setNevent(getNevent(note.id, [...new Set(urls)]))
})
}
}, [note, database])
return (
<View style={styles.mainLayout}>
<View style={styles.qr}>
<TouchableRipple
onPress={() => {
if (qrCode) {
qrCode.toDataURL((base64: string) => {
Share.open({
url: `data:image/png;base64,${base64}`,
filename: note?.id ?? 'nostrosshare',
})
})
}
}}
>
<QRCode
quietZone={8}
value={`nostr:${nEvent}`}
size={Dimensions.get('window').width - 64}
logoBorderRadius={50}
logoSize={100}
logo={{ uri: note?.picture }}
getRef={setQrCode}
/>
</TouchableRipple>
</View>
<View style={styles.shareActionButton}>
<IconButton
icon='key-outline'
size={28}
onPress={() => {
setShowNotification('npubCopied')
Clipboard.setString(nEvent ?? '')
bottomSheetShareRef.current?.close()
}}
/>
<Text>{t('profileShare.copyNPub')}</Text>
</View>
{showNotification && (
<Snackbar
style={styles.snackbar}
visible={showNotification !== undefined}
duration={Snackbar.DURATION_SHORT}
onIconPress={() => setShowNotification(undefined)}
onDismiss={() => setShowNotification(undefined)}
>
{t(`profileShare.notifications.${showNotification}`)}
</Snackbar>
)}
</View>
)
}
const styles = StyleSheet.create({
snackbar: {
marginBottom: 85,
width: '100%',
},
mainLayout: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
justifyContent: 'flex-start',
},
qr: {
justifyContent: 'center',
alignItems: 'center',
padding: 16,
},
shareActionButton: {
justifyContent: 'center',
alignItems: 'center',
flexBasis: '100%',
marginBottom: 4,
},
})
export default NoteShare

View File

@ -277,6 +277,7 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
closeOnDragDown={true} closeOnDragDown={true}
customStyles={bottomSheetStyles} customStyles={bottomSheetStyles}
dragFromTopOnly={true} dragFromTopOnly={true}
onClose={() => onActionDone()}
> >
<View> <View>
<Text variant='titleLarge'>{t('profileCard.relaysTitle')}</Text> <Text variant='titleLarge'>{t('profileCard.relaysTitle')}</Text>
@ -309,10 +310,20 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
</Snackbar> </Snackbar>
)} )}
</RBSheet> </RBSheet>
<RBSheet ref={bottomSheetShareRef} closeOnDragDown={true} customStyles={bottomSheetStyles}> <RBSheet
ref={bottomSheetShareRef}
closeOnDragDown={true}
customStyles={bottomSheetStyles}
onClose={() => onActionDone()}
>
<ProfileShare user={user} /> <ProfileShare user={user} />
</RBSheet> </RBSheet>
<RBSheet ref={bottomSheetMuteRef} closeOnDragDown={true} customStyles={bottomSheetStyles}> <RBSheet
ref={bottomSheetMuteRef}
closeOnDragDown={true}
customStyles={bottomSheetStyles}
onClose={() => onActionDone()}
>
<View style={styles.muteContainer}> <View style={styles.muteContainer}>
<Text variant='titleLarge'> <Text variant='titleLarge'>
{t('profileCard.muteUser', { username: username(user) })} {t('profileCard.muteUser', { username: username(user) })}

View File

@ -60,12 +60,9 @@ export const ProfileData: React.FC<ProfileCardProps> = ({
<></> <></>
)} )}
</View> </View>
<Text numberOfLines={1}>{validNip05 ? getNip05Domain(nip05) : ''}</Text> <Text numberOfLines={1}>
</View> {timestamp ? date : validNip05 ? getNip05Domain(nip05) : ''}
</View> </Text>
<View style={styles.right}>
<View style={styles.date}>
<Text numberOfLines={1}>{date ?? ''}</Text>
</View> </View>
</View> </View>
</View> </View>
@ -73,14 +70,8 @@ export const ProfileData: React.FC<ProfileCardProps> = ({
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
right: {
flexDirection: 'row',
width: '50%',
justifyContent: 'flex-end',
},
left: { left: {
flexDirection: 'row', flexDirection: 'row',
width: '50%',
}, },
container: { container: {
flexDirection: 'row', flexDirection: 'row',

View File

@ -40,7 +40,7 @@ interface RelayCardProps {
export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) => { export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) => {
const theme = useTheme() const theme = useTheme()
const { publicKey } = React.useContext(UserContext) const { publicKey } = React.useContext(UserContext)
const { updateRelayItem, relayPool, removeRelayItem, sendRelays, relays } = const { updateRelayItem, relayPool, removeRelayItem, sendRelays } =
React.useContext(RelayPoolContext) React.useContext(RelayPoolContext)
const { database } = React.useContext(AppContext) const { database } = React.useContext(AppContext)
const [relay, setRelay] = React.useState<Relay>() const [relay, setRelay] = React.useState<Relay>()
@ -57,7 +57,7 @@ export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) =>
React.useEffect(() => { React.useEffect(() => {
loadRelay() loadRelay()
}, []) }, [url])
React.useEffect(() => { React.useEffect(() => {
if (pushUserHistoric && url && database && publicKey && relayPool) { if (pushUserHistoric && url && database && publicKey && relayPool) {
@ -103,7 +103,7 @@ export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) =>
relayPool.sendEvent(group, url) relayPool.sendEvent(group, url)
}) })
}) })
sendRelays(relays, url) sendRelays(url)
} }
}, [pushUserHistoric]) }, [pushUserHistoric])
@ -133,9 +133,8 @@ export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) =>
const removeRelay: () => void = () => { const removeRelay: () => void = () => {
if (relay) { if (relay) {
removeRelayItem(relay).then(() => { removeRelayItem(relay)
bottomSheetRef.current?.close() bottomSheetRef.current?.close()
})
} }
} }

View File

@ -10,7 +10,6 @@ import { Avatar, Card, Text, useTheme } from 'react-native-paper'
import { getNip19Key, getNpub } from '../../lib/nostr/Nip19' import { getNip19Key, getNpub } from '../../lib/nostr/Nip19'
import { navigate } from '../../lib/Navigation' import { navigate } from '../../lib/Navigation'
import { validBlueBirdUrl, validImageUrl, validMediaUrl } from '../../Functions/NativeFunctions' import { validBlueBirdUrl, validImageUrl, validMediaUrl } from '../../Functions/NativeFunctions'
import Clipboard from '@react-native-clipboard/clipboard'
import FastImage from 'react-native-fast-image' import FastImage from 'react-native-fast-image'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { decode, PaymentRequestObject, TagsObject } from 'bolt11' import { decode, PaymentRequestObject, TagsObject } from 'bolt11'
@ -22,7 +21,6 @@ interface TextContentProps {
showPreview?: boolean showPreview?: boolean
onPressUser?: (user: User) => void onPressUser?: (user: User) => void
numberOfLines?: number numberOfLines?: number
copyOnPress?: boolean
} }
export const TextContent: React.FC<TextContentProps> = ({ export const TextContent: React.FC<TextContentProps> = ({
@ -31,7 +29,6 @@ export const TextContent: React.FC<TextContentProps> = ({
showPreview = true, showPreview = true,
onPressUser = () => {}, onPressUser = () => {},
numberOfLines, numberOfLines,
copyOnPress = true,
}) => { }) => {
const theme = useTheme() const theme = useTheme()
const { t } = useTranslation('common') const { t } = useTranslation('common')
@ -267,8 +264,8 @@ export const TextContent: React.FC<TextContentProps> = ({
}, },
]} ]}
childrenProps={{ allowFontScaling: false }} childrenProps={{ allowFontScaling: false }}
onLongPress={copyOnPress ? () => Clipboard.setString(text) : undefined}
numberOfLines={numberOfLines} numberOfLines={numberOfLines}
selectable
> >
{text} {text}
</ParsedText> </ParsedText>

View File

@ -35,6 +35,8 @@ export interface AppContextProps {
checkClipboard: () => void checkClipboard: () => void
displayUserDrawer?: string displayUserDrawer?: string
setDisplayUserDrawer: (displayUserDrawer: string | undefined) => void setDisplayUserDrawer: (displayUserDrawer: string | undefined) => void
displayNoteDrawer?: string
setDisplayNoteDrawer: (displayNoteDrawer: string | undefined) => void
refreshBottomBarAt?: number refreshBottomBarAt?: number
setRefreshBottomBarAt: (refreshBottomBarAt: number) => void setRefreshBottomBarAt: (refreshBottomBarAt: number) => void
longPressZap?: number | undefined longPressZap?: number | undefined
@ -78,6 +80,7 @@ export const initialAppContext: AppContextProps = {
getSatoshiSymbol: () => <></>, getSatoshiSymbol: () => <></>,
setClipboardNip21: () => {}, setClipboardNip21: () => {},
setDisplayUserDrawer: () => {}, setDisplayUserDrawer: () => {},
setDisplayNoteDrawer: () => {},
longPressZap: undefined, longPressZap: undefined,
setLongPressZap: () => {}, setLongPressZap: () => {},
} }
@ -105,6 +108,7 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
const [clipboardLoads, setClipboardLoads] = React.useState<string[]>([]) const [clipboardLoads, setClipboardLoads] = React.useState<string[]>([])
const [clipboardNip21, setClipboardNip21] = React.useState<string>() const [clipboardNip21, setClipboardNip21] = React.useState<string>()
const [displayUserDrawer, setDisplayUserDrawer] = React.useState<string>() const [displayUserDrawer, setDisplayUserDrawer] = React.useState<string>()
const [displayNoteDrawer, setDisplayNoteDrawer] = React.useState<string>()
const [pushedTab, setPushedTab] = useState<string>() const [pushedTab, setPushedTab] = useState<string>()
useEffect(() => { useEffect(() => {
@ -238,6 +242,8 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
setPushedTab, setPushedTab,
longPressZap, longPressZap,
setLongPressZap, setLongPressZap,
displayNoteDrawer,
setDisplayNoteDrawer,
}} }}
> >
{children} {children}

View File

@ -7,7 +7,7 @@ import { getActiveRelays, getRelays, Relay } from '../Functions/DatabaseFunction
import { UserContext } from './UserContext' import { UserContext } from './UserContext'
import { randomInt } from '../Functions/NativeFunctions' import { randomInt } from '../Functions/NativeFunctions'
import { getUnixTime } from 'date-fns' import { getUnixTime } from 'date-fns'
import { Event } from '../../lib/nostr/Events' import { Event } from '../lib/nostr/Events'
export interface RelayPoolContextProps { export interface RelayPoolContextProps {
relayPoolReady: boolean relayPoolReady: boolean
@ -21,7 +21,7 @@ export interface RelayPoolContextProps {
addRelayItem: (relay: Relay) => Promise<void> addRelayItem: (relay: Relay) => Promise<void>
removeRelayItem: (relay: Relay) => Promise<void> removeRelayItem: (relay: Relay) => Promise<void>
updateRelayItem: (relay: Relay) => Promise<void> updateRelayItem: (relay: Relay) => Promise<void>
sendRelays: (relays: Relay[], url?: string) => Promise<void> sendRelays: (url?: string) => Promise<void>
} }
export interface WebsocketEvent { export interface WebsocketEvent {
@ -36,12 +36,12 @@ export interface RelayPoolContextProviderProps {
export const initialRelayPoolContext: RelayPoolContextProps = { export const initialRelayPoolContext: RelayPoolContextProps = {
relayPoolReady: true, relayPoolReady: true,
setRelayPool: () => {}, setRelayPool: () => {},
addRelayItem: async () => await new Promise(() => {}), addRelayItem: async () => {},
removeRelayItem: async () => await new Promise(() => {}), removeRelayItem: async () => {},
updateRelayItem: async () => await new Promise(() => {}), updateRelayItem: async () => {},
relays: [], relays: [],
setDisplayrelayDrawer: () => {}, setDisplayrelayDrawer: () => {},
sendRelays: () => {}, sendRelays: async () => {},
} }
export const RelayPoolContextProvider = ({ export const RelayPoolContextProvider = ({
@ -58,17 +58,21 @@ export const RelayPoolContextProvider = ({
const [relays, setRelays] = React.useState<Relay[]>([]) const [relays, setRelays] = React.useState<Relay[]>([])
const [displayRelayDrawer, setDisplayrelayDrawer] = React.useState<string>() const [displayRelayDrawer, setDisplayrelayDrawer] = React.useState<string>()
const sendRelays: (relayList: Relay[], url?: string) => void = (relayList, url) => { const sendRelays: (url?: string) => Promise<void> = async (url) => {
if (publicKey && relayList.length > 0) { if (publicKey && database) {
getActiveRelays(database).then((results) => {
if (publicKey && results.length > 0) {
const event: Event = { const event: Event = {
content: '', content: '',
created_at: getUnixTime(new Date()), created_at: getUnixTime(new Date()),
kind: 1002, kind: 1002,
pubkey: publicKey, pubkey: publicKey,
tags: relayList.map((relay) => ['r', relay.url, relay.mode ?? '']), tags: results.map((relay) => ['r', relay.url, relay.mode ?? '']),
} }
url ? relayPool?.sendEvent(event, url) : relayPool?.sendEvent(event) url ? relayPool?.sendEvent(event, url) : relayPool?.sendEvent(event)
} }
})
}
} }
const changeEventIdHandler: (event: WebsocketEvent) => void = (event) => { const changeEventIdHandler: (event: WebsocketEvent) => void = (event) => {
@ -79,11 +83,11 @@ export const RelayPoolContextProvider = ({
} }
const debouncedEventIdHandler = useMemo( const debouncedEventIdHandler = useMemo(
() => debounce(changeEventIdHandler, 1000), () => debounce(changeEventIdHandler, 250),
[setLastEventId], [setLastEventId],
) )
const debouncedConfirmationHandler = useMemo( const debouncedConfirmationHandler = useMemo(
() => debounce(changeConfirmationIdHandler, 500), () => debounce(changeConfirmationIdHandler, 250),
[setLastConfirmationId], [setLastConfirmationId],
) )
@ -146,8 +150,8 @@ export const RelayPoolContextProvider = ({
return await new Promise((resolve, _reject) => { return await new Promise((resolve, _reject) => {
if (relayPool && database && publicKey) { if (relayPool && database && publicKey) {
relayPool.add(relay.url, () => { relayPool.add(relay.url, () => {
loadRelays().then((results) => { loadRelays().then(() => {
sendRelays(results) sendRelays()
resolve() resolve()
}) })
}) })
@ -160,8 +164,8 @@ export const RelayPoolContextProvider = ({
return await new Promise((resolve, _reject) => { return await new Promise((resolve, _reject) => {
if (relayPool && database && publicKey) { if (relayPool && database && publicKey) {
relayPool.remove(relay.url, () => { relayPool.remove(relay.url, () => {
loadRelays().then((results) => { loadRelays().then(() => {
sendRelays(results) sendRelays()
resolve() resolve()
}) })
}) })

View File

@ -31,7 +31,7 @@ export const searchRelays: (
relayUrl: string, relayUrl: string,
db: QuickSQLiteConnection, db: QuickSQLiteConnection,
) => Promise<Relay[]> = async (relayUrl, db) => { ) => Promise<Relay[]> = async (relayUrl, db) => {
const searchQuery = `SELECT * FROM nostros_relays WHERE url = '${relayUrl}';` const searchQuery = `SELECT * FROM nostros_relays WHERE url = '${relayUrl}' AND deleted_at = 0;`
const results = await db.execute(searchQuery) const results = await db.execute(searchQuery)
const items: object[] = getItems(results) const items: object[] = getItems(results)
const relays: Relay[] = items.map((object) => databaseToEntity(object)) const relays: Relay[] = items.map((object) => databaseToEntity(object))
@ -39,7 +39,7 @@ export const searchRelays: (
} }
export const getRelays: (db: QuickSQLiteConnection) => Promise<Relay[]> = async (db) => { export const getRelays: (db: QuickSQLiteConnection) => Promise<Relay[]> = async (db) => {
const notesQuery = 'SELECT * FROM nostros_relays;' const notesQuery = 'SELECT * FROM nostros_relays WHERE deleted_at = 0;'
const resultSet = await db.execute(notesQuery) const resultSet = await db.execute(notesQuery)
const items: object[] = getItems(resultSet) const items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object)) const relays: Relay[] = items.map((object) => databaseToEntity(object))
@ -47,7 +47,7 @@ export const getRelays: (db: QuickSQLiteConnection) => Promise<Relay[]> = async
} }
export const getActiveRelays: (db: QuickSQLiteConnection) => Promise<Relay[]> = async (db) => { export const getActiveRelays: (db: QuickSQLiteConnection) => Promise<Relay[]> = async (db) => {
const notesQuery = 'SELECT * FROM nostros_relays WHERE active = 1;' const notesQuery = 'SELECT * FROM nostros_relays WHERE active = 1 AND deleted_at = 0;'
const resultSet = await db.execute(notesQuery) const resultSet = await db.execute(notesQuery)
const items: object[] = getItems(resultSet) const items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object)) const relays: Relay[] = items.map((object) => databaseToEntity(object))
@ -58,23 +58,13 @@ export const getRelay: (db: QuickSQLiteConnection, url: string) => Promise<Relay
db, db,
url, url,
) => { ) => {
const notesQuery = 'SELECT * FROM nostros_relays WHERE url = ?;' const notesQuery = 'SELECT * FROM nostros_relays WHERE url = ? AND deleted_at = 0;'
const resultSet = await db.execute(notesQuery, [url]) const resultSet = await db.execute(notesQuery, [url])
const items: object[] = getItems(resultSet) const items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object)) const relays: Relay[] = items.map((object) => databaseToEntity(object))
return relays[0] return relays[0]
} }
export const createRelay: (db: QuickSQLiteConnection, url: string) => Promise<QueryResult> = async (
db,
url,
) => {
const query = `
INSERT OR IGNORE INTO nostros_relays (url) VALUES (?)
`
return db.execute(query, [url])
}
export const createResilientRelay: ( export const createResilientRelay: (
db: QuickSQLiteConnection, db: QuickSQLiteConnection,
url: string, url: string,

View File

@ -1,12 +0,0 @@
import RelayPool from '../../lib/nostr/RelayPool/intex'
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
import { populatePets, populateProfile } from './Users'
export const populateRelay: (
relayPool: RelayPool,
database: QuickSQLiteConnection,
publicKey: string,
) => void = (relayPool, database, publicKey) => {
populateProfile(relayPool, database, publicKey)
populatePets(relayPool, database, publicKey)
}

View File

@ -86,14 +86,22 @@
"app": "App", "app": "App",
"feed": "Feed", "feed": "Feed",
"zaps": "Zaps", "zaps": "Zaps",
"disabled": "Disabled", "disabled": "deaktiviert",
"longPressZap": "Long press Zaps", "longPressZap": "Zaps lange drücken",
"longPressZapDescription": "Define a default amount to automatically generate invoices after a long press on the Zap button.", "longPressZapDescription": "Lege den voreingestellten Betrag fest, mit der Invoices erstellt werden sollen, wenn Zaps lange gedrückt wird.",
"defaultZapAmount": "Defaul Zap amount", "defaultZapAmount": "Standard Zap Betrag",
"update": "Update", "update": "Aktualisieren",
"disable": "Disable" "disable": "Deaktivieren"
},
"noteActions": {
"copy": "Kopieren",
"share": "Teilen",
"view": "Ansehen"
}, },
"noteCard": { "noteCard": {
"notifications": {
"zapServerError": "Beim Anfragen des Invoice hat es einen Fehler vom Server gegeben."
},
"answering": "{{pubkey}} antworten", "answering": "{{pubkey}} antworten",
"reposting": "{{pubkey}} reposten", "reposting": "{{pubkey}} reposten",
"seeParent": "Notiz ansehen", "seeParent": "Notiz ansehen",
@ -110,7 +118,7 @@
"cancel": "Abbrechen", "cancel": "Abbrechen",
"copy": "Kopieren", "copy": "Kopieren",
"open": "Öffne Wallet", "open": "Öffne Wallet",
"anonTip": "Anonymous tip", "anonTip": "Anonymer Tip",
"zap": "Zap" "zap": "Zap"
}, },
"notificationsFeed": { "notificationsFeed": {
@ -210,9 +218,9 @@
"resilienceCategoriesDescription": "Ein erster Ansatz, den Nostros testet, ist die zufällige Berechnung (auf der Grundlage der empfangenen Kontaktereignisse), welche Relays am ausgewogensten sind. Dabei wird versucht, sowohl Relays mit zu vielen Ereignissen (Zentralisierung) als auch Relays mit fast keinen Ereignissen (Akkuentladung) zu vermeiden\n\nEs ist das Ziel, eine Liste von 5 Relays zu erstellen, die alle Kontakte abdeckt. Sollte dies jedoch nicht möglich sein, wird Nostros unter anderen Relays suchen und den Benutzer darüber warnen.", "resilienceCategoriesDescription": "Ein erster Ansatz, den Nostros testet, ist die zufällige Berechnung (auf der Grundlage der empfangenen Kontaktereignisse), welche Relays am ausgewogensten sind. Dabei wird versucht, sowohl Relays mit zu vielen Ereignissen (Zentralisierung) als auch Relays mit fast keinen Ereignissen (Akkuentladung) zu vermeiden\n\nEs ist das Ziel, eine Liste von 5 Relays zu erstellen, die alle Kontakte abdeckt. Sollte dies jedoch nicht möglich sein, wird Nostros unter anderen Relays suchen und den Benutzer darüber warnen.",
"centralized": "Zentralisiert", "centralized": "Zentralisiert",
"small": "Klein", "small": "Klein",
"contacts": "# Kontakte", "contacts": "zugewiesene Kontakte",
"resilienceMode": "Beständigkeit (experimental)", "resilienceMode": "Beständigkeit (experimental)",
"relayName": "Addresse", "relayName": "Adresse",
"globalFeed": "Globaler Feed", "globalFeed": "Globaler Feed",
"active": "Aktiv", "active": "Aktiv",
"labelAdd": "Beschreibung des Relays", "labelAdd": "Beschreibung des Relays",
@ -233,7 +241,6 @@
"alreadyExists": "Relay existiert bereits" "alreadyExists": "Relay existiert bereits"
} }
}, },
"relayCard": { "relayCard": {
"pushDone": "Fertig", "pushDone": "Fertig",
"pushHistoricTitle": "Alle meine Daten hochladen", "pushHistoricTitle": "Alle meine Daten hochladen",
@ -241,7 +248,7 @@
"pushHistoricAlertTitle": "Wichtig!", "pushHistoricAlertTitle": "Wichtig!",
"pushHistoricAlert": "Öffentliche Relays haben strenge Anti-Spam Regeln, diese Aktion kann dein Profil sperren oder verbannen. Stelle zuvor sicher, das du große Datenmengen auf diesen Relay schieben darfst.\n\nWir empfehlen, dieses Feature ausschlisslich für private Relays zu benutzen, wie z.B. Umbrel's Nostr Relay.", "pushHistoricAlert": "Öffentliche Relays haben strenge Anti-Spam Regeln, diese Aktion kann dein Profil sperren oder verbannen. Stelle zuvor sicher, das du große Datenmengen auf diesen Relay schieben darfst.\n\nWir empfehlen, dieses Feature ausschlisslich für private Relays zu benutzen, wie z.B. Umbrel's Nostr Relay.",
"pushConsent": "Ich schiebe meine Daten auf einen privaten Relay", "pushConsent": "Ich schiebe meine Daten auf einen privaten Relay",
"pushingEvent": "Event {{eventId}} pushed", "pushingEvent": "Event {{eventId}} hochgeladen",
"pushRelay": "Alle meine Daten hochladen", "pushRelay": "Alle meine Daten hochladen",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"moreInfo": "Mehr Infos", "moreInfo": "Mehr Infos",
@ -266,10 +273,9 @@
"pubkeyCopied": "Öffentlichen Schlüssel kopiert.", "pubkeyCopied": "Öffentlichen Schlüssel kopiert.",
"contactCopied": "Kontakt kopiert.", "contactCopied": "Kontakt kopiert.",
"urlCopied": "URL kopiert.", "urlCopied": "URL kopiert.",
"pushCompleted": "All your data has been pushed." "pushCompleted": "Alle deine Daten wurden hochgeladen"
} }
}, },
"profileConfigPage": { "profileConfigPage": {
"notifications": { "notifications": {
"nsecCopied": "Privater Schlüssel kopiert.", "nsecCopied": "Privater Schlüssel kopiert.",
@ -318,7 +324,7 @@
"active": "Relay aktiviert.", "active": "Relay aktiviert.",
"desactive": "Relay deaktiviert." "desactive": "Relay deaktiviert."
}, },
"invoice": "Zap", "invoice": "Tip",
"message": "Nachricht", "message": "Nachricht",
"follow": "Folgen", "follow": "Folgen",
"isFollower": "folgt dir", "isFollower": "folgt dir",
@ -327,15 +333,15 @@
"relaysDescription": "Dies sind {{username}}'s Relays, aktiviere diejenigen, zu denen du verbunden werden möchtest." "relaysDescription": "Dies sind {{username}}'s Relays, aktiviere diejenigen, zu denen du verbunden werden möchtest."
}, },
"groupPage": { "groupPage": {
"typeMessage": "Nachricht schreiben" "typeMessage": "Nachricht schreiben",
"replyText": "Nachricht"
}, },
"groupHeaderIcon": { "groupHeaderIcon": {
"delete": "Leave group", "delete": "Gruppe verlassen",
"share": "Share group", "share": "Gruppe teilen",
"edit": "Edit group" "edit": "Gruppe editieren"
}, },
"groupsFeed": { "groupsFeed": {
"delete": "Gruppe verlassen",
"updateTitle": "Gruppe aktualisieren", "updateTitle": "Gruppe aktualisieren",
"newGroupName": "Name", "newGroupName": "Name",
"newGroupDescription": "Beschreibung", "newGroupDescription": "Beschreibung",
@ -345,7 +351,8 @@
"createTitle": "Gruppe erstellen", "createTitle": "Gruppe erstellen",
"groupId": "Gruppen ID", "groupId": "Gruppen ID",
"addTitle": "Bestehende Gruppe hinzufügen", "addTitle": "Bestehende Gruppe hinzufügen",
"add": "Gruppe hinzufügen" "add": "Gruppe hinzufügen",
"delete": "Gruppe löschen"
}, },
"homePage": { "homePage": {
"clipboardTitle": "Nostr Schlüssel entdeckt", "clipboardTitle": "Nostr Schlüssel entdeckt",
@ -368,7 +375,6 @@
"copyNip05": "NIP-05 kopieren", "copyNip05": "NIP-05 kopieren",
"copyNPub": "Schlüssel kopieren" "copyNPub": "Schlüssel kopieren"
}, },
"profileCard": { "profileCard": {
"notifications": { "notifications": {
"contactAdded": "Aboniert", "contactAdded": "Aboniert",
@ -376,7 +382,7 @@
"userUnblocked": "Profil entblockt", "userUnblocked": "Profil entblockt",
"userBlocked": "Profile geblockt" "userBlocked": "Profile geblockt"
}, },
"invoice": "Zap", "invoice": "Tip",
"message": "Nachricht", "message": "Nachricht",
"follow": "Folgen", "follow": "Folgen",
"block": "Blockieren", "block": "Blockieren",
@ -384,9 +390,14 @@
"unblock": "Erlauben", "unblock": "Erlauben",
"unfollow": "Abo entfernen", "unfollow": "Abo entfernen",
"relaysTitle": "Relays", "relaysTitle": "Relays",
"blocked": "Blocked", "blocked": "Blockiert",
"mute": "Mute", "mute": "Stumm",
"muted": "Muted" "muted": "Stumm",
"muteUser": "{{username}} in Chats stumm schalten",
"muteWarningTitle": "Wichtig",
"muteWarning": "Diese Aktion wird Nachrichten dieses Benutzers in allen Chats unwiderruflich verstecken und kann nicht rückgängig gemacht werden.",
"muteForever": "{{username}} für immer stumm schalten",
"cancel": "Abbrechen"
}, },
"conversationsFeed": { "conversationsFeed": {
"openMessage": "Unterhaltung beginnen", "openMessage": "Unterhaltung beginnen",

View File

@ -89,11 +89,19 @@
"disabled": "Disabled", "disabled": "Disabled",
"longPressZap": "Long press Zaps", "longPressZap": "Long press Zaps",
"longPressZapDescription": "Define a default amount to automatically generate invoices after a long press on the Zap button.", "longPressZapDescription": "Define a default amount to automatically generate invoices after a long press on the Zap button.",
"defaultZapAmount": "Defaul Zap amount", "defaultZapAmount": "Default Zap amount",
"update": "Update", "update": "Update",
"disable": "Disable" "disable": "Disable"
}, },
"noteActions": {
"copy": "Copy",
"share": "Share",
"view": "View"
},
"noteCard": { "noteCard": {
"notifications": {
"zapServerError": "There was an error on the server while trying to request the invoice."
},
"answering": "Answer to {{pubkey}}", "answering": "Answer to {{pubkey}}",
"reposting": "Reposted {{pubkey}}", "reposting": "Reposted {{pubkey}}",
"userBlocked": "User blocked", "userBlocked": "User blocked",
@ -127,7 +135,7 @@
"skip": "Skip", "skip": "Skip",
"confirmTitle": "Type the words in the right order.", "confirmTitle": "Type the words in the right order.",
"warningTitle": "Important", "warningTitle": "Important",
"warningDescription": "Store your key in a safe place. If lose it, you won't be able to access or recover your profile.", "warningDescription": "Store your key in a safe place. If you lose it, you won't be able to access or recover your profile.",
"warningAction": "Copy private key", "warningAction": "Copy private key",
"continue": "Continue", "continue": "Continue",
"mnemonicTitle": "These words or your private key are the only way to recover your account.", "mnemonicTitle": "These words or your private key are the only way to recover your account.",

View File

@ -114,7 +114,15 @@
"update": "Actualizar", "update": "Actualizar",
"disable": "Desabilitar" "disable": "Desabilitar"
}, },
"noteActions": {
"copy": "Copiar",
"share": "Compartir",
"view": "Ver"
},
"noteCard": { "noteCard": {
"notifications": {
"zapServerError": "Ha ocurrido un error en el servidor mientras se intentaba obtener la factura."
},
"answering": "Responder a {{pubkey}}", "answering": "Responder a {{pubkey}}",
"reposting": "Reposted {{pubkey}}", "reposting": "Reposted {{pubkey}}",
"seeParent": "Ver nota", "seeParent": "Ver nota",

View File

@ -121,7 +121,15 @@
"update": "Update", "update": "Update",
"disable": "Disable" "disable": "Disable"
}, },
"noteActions": {
"copy": "Copier",
"share": "Partager",
"view": "View"
},
"noteCard": { "noteCard": {
"notifications": {
"zapServerError": "There was an error on the server while trying to request for the invoice."
},
"answering": "Répondre à {{pubkey}}", "answering": "Répondre à {{pubkey}}",
"reposting": "Republié {{pubkey}}", "reposting": "Republié {{pubkey}}",
"seeParent": "Voir la note", "seeParent": "Voir la note",

View File

@ -114,7 +114,15 @@
"update": "Update", "update": "Update",
"disable": "Disable" "disable": "Disable"
}, },
"noteActions": {
"copy": "Скопировать",
"share": "Поделиться",
"view": "View"
},
"noteCard": { "noteCard": {
"notifications": {
"zapServerError": "There was an error on the server while trying to request for the invoice."
},
"answering": "Ответить {{pubkey}}", "answering": "Ответить {{pubkey}}",
"reposting": "Reposted {{pubkey}}", "reposting": "Reposted {{pubkey}}",
"seeParent": "See note", "seeParent": "See note",

View File

@ -96,7 +96,7 @@
"logout": "退出" "logout": "退出"
}, },
"configPage": { "configPage": {
"showPublicImages": "在发现页显示图片", "showPublicImages": "显示发现页图片",
"showSensitive": "显示敏感的 Notes", "showSensitive": "显示敏感的 Notes",
"satoshi": "聪符号", "satoshi": "聪符号",
"imageHostingService": "图片托管服务", "imageHostingService": "图片托管服务",
@ -104,16 +104,24 @@
"language": "语言", "language": "语言",
"relayColoruring": "颜色标示中继状态", "relayColoruring": "颜色标示中继状态",
"app": "App", "app": "App",
"feed": "Feed", "feed": "信息流",
"zaps": "Zaps", "zaps": "Zaps",
"disabled": "Disabled", "disabled": "停用",
"longPressZap": "Long press Zaps", "longPressZap": "长按 Zaps",
"longPressZapDescription": "Define a default amount to automatically generate invoices after a long press on the Zap button.", "longPressZapDescription": "设置长按 Zap 按钮后自动生成账单的默认金额",
"defaultZapAmount": "Defaul Zap amount", "defaultZapAmount": "默认 Zap 金额",
"update": "Update", "update": "更新",
"disable": "Disable" "disable": "停用"
},
"noteActions": {
"copy": "复制",
"share": "分享",
"view": "查看"
}, },
"noteCard": { "noteCard": {
"notifications": {
"zapServerError": "尝试请求收据时服务器出现错误。"
},
"answering": "回复 {{pubkey}}", "answering": "回复 {{pubkey}}",
"reposting": "已转发 {{pubkey}}", "reposting": "已转发 {{pubkey}}",
"userBlocked": "用户已屏蔽", "userBlocked": "用户已屏蔽",
@ -130,7 +138,7 @@
"npub": "公钥", "npub": "公钥",
"copy": "复制", "copy": "复制",
"open": "打开钱包", "open": "打开钱包",
"anonTip": "Anonymous tip", "anonTip": "匿名赞赏",
"zap": "Zap" "zap": "Zap"
}, },
"notificationsFeed": { "notificationsFeed": {
@ -243,7 +251,7 @@
"myList": "我的中继", "myList": "我的中继",
"notifications": { "notifications": {
"globalFeedActive": "已为发现页开启", "globalFeedActive": "已为发现页开启",
"globalFeedActiveUnactive": "已为发现页用", "globalFeedActiveUnactive": "已为发现页用",
"add": "中继已添加", "add": "中继已添加",
"remove": "中继已移除", "remove": "中继已移除",
"active": "中继已启用", "active": "中继已启用",
@ -276,7 +284,7 @@
"pubkeyCopied": "已复制私钥", "pubkeyCopied": "已复制私钥",
"contactCopied": "已复制联系人", "contactCopied": "已复制联系人",
"urlCopied": "已复制链接", "urlCopied": "已复制链接",
"pushCompleted": "All your data has been pushed." "pushCompleted": "您的所有数据都已推送"
} }
}, },
"profileConfigPage": { "profileConfigPage": {
@ -285,7 +293,7 @@
"npubCopied": "已复制公钥", "npubCopied": "已复制公钥",
"profilePublished": "简介已发布", "profilePublished": "简介已发布",
"lud06Published": "LUDs 已发布", "lud06Published": "LUDs 已发布",
"nip05Published": "NIP-05 已发布 \n\n{{nip05}}", "nip05Published": "NIP-05 已发布\n\n{{nip05}}",
"picturePublished": "头像已发布", "picturePublished": "头像已发布",
"connectionError": "连接错误", "connectionError": "连接错误",
"active": "中继已启用", "active": "中继已启用",
@ -331,7 +339,7 @@
"unfollow": "关注中", "unfollow": "关注中",
"copyNPub": "复制公钥", "copyNPub": "复制公钥",
"relaysTitle": "中继", "relaysTitle": "中继",
"relaysDescription": "以下是{{username}}的中继,您可以激活想要连接的中继" "relaysDescription": "以下是{{username}}的中继,您可以启用想要连接的中继"
}, },
"homePage": { "homePage": {
"clipboardTitle": "检测到 Nostr 公钥", "clipboardTitle": "检测到 Nostr 公钥",

View File

@ -23,15 +23,23 @@ import { getUnixTime } from 'date-fns'
import ContactsPage from '../ContactsPage' import ContactsPage from '../ContactsPage'
import GroupPage from '../GroupPage' import GroupPage from '../GroupPage'
import GroupHeaderIcon from '../../Components/GroupHeaderIcon' import GroupHeaderIcon from '../../Components/GroupHeaderIcon'
import NoteActions from '../../Components/NoteActions'
export const HomeNavigator: React.FC = () => { export const HomeNavigator: React.FC = () => {
const theme = useTheme() const theme = useTheme()
const { t } = useTranslation('common') const { t } = useTranslation('common')
const { displayRelayDrawer, setDisplayrelayDrawer } = React.useContext(RelayPoolContext) const { displayRelayDrawer, setDisplayrelayDrawer } = React.useContext(RelayPoolContext)
const { displayUserDrawer, setDisplayUserDrawer, setRefreshBottomBarAt, database } = const {
React.useContext(AppContext) displayUserDrawer,
setDisplayNoteDrawer,
displayNoteDrawer,
setDisplayUserDrawer,
setRefreshBottomBarAt,
database,
} = React.useContext(AppContext)
const bottomSheetRef = React.useRef<RBSheet>(null) const bottomSheetRef = React.useRef<RBSheet>(null)
const bottomSheetProfileRef = React.useRef<RBSheet>(null) const bottomSheetProfileRef = React.useRef<RBSheet>(null)
const bottomSheetNoteRef = React.useRef<RBSheet>(null)
const bottomSheetRelayRef = React.useRef<RBSheet>(null) const bottomSheetRelayRef = React.useRef<RBSheet>(null)
const Stack = React.useMemo(() => createStackNavigator(), []) const Stack = React.useMemo(() => createStackNavigator(), [])
const cardStyleInterpolator = React.useMemo( const cardStyleInterpolator = React.useMemo(
@ -74,6 +82,10 @@ export const HomeNavigator: React.FC = () => {
if (displayRelayDrawer) bottomSheetRelayRef.current?.open() if (displayRelayDrawer) bottomSheetRelayRef.current?.open()
}, [displayRelayDrawer]) }, [displayRelayDrawer])
React.useEffect(() => {
if (displayNoteDrawer) bottomSheetNoteRef.current?.open()
}, [displayNoteDrawer])
React.useEffect(() => { React.useEffect(() => {
if (displayUserDrawer) bottomSheetProfileRef.current?.open() if (displayUserDrawer) bottomSheetProfileRef.current?.open()
}, [displayUserDrawer]) }, [displayUserDrawer])
@ -172,6 +184,14 @@ export const HomeNavigator: React.FC = () => {
> >
<ProfileCard bottomSheetRef={bottomSheetProfileRef} /> <ProfileCard bottomSheetRef={bottomSheetProfileRef} />
</RBSheet> </RBSheet>
<RBSheet
ref={bottomSheetNoteRef}
closeOnDragDown={true}
customStyles={bottomSheetStyles}
onClose={() => setDisplayNoteDrawer(undefined)}
>
<NoteActions bottomSheetRef={bottomSheetNoteRef} />
</RBSheet>
<RBSheet <RBSheet
ref={bottomSheetRelayRef} ref={bottomSheetRelayRef}
closeOnDragDown={true} closeOnDragDown={true}

View File

@ -329,8 +329,8 @@ export const GroupPage: React.FC<GroupPageProps> = ({ route }) => {
friction={2} friction={2}
overshootRight={false} overshootRight={false}
onSwipeableOpen={() => { onSwipeableOpen={() => {
setReply(item)
row[index].close() row[index].close()
setReply(item)
}} }}
> >
<View style={styles.messageRow} key={index}> <View style={styles.messageRow} key={index}>

View File

@ -6,24 +6,18 @@ import { useTranslation } from 'react-i18next'
import { RelayPoolContext } from '../../Contexts/RelayPoolContext' import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
import getUnixTime from 'date-fns/getUnixTime' import getUnixTime from 'date-fns/getUnixTime'
import { Note } from '../../Functions/DatabaseFunctions/Notes' import { Note } from '../../Functions/DatabaseFunctions/Notes'
import { getETags, getTaggedPubKeys } from '../../Functions/RelayFunctions/Events' import { getETags } from '../../Functions/RelayFunctions/Events'
import { getUsers, User } from '../../Functions/DatabaseFunctions/Users' import { getUsers, User } from '../../Functions/DatabaseFunctions/Users'
import { formatPubKey } from '../../Functions/RelayFunctions/Users' import { formatPubKey } from '../../Functions/RelayFunctions/Users'
import { import { Button, IconButton, Switch, Text, TextInput, useTheme } from 'react-native-paper'
Button,
IconButton,
Switch,
Text,
TextInput,
TouchableRipple,
useTheme,
} from 'react-native-paper'
import { UserContext } from '../../Contexts/UserContext' import { UserContext } from '../../Contexts/UserContext'
import { goBack } from '../../lib/Navigation' import { goBack } from '../../lib/Navigation'
import { Kind } from 'nostr-tools' import { Kind } from 'nostr-tools'
import ProfileData from '../../Components/ProfileData' import ProfileData from '../../Components/ProfileData'
import NoteCard from '../../Components/NoteCard' import NoteCard from '../../Components/NoteCard'
import UploadImage from '../../Components/UploadImage' import UploadImage from '../../Components/UploadImage'
import { useFocusEffect } from '@react-navigation/native'
import { TouchableWithoutFeedback } from 'react-native-gesture-handler'
interface SendPageProps { interface SendPageProps {
route: { params: { note: Note; type?: 'reply' | 'repost' } | undefined } route: { params: { note: Note; type?: 'reply' | 'repost' } | undefined }
@ -38,6 +32,7 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
// state // state
const [content, setContent] = useState<string>('') const [content, setContent] = useState<string>('')
const [contentWarning, setContentWarning] = useState<boolean>(false) const [contentWarning, setContentWarning] = useState<boolean>(false)
const [users, setUsers] = useState<User[]>([])
const [userSuggestions, setUserSuggestions] = useState<User[]>([]) const [userSuggestions, setUserSuggestions] = useState<User[]>([])
const [userMentions, setUserMentions] = useState<User[]>([]) const [userMentions, setUserMentions] = useState<User[]>([])
const [isSending, setIsSending] = useState<boolean>(false) const [isSending, setIsSending] = useState<boolean>(false)
@ -45,26 +40,23 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
const [startUpload, setStartUpload] = useState<boolean>(false) const [startUpload, setStartUpload] = useState<boolean>(false)
const note = React.useMemo(() => route.params?.note, []) const note = React.useMemo(() => route.params?.note, [])
useFocusEffect(
React.useCallback(() => {
if (database) getUsers(database, {}).then(setUsers)
return () => {}
}, []),
)
useEffect(() => { useEffect(() => {
if (isSending) goBack() if (isSending) goBack()
}, [lastConfirmationtId]) }, [lastConfirmationtId])
const onChangeText: (text: string) => void = (text) => { const onChangeText: (text: string) => void = (text) => {
const match = text.match(/.*@(.*)$/) const match = text.match(/.*@(.*)$/)
const note: Note | undefined = route.params?.note if (database && match && match[1] !== '') {
if (database && match && match?.length > 0) { const search = match[1].toLocaleLowerCase()
if (match[1] === '' && note) { setUserSuggestions(users.filter((item) => item.name?.toLocaleLowerCase()?.includes(search)))
const taggedPubKeys = getTaggedPubKeys(note)
getUsers(database, {
includeIds: [...taggedPubKeys, note.pubkey],
}).then((results) => {
if (results) setUserSuggestions(results.filter((item) => item.id !== publicKey))
})
} else {
getUsers(database, { name: match[1] }).then((results) => {
if (results) setUserSuggestions(results.filter((item) => item.id !== publicKey))
})
}
} else { } else {
setUserSuggestions([]) setUserSuggestions([])
} }
@ -130,7 +122,7 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
} }
const renderContactItem: (item: User, index: number) => JSX.Element = (item, index) => ( const renderContactItem: (item: User, index: number) => JSX.Element = (item, index) => (
<TouchableRipple onPress={() => addUserMention(item)}> <TouchableWithoutFeedback onPress={() => addUserMention(item)}>
<View key={index} style={styles.contactRow}> <View key={index} style={styles.contactRow}>
<ProfileData <ProfileData
username={item?.name} username={item?.name}
@ -142,12 +134,17 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
picture={item?.picture} picture={item?.picture}
/> />
</View> </View>
</TouchableRipple> </TouchableWithoutFeedback>
) )
return ( return (
<> <>
<View style={[styles.textInputContainer]}> <ScrollView
style={[styles.textInputContainer]}
keyboardShouldPersistTaps='handled'
scrollEnabled={false}
showsVerticalScrollIndicator={false}
>
{note && ( {note && (
<View style={styles.noteCard}> <View style={styles.noteCard}>
<NoteCard <NoteCard
@ -172,10 +169,12 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
value={content} value={content}
onChangeText={onChangeText} onChangeText={onChangeText}
scrollEnabled scrollEnabled
// cursorColor={theme.colors.inverseOnSurface}
// selectionColor={theme.colors.inverseOnSurface}
/> />
</View> </View>
</View> </ScrollView>
<View style={styles.actions}> <View>
{userSuggestions.length > 0 ? ( {userSuggestions.length > 0 ? (
<View style={[styles.contactsList, { backgroundColor: theme.colors.background }]}> <View style={[styles.contactsList, { backgroundColor: theme.colors.background }]}>
<ScrollView> <ScrollView>
@ -238,9 +237,7 @@ const styles = StyleSheet.create({
bottom: 100, bottom: 100,
width: '100%', width: '100%',
}, },
textInputContainer: { textInputContainer: {},
flex: 1,
},
textInput: { textInput: {
paddingBottom: 0, paddingBottom: 0,
}, },
@ -253,9 +250,6 @@ const styles = StyleSheet.create({
paddingLeft: 16, paddingLeft: 16,
paddingRight: 16, paddingRight: 16,
}, },
actions: {
zIndex: 999,
},
contactsList: { contactsList: {
bottom: 0, bottom: 0,
maxHeight: 180, maxHeight: 180,

View File

@ -35,12 +35,12 @@ export function getNprofile(key: string, relays: string[]): string {
return key return key
} }
export function getNevent(key: string | undefined): string { export function getNevent(key: string | undefined, relays: string[]): string {
if (!key) return '' if (!key) return ''
if (isPublicKey(key)) return key if (isPublicKey(key)) return key
try { try {
return neventEncode({ id: key, relays: [] }) return neventEncode({ id: key, relays })
} catch { } catch {
console.log('Error encoding') console.log('Error encoding')
} }

View File

@ -35,7 +35,7 @@
"nostr-tools": "^1.2.1", "nostr-tools": "^1.2.1",
"react": "18.2.0", "react": "18.2.0",
"react-content-loader": "^6.2.0", "react-content-loader": "^6.2.0",
"react-i18next": "^12.1.4", "react-i18next": "^12.1.5",
"react-native": "0.70.6", "react-native": "0.70.6",
"react-native-action-button": "^2.8.5", "react-native-action-button": "^2.8.5",
"react-native-bidirectional-infinite-scroll": "^0.3.3", "react-native-bidirectional-infinite-scroll": "^0.3.3",
@ -44,10 +44,10 @@
"react-native-gesture-handler": "^2.8.0", "react-native-gesture-handler": "^2.8.0",
"react-native-image-picker": "^5.0.1", "react-native-image-picker": "^5.0.1",
"react-native-multithreading": "^1.1.1", "react-native-multithreading": "^1.1.1",
"react-native-pager-view": "^6.1.2", "react-native-pager-view": "^6.1.4",
"react-native-paper": "^5.1.3", "react-native-paper": "^5.1.3",
"react-native-parsed-text": "^0.0.22", "react-native-parsed-text": "^0.0.22",
"react-native-qrcode-svg": "^6.1.2", "react-native-qrcode-svg": "^6.2.0",
"react-native-quick-sqlite": "^7.0.0", "react-native-quick-sqlite": "^7.0.0",
"react-native-raw-bottom-sheet": "^2.2.0", "react-native-raw-bottom-sheet": "^2.2.0",
"react-native-reanimated": "^2.14.4", "react-native-reanimated": "^2.14.4",
@ -73,7 +73,7 @@
"@babel/runtime": "^7.20.13", "@babel/runtime": "^7.20.13",
"@react-native-community/eslint-config": "^3.2.0", "@react-native-community/eslint-config": "^3.2.0",
"@types/create-hash": "^1.2.2", "@types/create-hash": "^1.2.2",
"@types/jest": "^29.2.6", "@types/jest": "^29.4.0",
"@types/linkify-it": "^3.0.2", "@types/linkify-it": "^3.0.2",
"@types/lodash.debounce": "^4.0.7", "@types/lodash.debounce": "^4.0.7",
"@types/react-native": "^0.70.8", "@types/react-native": "^0.70.8",
@ -95,7 +95,7 @@
"eslint-plugin-promise": "^6.1.0", "eslint-plugin-promise": "^6.1.0",
"eslint-plugin-react": "^7.31.11", "eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"jest": "^29.3.1", "jest": "^29.4.3",
"metro-react-native-babel-preset": "^0.75.0", "metro-react-native-babel-preset": "^0.75.0",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"react-test-renderer": "18.1.0", "react-test-renderer": "18.1.0",

975
yarn.lock

File diff suppressed because it is too large Load Diff