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/bin/noscl setprivate '${{ secrets.NOSTROS_PRIVATE_KEY }}'
~/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.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.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 }}"

View File

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

View File

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

View File

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

View File

@ -52,14 +52,17 @@ public class Relay {
values.put("url", url);
values.put("active", active);
values.put("global_feed", globalFeed);
values.put("deleted_at", 0);
database.replace("nostros_relays", null, values);
}
public void destroy(SQLiteDatabase database) {
public void delete(SQLiteDatabase database) {
String whereClause = "url = ?";
String[] whereArgs = new String[] {
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);");
} 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 {
@ -207,13 +210,13 @@ public class DatabaseModule {
relay.save(database);
}
public void destroyRelay(Relay relay) {
relay.destroy(database);
public void deleteRelay(Relay relay) {
relay.delete(database);
}
public List<Relay> getRelays(ReactApplicationContext reactContext) {
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[] {});
if (cursor.getCount() > 0) {
cursor.moveToFirst();

View File

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

View File

@ -1,6 +1,6 @@
import { t } from 'i18next'
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 { IconButton, Snackbar, Text, TouchableRipple } from 'react-native-paper'
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 QRCode from 'react-native-qrcode-svg'
import { Group } from '../../Functions/DatabaseFunctions/Groups'
import { getNoteRelays } from '../../Functions/DatabaseFunctions/Notes'
import { AppContext } from '../../Contexts/AppContext'
interface GroupShareProps {
group: Group
}
export const GroupShare: React.FC<GroupShareProps> = ({ group }) => {
const { database } = React.useContext(AppContext)
const bottomSheetShareRef = React.useRef<RBSheet>(null)
const [qrCode, setQrCode] = React.useState<any>()
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 (
<View style={styles.mainLayout}>
@ -37,7 +49,7 @@ export const GroupShare: React.FC<GroupShareProps> = ({ group }) => {
<QRCode
quietZone={8}
value={`nostr:${nEvent}`}
size={350}
size={Dimensions.get('window').width - 64}
logoBorderRadius={50}
logoSize={100}
logo={{ uri: group?.picture }}

View File

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

View File

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

View File

@ -40,7 +40,7 @@ interface RelayCardProps {
export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) => {
const theme = useTheme()
const { publicKey } = React.useContext(UserContext)
const { updateRelayItem, relayPool, removeRelayItem, sendRelays, relays } =
const { updateRelayItem, relayPool, removeRelayItem, sendRelays } =
React.useContext(RelayPoolContext)
const { database } = React.useContext(AppContext)
const [relay, setRelay] = React.useState<Relay>()
@ -57,7 +57,7 @@ export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) =>
React.useEffect(() => {
loadRelay()
}, [])
}, [url])
React.useEffect(() => {
if (pushUserHistoric && url && database && publicKey && relayPool) {
@ -103,7 +103,7 @@ export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) =>
relayPool.sendEvent(group, url)
})
})
sendRelays(relays, url)
sendRelays(url)
}
}, [pushUserHistoric])
@ -133,9 +133,8 @@ export const RelayCard: React.FC<RelayCardProps> = ({ url, bottomSheetRef }) =>
const removeRelay: () => void = () => {
if (relay) {
removeRelayItem(relay).then(() => {
removeRelayItem(relay)
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 { navigate } from '../../lib/Navigation'
import { validBlueBirdUrl, validImageUrl, validMediaUrl } from '../../Functions/NativeFunctions'
import Clipboard from '@react-native-clipboard/clipboard'
import FastImage from 'react-native-fast-image'
import { useTranslation } from 'react-i18next'
import { decode, PaymentRequestObject, TagsObject } from 'bolt11'
@ -22,7 +21,6 @@ interface TextContentProps {
showPreview?: boolean
onPressUser?: (user: User) => void
numberOfLines?: number
copyOnPress?: boolean
}
export const TextContent: React.FC<TextContentProps> = ({
@ -31,7 +29,6 @@ export const TextContent: React.FC<TextContentProps> = ({
showPreview = true,
onPressUser = () => {},
numberOfLines,
copyOnPress = true,
}) => {
const theme = useTheme()
const { t } = useTranslation('common')
@ -267,8 +264,8 @@ export const TextContent: React.FC<TextContentProps> = ({
},
]}
childrenProps={{ allowFontScaling: false }}
onLongPress={copyOnPress ? () => Clipboard.setString(text) : undefined}
numberOfLines={numberOfLines}
selectable
>
{text}
</ParsedText>

View File

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

View File

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

View File

@ -31,7 +31,7 @@ export const searchRelays: (
relayUrl: string,
db: QuickSQLiteConnection,
) => 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 items: object[] = getItems(results)
const relays: Relay[] = items.map((object) => databaseToEntity(object))
@ -39,7 +39,7 @@ export const searchRelays: (
}
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 items: object[] = getItems(resultSet)
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) => {
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 items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object))
@ -58,23 +58,13 @@ export const getRelay: (db: QuickSQLiteConnection, url: string) => Promise<Relay
db,
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 items: object[] = getItems(resultSet)
const relays: Relay[] = items.map((object) => databaseToEntity(object))
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: (
db: QuickSQLiteConnection,
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",
"feed": "Feed",
"zaps": "Zaps",
"disabled": "Disabled",
"longPressZap": "Long press Zaps",
"longPressZapDescription": "Define a default amount to automatically generate invoices after a long press on the Zap button.",
"defaultZapAmount": "Defaul Zap amount",
"update": "Update",
"disable": "Disable"
"disabled": "deaktiviert",
"longPressZap": "Zaps lange drücken",
"longPressZapDescription": "Lege den voreingestellten Betrag fest, mit der Invoices erstellt werden sollen, wenn Zaps lange gedrückt wird.",
"defaultZapAmount": "Standard Zap Betrag",
"update": "Aktualisieren",
"disable": "Deaktivieren"
},
"noteActions": {
"copy": "Kopieren",
"share": "Teilen",
"view": "Ansehen"
},
"noteCard": {
"notifications": {
"zapServerError": "Beim Anfragen des Invoice hat es einen Fehler vom Server gegeben."
},
"answering": "{{pubkey}} antworten",
"reposting": "{{pubkey}} reposten",
"seeParent": "Notiz ansehen",
@ -110,7 +118,7 @@
"cancel": "Abbrechen",
"copy": "Kopieren",
"open": "Öffne Wallet",
"anonTip": "Anonymous tip",
"anonTip": "Anonymer Tip",
"zap": "Zap"
},
"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.",
"centralized": "Zentralisiert",
"small": "Klein",
"contacts": "# Kontakte",
"contacts": "zugewiesene Kontakte",
"resilienceMode": "Beständigkeit (experimental)",
"relayName": "Addresse",
"relayName": "Adresse",
"globalFeed": "Globaler Feed",
"active": "Aktiv",
"labelAdd": "Beschreibung des Relays",
@ -233,7 +241,6 @@
"alreadyExists": "Relay existiert bereits"
}
},
"relayCard": {
"pushDone": "Fertig",
"pushHistoricTitle": "Alle meine Daten hochladen",
@ -241,7 +248,7 @@
"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.",
"pushConsent": "Ich schiebe meine Daten auf einen privaten Relay",
"pushingEvent": "Event {{eventId}} pushed",
"pushingEvent": "Event {{eventId}} hochgeladen",
"pushRelay": "Alle meine Daten hochladen",
"cancel": "Abbrechen",
"moreInfo": "Mehr Infos",
@ -266,10 +273,9 @@
"pubkeyCopied": "Öffentlichen Schlüssel kopiert.",
"contactCopied": "Kontakt kopiert.",
"urlCopied": "URL kopiert.",
"pushCompleted": "All your data has been pushed."
"pushCompleted": "Alle deine Daten wurden hochgeladen"
}
},
"profileConfigPage": {
"notifications": {
"nsecCopied": "Privater Schlüssel kopiert.",
@ -318,7 +324,7 @@
"active": "Relay aktiviert.",
"desactive": "Relay deaktiviert."
},
"invoice": "Zap",
"invoice": "Tip",
"message": "Nachricht",
"follow": "Folgen",
"isFollower": "folgt dir",
@ -327,15 +333,15 @@
"relaysDescription": "Dies sind {{username}}'s Relays, aktiviere diejenigen, zu denen du verbunden werden möchtest."
},
"groupPage": {
"typeMessage": "Nachricht schreiben"
"typeMessage": "Nachricht schreiben",
"replyText": "Nachricht"
},
"groupHeaderIcon": {
"delete": "Leave group",
"share": "Share group",
"edit": "Edit group"
"delete": "Gruppe verlassen",
"share": "Gruppe teilen",
"edit": "Gruppe editieren"
},
"groupsFeed": {
"delete": "Gruppe verlassen",
"updateTitle": "Gruppe aktualisieren",
"newGroupName": "Name",
"newGroupDescription": "Beschreibung",
@ -345,7 +351,8 @@
"createTitle": "Gruppe erstellen",
"groupId": "Gruppen ID",
"addTitle": "Bestehende Gruppe hinzufügen",
"add": "Gruppe hinzufügen"
"add": "Gruppe hinzufügen",
"delete": "Gruppe löschen"
},
"homePage": {
"clipboardTitle": "Nostr Schlüssel entdeckt",
@ -368,7 +375,6 @@
"copyNip05": "NIP-05 kopieren",
"copyNPub": "Schlüssel kopieren"
},
"profileCard": {
"notifications": {
"contactAdded": "Aboniert",
@ -376,7 +382,7 @@
"userUnblocked": "Profil entblockt",
"userBlocked": "Profile geblockt"
},
"invoice": "Zap",
"invoice": "Tip",
"message": "Nachricht",
"follow": "Folgen",
"block": "Blockieren",
@ -384,9 +390,14 @@
"unblock": "Erlauben",
"unfollow": "Abo entfernen",
"relaysTitle": "Relays",
"blocked": "Blocked",
"mute": "Mute",
"muted": "Muted"
"blocked": "Blockiert",
"mute": "Stumm",
"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": {
"openMessage": "Unterhaltung beginnen",

View File

@ -89,11 +89,19 @@
"disabled": "Disabled",
"longPressZap": "Long press Zaps",
"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",
"disable": "Disable"
},
"noteActions": {
"copy": "Copy",
"share": "Share",
"view": "View"
},
"noteCard": {
"notifications": {
"zapServerError": "There was an error on the server while trying to request the invoice."
},
"answering": "Answer to {{pubkey}}",
"reposting": "Reposted {{pubkey}}",
"userBlocked": "User blocked",
@ -127,7 +135,7 @@
"skip": "Skip",
"confirmTitle": "Type the words in the right order.",
"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",
"continue": "Continue",
"mnemonicTitle": "These words or your private key are the only way to recover your account.",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@
"nostr-tools": "^1.2.1",
"react": "18.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-action-button": "^2.8.5",
"react-native-bidirectional-infinite-scroll": "^0.3.3",
@ -44,10 +44,10 @@
"react-native-gesture-handler": "^2.8.0",
"react-native-image-picker": "^5.0.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-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-raw-bottom-sheet": "^2.2.0",
"react-native-reanimated": "^2.14.4",
@ -73,7 +73,7 @@
"@babel/runtime": "^7.20.13",
"@react-native-community/eslint-config": "^3.2.0",
"@types/create-hash": "^1.2.2",
"@types/jest": "^29.2.6",
"@types/jest": "^29.4.0",
"@types/linkify-it": "^3.0.2",
"@types/lodash.debounce": "^4.0.7",
"@types/react-native": "^0.70.8",
@ -95,7 +95,7 @@
"eslint-plugin-promise": "^6.1.0",
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0",
"jest": "^29.3.1",
"jest": "^29.4.3",
"metro-react-native-babel-preset": "^0.75.0",
"prettier": "^2.8.4",
"react-test-renderer": "18.1.0",

975
yarn.lock

File diff suppressed because it is too large Load Diff