mirror of
https://github.com/KoalaSat/nostros.git
synced 2024-09-29 06:30:47 +00:00
Share contact and Upload image preview (#221)
This commit is contained in:
commit
25de204b0c
@ -2,6 +2,8 @@
|
|||||||
package="com.nostros">
|
package="com.nostros">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<!-- required for react-native-share base64 sharing -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
|
@ -156,8 +156,8 @@ export const LnPayment: React.FC<TextContentProps> = ({ open, setOpen, event, us
|
|||||||
>
|
>
|
||||||
<Card style={styles.qrContainer}>
|
<Card style={styles.qrContainer}>
|
||||||
<Card.Content>
|
<Card.Content>
|
||||||
<View>
|
<View style={styles.qr}>
|
||||||
<QRCode value={invoice} size={350} />
|
<QRCode value={invoice} size={300} quietZone={8}/>
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.qrText}>
|
<View style={styles.qrText}>
|
||||||
<Text>{monto} </Text>
|
<Text>{monto} </Text>
|
||||||
@ -222,6 +222,11 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
justifyContent: 'space-around',
|
justifyContent: 'space-around',
|
||||||
},
|
},
|
||||||
|
qr: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 16
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default LnPayment
|
export default LnPayment
|
||||||
|
@ -2,7 +2,7 @@ import { t } from 'i18next'
|
|||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import Clipboard from '@react-native-clipboard/clipboard'
|
import Clipboard from '@react-native-clipboard/clipboard'
|
||||||
import { Card, IconButton, Snackbar, Text, useTheme } from 'react-native-paper'
|
import { Card, IconButton, Snackbar, Text, TouchableRipple, useTheme } from 'react-native-paper'
|
||||||
import { AppContext } from '../../Contexts/AppContext'
|
import { AppContext } from '../../Contexts/AppContext'
|
||||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||||
import { UserContext } from '../../Contexts/UserContext'
|
import { UserContext } from '../../Contexts/UserContext'
|
||||||
@ -12,6 +12,7 @@ import {
|
|||||||
updateUserContact,
|
updateUserContact,
|
||||||
User,
|
User,
|
||||||
} from '../../Functions/DatabaseFunctions/Users'
|
} from '../../Functions/DatabaseFunctions/Users'
|
||||||
|
import Share from 'react-native-share'
|
||||||
import { populatePets, usernamePubKey } from '../../Functions/RelayFunctions/Users'
|
import { populatePets, usernamePubKey } from '../../Functions/RelayFunctions/Users'
|
||||||
import LnPayment from '../LnPayment'
|
import LnPayment from '../LnPayment'
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||||
@ -19,6 +20,7 @@ import { navigate, push } from '../../lib/Navigation'
|
|||||||
import RBSheet from 'react-native-raw-bottom-sheet'
|
import RBSheet from 'react-native-raw-bottom-sheet'
|
||||||
import { getNpub } from '../../lib/nostr/Nip19'
|
import { getNpub } from '../../lib/nostr/Nip19'
|
||||||
import ProfileData from '../ProfileData'
|
import ProfileData from '../ProfileData'
|
||||||
|
import QRCode from 'react-native-qrcode-svg'
|
||||||
|
|
||||||
interface ProfileCardProps {
|
interface ProfileCardProps {
|
||||||
userPubKey: string
|
userPubKey: string
|
||||||
@ -32,6 +34,7 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({
|
|||||||
showImages = true,
|
showImages = true,
|
||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
const bottomSheetShareRef = React.useRef<RBSheet>(null)
|
||||||
const { database } = React.useContext(AppContext)
|
const { database } = React.useContext(AppContext)
|
||||||
const { publicKey } = React.useContext(UserContext)
|
const { publicKey } = React.useContext(UserContext)
|
||||||
const { relayPool } = React.useContext(RelayPoolContext)
|
const { relayPool } = React.useContext(RelayPoolContext)
|
||||||
@ -40,6 +43,7 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({
|
|||||||
const [openLn, setOpenLn] = React.useState<boolean>(false)
|
const [openLn, setOpenLn] = React.useState<boolean>(false)
|
||||||
const [isContact, setIsContact] = React.useState<boolean>()
|
const [isContact, setIsContact] = React.useState<boolean>()
|
||||||
const [showNotification, setShowNotification] = React.useState<undefined | string>()
|
const [showNotification, setShowNotification] = React.useState<undefined | string>()
|
||||||
|
const [qrCode, setQrCode] = React.useState<any>()
|
||||||
const nPub = React.useMemo(() => getNpub(userPubKey), [userPubKey])
|
const nPub = React.useMemo(() => getNpub(userPubKey), [userPubKey])
|
||||||
const username = React.useMemo(() => usernamePubKey(user?.name ?? '', nPub), [nPub, user])
|
const username = React.useMemo(() => usernamePubKey(user?.name ?? '', nPub), [nPub, user])
|
||||||
|
|
||||||
@ -96,6 +100,21 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({
|
|||||||
push('Profile', { pubKey: userPubKey, title: username })
|
push('Profile', { pubKey: userPubKey, title: username })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<Card onPress={goToProfile}>
|
<Card onPress={goToProfile}>
|
||||||
@ -144,14 +163,6 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({
|
|||||||
<Text>{isContact ? t('profileCard.unfollow') : t('profileCard.follow')}</Text>
|
<Text>{isContact ? t('profileCard.unfollow') : t('profileCard.follow')}</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
<View style={styles.actionButton}>
|
|
||||||
<IconButton
|
|
||||||
icon={blocked ? 'account-cancel' : 'account-cancel-outline'}
|
|
||||||
size={28}
|
|
||||||
onPress={onChangeBlockUser}
|
|
||||||
/>
|
|
||||||
<Text>{t(blocked ? 'profileCard.unblock' : 'profileCard.block')}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={styles.actionButton}>
|
<View style={styles.actionButton}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon='message-plus-outline'
|
icon='message-plus-outline'
|
||||||
@ -165,14 +176,13 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({
|
|||||||
</View>
|
</View>
|
||||||
<View style={styles.actionButton}>
|
<View style={styles.actionButton}>
|
||||||
<IconButton
|
<IconButton
|
||||||
icon='content-copy'
|
icon='share-variant-outline'
|
||||||
size={28}
|
size={28}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setShowNotification('npubCopied')
|
bottomSheetShareRef.current?.open()
|
||||||
Clipboard.setString(nPub ?? '')
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text>{t('profileCard.copyNPub')}</Text>
|
<Text>{t('profileCard.share')}</Text>
|
||||||
</View>
|
</View>
|
||||||
{user?.lnurl && (
|
{user?.lnurl && (
|
||||||
<View style={styles.actionButton}>
|
<View style={styles.actionButton}>
|
||||||
@ -187,6 +197,14 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({
|
|||||||
</>
|
</>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
<View style={styles.actionButton}>
|
||||||
|
<IconButton
|
||||||
|
icon={blocked ? 'account-cancel' : 'account-cancel-outline'}
|
||||||
|
size={28}
|
||||||
|
onPress={onChangeBlockUser}
|
||||||
|
/>
|
||||||
|
<Text>{t(blocked ? 'profileCard.unblock' : 'profileCard.block')}</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
{showNotification && (
|
{showNotification && (
|
||||||
<Snackbar
|
<Snackbar
|
||||||
@ -200,6 +218,60 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({
|
|||||||
</Snackbar>
|
</Snackbar>
|
||||||
)}
|
)}
|
||||||
<LnPayment setOpen={setOpenLn} open={openLn} user={user} />
|
<LnPayment setOpen={setOpenLn} open={openLn} user={user} />
|
||||||
|
<RBSheet ref={bottomSheetShareRef} closeOnDragDown={true} customStyles={bottomSheetStyles}>
|
||||||
|
<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: user?.id ?? 'nostrosshare'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<QRCode
|
||||||
|
quietZone={8}
|
||||||
|
value={`nostr:${nPub}`}
|
||||||
|
size={350}
|
||||||
|
logoBorderRadius={50}
|
||||||
|
logoSize={100}
|
||||||
|
logo={{ uri: user?.picture }}
|
||||||
|
getRef={setQrCode}
|
||||||
|
/>
|
||||||
|
</TouchableRipple>
|
||||||
|
</View>
|
||||||
|
<View style={styles.shareActionButton}>
|
||||||
|
<IconButton
|
||||||
|
icon='key-outline'
|
||||||
|
size={28}
|
||||||
|
onPress={() => {
|
||||||
|
setShowNotification('npubCopied')
|
||||||
|
Clipboard.setString(nPub ?? '')
|
||||||
|
bottomSheetShareRef.current?.close()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text>{t('profileCard.copyNPub')}</Text>
|
||||||
|
</View>
|
||||||
|
{user?.nip05 && (
|
||||||
|
<View style={styles.shareActionButton}>
|
||||||
|
<IconButton
|
||||||
|
icon='check-decagram-outline'
|
||||||
|
size={28}
|
||||||
|
onPress={() => {
|
||||||
|
setShowNotification('npubCopied')
|
||||||
|
Clipboard.setString(user?.nip05 ?? '')
|
||||||
|
bottomSheetShareRef.current?.close()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text>{t('profileCard.copyNip05')}</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</RBSheet>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -247,7 +319,18 @@ const styles = StyleSheet.create({
|
|||||||
actionButton: {
|
actionButton: {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
flexBasis: '33.333333%',
|
flexBasis: '25%',
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
qr: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 16,
|
||||||
|
},
|
||||||
|
shareActionButton: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexBasis: '50%',
|
||||||
marginBottom: 4,
|
marginBottom: 4,
|
||||||
},
|
},
|
||||||
list: {
|
list: {
|
||||||
|
@ -6,6 +6,7 @@ import { Linking, StyleSheet } from 'react-native'
|
|||||||
import { Text } from 'react-native-paper'
|
import { Text } from 'react-native-paper'
|
||||||
import { Config } from '../Pages/ConfigPage'
|
import { Config } from '../Pages/ConfigPage'
|
||||||
import { imageHostingServices } from '../Constants/Services'
|
import { imageHostingServices } from '../Constants/Services'
|
||||||
|
import { randomInt } from '../Functions/NativeFunctions'
|
||||||
|
|
||||||
export interface AppContextProps {
|
export interface AppContextProps {
|
||||||
init: () => void
|
init: () => void
|
||||||
@ -22,6 +23,7 @@ export interface AppContextProps {
|
|||||||
setImageHostingService: (imageHostingService: string) => void
|
setImageHostingService: (imageHostingService: string) => void
|
||||||
setSatoshi: (showPublicImages: 'kebab' | 'sats') => void
|
setSatoshi: (showPublicImages: 'kebab' | 'sats') => void
|
||||||
getSatoshiSymbol: (fontSize?: number) => JSX.Element
|
getSatoshiSymbol: (fontSize?: number) => JSX.Element
|
||||||
|
getImageHostingService: () => string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppContextProviderProps {
|
export interface AppContextProviderProps {
|
||||||
@ -42,6 +44,7 @@ export const initialAppContext: AppContextProps = {
|
|||||||
setSatoshi: () => {},
|
setSatoshi: () => {},
|
||||||
imageHostingService: Object.keys(imageHostingServices)[0],
|
imageHostingService: Object.keys(imageHostingServices)[0],
|
||||||
setImageHostingService: () => {},
|
setImageHostingService: () => {},
|
||||||
|
getImageHostingService: () => "",
|
||||||
getSatoshiSymbol: () => <></>,
|
getSatoshiSymbol: () => <></>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +104,14 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getImageHostingService: () => string = () => {
|
||||||
|
if (imageHostingService !== 'random') return imageHostingService
|
||||||
|
|
||||||
|
const randomIndex = randomInt(1, Object.keys(imageHostingServices).length)
|
||||||
|
|
||||||
|
return Object.keys(imageHostingServices)[randomIndex - 1]
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(init, [])
|
useEffect(init, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -108,6 +119,7 @@ export const AppContextProvider = ({ children }: AppContextProviderProps): JSX.E
|
|||||||
value={{
|
value={{
|
||||||
imageHostingService,
|
imageHostingService,
|
||||||
setImageHostingService,
|
setImageHostingService,
|
||||||
|
getImageHostingService,
|
||||||
init,
|
init,
|
||||||
loadingDb,
|
loadingDb,
|
||||||
database,
|
database,
|
||||||
|
@ -61,3 +61,5 @@ export const validNip21: (string: string | undefined) => boolean = (string) => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const randomInt: (min: number, max: number) => number = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
|
||||||
|
@ -47,8 +47,11 @@
|
|||||||
"isNotContact": "Not following",
|
"isNotContact": "Not following",
|
||||||
"contentWarning": "Sensitive content",
|
"contentWarning": "Sensitive content",
|
||||||
"send": "Send",
|
"send": "Send",
|
||||||
"imageUploaded": "Your file has been uploaded.\nConsider donating to the service: {{uri}}",
|
"imageUploaded": "Your file has been uploaded.\nConsider donating to the service:\n{{uri}}",
|
||||||
"imageUploadErro": "There was an error while trying to upload your file"
|
"imageUploadErro": "There was an error while trying to upload your file",
|
||||||
|
"uploadImage": "Upload image now",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"poweredBy": "Powered by {{uri}}"
|
||||||
},
|
},
|
||||||
"menuItems": {
|
"menuItems": {
|
||||||
"relays": "Relays",
|
"relays": "Relays",
|
||||||
@ -64,7 +67,8 @@
|
|||||||
"showPublicImages": "Show images on public feed",
|
"showPublicImages": "Show images on public feed",
|
||||||
"showSensitive": "Show sensitive notes",
|
"showSensitive": "Show sensitive notes",
|
||||||
"satoshi": "Satoshi symbol",
|
"satoshi": "Satoshi symbol",
|
||||||
"imageHostingService": "Image hosting service"
|
"imageHostingService": "Image hosting service",
|
||||||
|
"random": "Random"
|
||||||
},
|
},
|
||||||
"noteCard": {
|
"noteCard": {
|
||||||
"answering": "Answer to {{pubkey}}",
|
"answering": "Answer to {{pubkey}}",
|
||||||
@ -233,6 +237,8 @@
|
|||||||
"unfollow": "Following",
|
"unfollow": "Following",
|
||||||
"block": "Block",
|
"block": "Block",
|
||||||
"unblock": "Unblock",
|
"unblock": "Unblock",
|
||||||
|
"share": "Share",
|
||||||
|
"copyNip05": "Copy NIP-05",
|
||||||
"copyNPub": "Copy key"
|
"copyNPub": "Copy key"
|
||||||
},
|
},
|
||||||
"conversationsFeed": {
|
"conversationsFeed": {
|
||||||
|
@ -46,7 +46,12 @@
|
|||||||
"isContact": "Siguiendo",
|
"isContact": "Siguiendo",
|
||||||
"isNotContact": "Sin seguir",
|
"isNotContact": "Sin seguir",
|
||||||
"contentWarning": "Contenido sensible",
|
"contentWarning": "Contenido sensible",
|
||||||
"send": "Enviar"
|
"send": "Enviar",
|
||||||
|
"imageUploaded": "Tu archivo se ha subido.\nConsidera donar al servicio:\n{{uri}}",
|
||||||
|
"imageUploadErro": "Se ha producido un error al subir la imagen.",
|
||||||
|
"uploadImage": "Subir imagen ahora",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"poweredBy": "Servido por {{uri}}"
|
||||||
},
|
},
|
||||||
"menuItems": {
|
"menuItems": {
|
||||||
"relays": "Relays",
|
"relays": "Relays",
|
||||||
@ -62,7 +67,8 @@
|
|||||||
"showPublicImages": "Mostrar imágenes en feed global",
|
"showPublicImages": "Mostrar imágenes en feed global",
|
||||||
"showSensitive": "Mostrar notas sensibles",
|
"showSensitive": "Mostrar notas sensibles",
|
||||||
"satoshi": "Símbolo de satoshi",
|
"satoshi": "Símbolo de satoshi",
|
||||||
"imageHostingService": "Servicio de subida de imágenes"
|
"imageHostingService": "Servicio de subida de imágenes",
|
||||||
|
"random": "Aleatorio"
|
||||||
},
|
},
|
||||||
"noteCard": {
|
"noteCard": {
|
||||||
"answering": "Responder a {{pubkey}}",
|
"answering": "Responder a {{pubkey}}",
|
||||||
@ -217,8 +223,10 @@
|
|||||||
"message": "Mensaje",
|
"message": "Mensaje",
|
||||||
"follow": "Seguir",
|
"follow": "Seguir",
|
||||||
"block": "Bloquear",
|
"block": "Bloquear",
|
||||||
|
"share": "Share",
|
||||||
"unblock": "Desbloquear",
|
"unblock": "Desbloquear",
|
||||||
"unfollow": "Siguiendo",
|
"unfollow": "Siguiendo",
|
||||||
|
"copyNip05": "Copiar NIP-05",
|
||||||
"copyNPub": "Copiar clave"
|
"copyNPub": "Copiar clave"
|
||||||
},
|
},
|
||||||
"conversationsFeed": {
|
"conversationsFeed": {
|
||||||
|
@ -46,7 +46,12 @@
|
|||||||
"isContact": "Following",
|
"isContact": "Following",
|
||||||
"isNotContact": "Not following",
|
"isNotContact": "Not following",
|
||||||
"contentWarning": "Делекатный контент",
|
"contentWarning": "Делекатный контент",
|
||||||
"send": "Send"
|
"send": "Send",
|
||||||
|
"imageUploaded": "Tu archivo se ha subido.\nConsidera donar al servicio:\n{{uri}}",
|
||||||
|
"imageUploadErro": "Se ha producido un error al subir la imagen.",
|
||||||
|
"uploadImage": "Subir imagen ahora",
|
||||||
|
"cancel": "Отменить",
|
||||||
|
"poweredBy": "Powered by {{uri}}"
|
||||||
},
|
},
|
||||||
"menuItems": {
|
"menuItems": {
|
||||||
"relays": "Реле",
|
"relays": "Реле",
|
||||||
@ -62,7 +67,8 @@
|
|||||||
"configPage": {
|
"configPage": {
|
||||||
"showPublicImages": "Show images on public feed",
|
"showPublicImages": "Show images on public feed",
|
||||||
"showSensitive": "Show sensitive notes",
|
"showSensitive": "Show sensitive notes",
|
||||||
"satoshi": "Satoshi symbol"
|
"satoshi": "Satoshi symbol",
|
||||||
|
"random": "Random"
|
||||||
},
|
},
|
||||||
"noteCard": {
|
"noteCard": {
|
||||||
"answering": "Ответить {{pubkey}}",
|
"answering": "Ответить {{pubkey}}",
|
||||||
@ -218,6 +224,8 @@
|
|||||||
"block": "Block",
|
"block": "Block",
|
||||||
"unblock": "Desbloquear",
|
"unblock": "Desbloquear",
|
||||||
"unfollow": "Following",
|
"unfollow": "Following",
|
||||||
|
"share": "Share",
|
||||||
|
"copyNip05": "Copy NIP-05",
|
||||||
"copyNPub": "Copy key"
|
"copyNPub": "Copy key"
|
||||||
},
|
},
|
||||||
"conversationsFeed": {
|
"conversationsFeed": {
|
||||||
|
@ -67,10 +67,10 @@ export const ConfigPage: React.FC = () => {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const imageHostingOptions = React.useMemo(() => {
|
const imageHostingOptions = React.useMemo(() => {
|
||||||
return Object.keys(imageHostingServices).map((service, index) => {
|
return ['random', ...Object.keys(imageHostingServices)].map((service, index) => {
|
||||||
return {
|
return {
|
||||||
key: index,
|
key: index,
|
||||||
title: <Text>{imageHostingServices[service].uri}</Text>,
|
title: <Text>{imageHostingServices[service]?.uri ?? t(`configPage.${service}`)}</Text>,
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
setImageHostingService(service)
|
setImageHostingService(service)
|
||||||
SInfo.getItem('config', {}).then((result) => {
|
SInfo.getItem('config', {}).then((result) => {
|
||||||
@ -141,7 +141,12 @@ export const ConfigPage: React.FC = () => {
|
|||||||
<List.Item
|
<List.Item
|
||||||
title={t('configPage.imageHostingService')}
|
title={t('configPage.imageHostingService')}
|
||||||
onPress={() => bottomSheetImageHostingRef.current?.open()}
|
onPress={() => bottomSheetImageHostingRef.current?.open()}
|
||||||
right={() => <Text>{imageHostingServices[imageHostingService].uri}</Text>}
|
right={() => (
|
||||||
|
<Text>
|
||||||
|
{imageHostingServices[imageHostingService]?.uri ??
|
||||||
|
t(`configPage.${imageHostingService}`)}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
<RBSheet ref={bottomSheetSatoshiRef} closeOnDragDown={true} customStyles={bottomSheetStyles}>
|
<RBSheet ref={bottomSheetSatoshiRef} closeOnDragDown={true} customStyles={bottomSheetStyles}>
|
||||||
<FlatList
|
<FlatList
|
||||||
|
@ -167,15 +167,19 @@ export const RelaysPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
<RBSheet ref={bottomSheetAddRef} closeOnDragDown={true} customStyles={rbSheetCustomStyles}>
|
<RBSheet ref={bottomSheetAddRef} closeOnDragDown={true} customStyles={rbSheetCustomStyles}>
|
||||||
<View style={styles.addRelay}>
|
<View style={styles.addRelay}>
|
||||||
<TextInput
|
<View style={styles.bottomDrawerButton}>
|
||||||
mode='outlined'
|
<TextInput
|
||||||
label={t('relaysPage.labelAdd') ?? ''}
|
mode='outlined'
|
||||||
onChangeText={setAddRelayInput}
|
label={t('relaysPage.labelAdd') ?? ''}
|
||||||
value={addRelayInput}
|
onChangeText={setAddRelayInput}
|
||||||
/>
|
value={addRelayInput}
|
||||||
<Button mode='contained' onPress={onPressAddRelay}>
|
/>
|
||||||
{t('relaysPage.add')}
|
</View>
|
||||||
</Button>
|
<View style={styles.bottomDrawerButton}>
|
||||||
|
<Button mode='contained' onPress={onPressAddRelay}>
|
||||||
|
{t('relaysPage.add')}
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
<Button
|
<Button
|
||||||
mode='outlined'
|
mode='outlined'
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
@ -224,6 +228,9 @@ const styles = StyleSheet.create({
|
|||||||
title: {
|
title: {
|
||||||
paddingLeft: 16,
|
paddingLeft: 16,
|
||||||
},
|
},
|
||||||
|
bottomDrawerButton: {
|
||||||
|
paddingBottom: 16,
|
||||||
|
},
|
||||||
container: {
|
container: {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
paddingBottom: 32,
|
paddingBottom: 32,
|
||||||
@ -243,7 +250,6 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
addRelay: {
|
addRelay: {
|
||||||
alignContent: 'center',
|
alignContent: 'center',
|
||||||
height: '80%',
|
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
},
|
},
|
||||||
relayActions: {
|
relayActions: {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react'
|
import React, { useContext, useEffect, useMemo, useState } from 'react'
|
||||||
import { StyleSheet, View } from 'react-native'
|
import { StyleSheet, View } from 'react-native'
|
||||||
import { AppContext } from '../../Contexts/AppContext'
|
import { AppContext } from '../../Contexts/AppContext'
|
||||||
import { Event } from '../../lib/nostr/Events'
|
import { Event } from '../../lib/nostr/Events'
|
||||||
@ -9,15 +9,17 @@ import { Note } from '../../Functions/DatabaseFunctions/Notes'
|
|||||||
import { getETags, getTaggedPubKeys } from '../../Functions/RelayFunctions/Events'
|
import { getETags, getTaggedPubKeys } 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 { launchImageLibrary } from 'react-native-image-picker'
|
import { Asset, launchImageLibrary } from 'react-native-image-picker'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
IconButton,
|
IconButton,
|
||||||
Snackbar,
|
Snackbar,
|
||||||
Switch,
|
Switch,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
TouchableRipple,
|
TouchableRipple,
|
||||||
|
useTheme,
|
||||||
} from 'react-native-paper'
|
} from 'react-native-paper'
|
||||||
import { UserContext } from '../../Contexts/UserContext'
|
import { UserContext } from '../../Contexts/UserContext'
|
||||||
import { goBack } from '../../lib/Navigation'
|
import { goBack } from '../../lib/Navigation'
|
||||||
@ -25,13 +27,15 @@ 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 { imageHostingServices } from '../../Constants/Services'
|
import { imageHostingServices } from '../../Constants/Services'
|
||||||
|
import RBSheet from 'react-native-raw-bottom-sheet'
|
||||||
|
|
||||||
interface SendPageProps {
|
interface SendPageProps {
|
||||||
route: { params: { note: Note; type?: 'reply' | 'repost' } | undefined }
|
route: { params: { note: Note; type?: 'reply' | 'repost' } | undefined }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
||||||
const { database, imageHostingService } = useContext(AppContext)
|
const theme = useTheme()
|
||||||
|
const { database, getImageHostingService } = useContext(AppContext)
|
||||||
const { publicKey } = useContext(UserContext)
|
const { publicKey } = useContext(UserContext)
|
||||||
const { relayPool, lastConfirmationtId } = useContext(RelayPoolContext)
|
const { relayPool, lastConfirmationtId } = useContext(RelayPoolContext)
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
@ -43,7 +47,10 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
|||||||
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)
|
||||||
|
const [imageUpload, setImageUpload] = useState<Asset>()
|
||||||
const note = React.useMemo(() => route.params?.note, [])
|
const note = React.useMemo(() => route.params?.note, [])
|
||||||
|
const [imageHostingService] = useState<string>(getImageHostingService())
|
||||||
|
const bottomSheetImageRef = React.useRef<RBSheet>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSending) goBack()
|
if (isSending) goBack()
|
||||||
@ -76,32 +83,44 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
|||||||
return `@${user.name ?? formatPubKey(user.id)}`
|
return `@${user.name ?? formatPubKey(user.id)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const onUploadImage: () => void = async () => {
|
const getImage: () => void = () => {
|
||||||
launchImageLibrary({ selectionLimit: 1, quality: 0, mediaType: 'photo' }, async (result) => {
|
launchImageLibrary({ selectionLimit: 1, mediaType: 'photo' }, async (result) => {
|
||||||
const assets = result?.assets
|
const assets = result?.assets
|
||||||
if (assets && assets.length > 0) {
|
if (assets && assets.length > 0) {
|
||||||
const file = assets[0]
|
const file = assets[0]
|
||||||
if (file.uri && file.type && file.fileName) {
|
if (file.uri && file.type && file.fileName) {
|
||||||
imageHostingServices[imageHostingService]
|
setImageUpload(file)
|
||||||
.sendFunction(file.uri, file.type, file.fileName)
|
bottomSheetImageRef.current?.open()
|
||||||
.then((imageUri) => {
|
|
||||||
setShowNotification('imageUploaded')
|
|
||||||
setUploadingFile(false)
|
|
||||||
setContent((prev) => `${prev}\n\n${imageUri}`)
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
setShowNotification('imageUploadErro')
|
|
||||||
setUploadingFile(false)
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
setUploadingFile(false)
|
setUploadingFile(false)
|
||||||
|
setShowNotification('imageUploadErro')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setUploadingFile(false)
|
setUploadingFile(false)
|
||||||
|
setShowNotification('imageUploadErro')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uploadImage: () => void = async () => {
|
||||||
|
if (imageUpload?.uri && imageUpload.type && imageUpload.fileName) {
|
||||||
|
imageHostingServices[imageHostingService]
|
||||||
|
.sendFunction(imageUpload.uri, imageUpload.type, imageUpload.fileName)
|
||||||
|
.then((imageUri) => {
|
||||||
|
bottomSheetImageRef.current?.close()
|
||||||
|
setUploadingFile(false)
|
||||||
|
setContent((prev) => `${prev}\n\n${imageUri}`)
|
||||||
|
setImageUpload(undefined)
|
||||||
|
setShowNotification('imageUploaded')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
bottomSheetImageRef.current?.close()
|
||||||
|
setUploadingFile(false)
|
||||||
|
setShowNotification('imageUploadErro')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onPressSend: () => void = () => {
|
const onPressSend: () => void = () => {
|
||||||
if (database && publicKey) {
|
if (database && publicKey) {
|
||||||
setIsSending(true)
|
setIsSending(true)
|
||||||
@ -179,6 +198,21 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
|||||||
</TouchableRipple>
|
</TouchableRipple>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<View style={[styles.textInputContainer, { paddingBottom: note ? 200 : 10 }]}>
|
<View style={[styles.textInputContainer, { paddingBottom: note ? 200 : 10 }]}>
|
||||||
@ -222,7 +256,7 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
|||||||
icon='image-outline'
|
icon='image-outline'
|
||||||
size={25}
|
size={25}
|
||||||
style={styles.imageButton}
|
style={styles.imageButton}
|
||||||
onPress={onUploadImage}
|
onPress={getImage}
|
||||||
disabled={uploadingFile}
|
disabled={uploadingFile}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@ -239,6 +273,31 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
<RBSheet ref={bottomSheetImageRef} closeOnDragDown={true} customStyles={bottomSheetStyles}>
|
||||||
|
<Card style={styles.imageUploadPreview}>
|
||||||
|
{imageUpload && (
|
||||||
|
<Card.Cover source={{ uri: imageUpload?.uri ?? '' }} resizeMode='contain' />
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
<Text>{t('sendPage.poweredBy', { uri: imageHostingServices[imageHostingService].uri })}</Text>
|
||||||
|
<Button
|
||||||
|
style={styles.buttonSpacer}
|
||||||
|
mode='contained'
|
||||||
|
onPress={uploadImage}
|
||||||
|
loading={uploadingFile}
|
||||||
|
>
|
||||||
|
{t('sendPage.uploadImage')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
mode='outlined'
|
||||||
|
onPress={() => {
|
||||||
|
bottomSheetImageRef.current?.close()
|
||||||
|
setImageUpload(undefined)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('sendPage.cancel')}
|
||||||
|
</Button>
|
||||||
|
</RBSheet>
|
||||||
{showNotification && (
|
{showNotification && (
|
||||||
<Snackbar
|
<Snackbar
|
||||||
style={styles.snackbar}
|
style={styles.snackbar}
|
||||||
@ -324,6 +383,14 @@ const styles = StyleSheet.create({
|
|||||||
paddingTop: 4,
|
paddingTop: 4,
|
||||||
paddingLeft: 5,
|
paddingLeft: 5,
|
||||||
},
|
},
|
||||||
|
imageUploadPreview: {
|
||||||
|
marginTop: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
buttonSpacer: {
|
||||||
|
marginTop: 16,
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default SendPage
|
export default SendPage
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
"react-native-screens": "^3.19.0",
|
"react-native-screens": "^3.19.0",
|
||||||
"react-native-securerandom": "^1.0.1",
|
"react-native-securerandom": "^1.0.1",
|
||||||
"react-native-sensitive-info": "^5.5.8",
|
"react-native-sensitive-info": "^5.5.8",
|
||||||
|
"react-native-share": "^8.1.0",
|
||||||
"react-native-svg": "^13.7.0",
|
"react-native-svg": "^13.7.0",
|
||||||
"react-native-tab-view": "^3.3.4",
|
"react-native-tab-view": "^3.3.4",
|
||||||
"react-native-vector-icons": "^9.2.0",
|
"react-native-vector-icons": "^9.2.0",
|
||||||
|
@ -7287,6 +7287,11 @@ react-native-sensitive-info@^5.5.8:
|
|||||||
resolved "https://registry.yarnpkg.com/react-native-sensitive-info/-/react-native-sensitive-info-5.5.8.tgz#6ebb67eed83d1c2867bd435630ef2c41eef204ed"
|
resolved "https://registry.yarnpkg.com/react-native-sensitive-info/-/react-native-sensitive-info-5.5.8.tgz#6ebb67eed83d1c2867bd435630ef2c41eef204ed"
|
||||||
integrity sha512-p99oaEW4QG1RdUNrkvd/c6Qdm856dQw/Rk81f9fA6Y3DlPs6ADNdU+jbPuTz3CcOUJwuKBDNenX6LR9KfmGFEg==
|
integrity sha512-p99oaEW4QG1RdUNrkvd/c6Qdm856dQw/Rk81f9fA6Y3DlPs6ADNdU+jbPuTz3CcOUJwuKBDNenX6LR9KfmGFEg==
|
||||||
|
|
||||||
|
react-native-share@^8.1.0:
|
||||||
|
version "8.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-native-share/-/react-native-share-8.1.0.tgz#34c9977e5aa49254b191f19d779bb4cff43fe2da"
|
||||||
|
integrity sha512-gME+6+FkQQ5/Ss4ulPjxwtgyZsF/YqBvG3qIVWN1urUhFFG2m2kycrNB0fPLLZy517/G6aDyUMioVZtPQArRHQ==
|
||||||
|
|
||||||
react-native-svg@^13.7.0:
|
react-native-svg@^13.7.0:
|
||||||
version "13.7.0"
|
version "13.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-13.7.0.tgz#be2ffb935e996762543dd7376bdc910722f7a43c"
|
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-13.7.0.tgz#be2ffb935e996762543dd7376bdc910722f7a43c"
|
||||||
|
Loading…
Reference in New Issue
Block a user