mirror of
https://github.com/KoalaSat/nostros.git
synced 2024-09-29 06:30:47 +00:00
Zaps (#350)
This commit is contained in:
commit
c9c3eae666
@ -247,6 +247,9 @@ android {
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
|
||||
// // https://stackoverflow.com/questions/56639529/duplicate-class-com-google-common-util-concurrent-listenablefuture-found-in-modu
|
||||
// implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
||||
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
|
||||
@ -258,6 +261,8 @@ dependencies {
|
||||
implementation 'com.facebook.fresco:webpsupport:2.6.0'
|
||||
|
||||
implementation 'com.facebook.fresco:animated-gif:2.6.0'
|
||||
|
||||
// implementation 'com.github.Samourai-Wallet:ExtLibJ:0.0.11'
|
||||
|
||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
||||
exclude group:'com.facebook.fbjni'
|
||||
|
@ -9,6 +9,8 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
//import com.samourai.wallet.segwit.bech32.Bech32;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@ -74,6 +76,8 @@ public class Event {
|
||||
muteUser(database);
|
||||
} else if (kind.equals("1002")) {
|
||||
saveRelays(database);
|
||||
} else if (kind.equals("9735")) {
|
||||
saveZap(database);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
@ -195,6 +199,45 @@ public class Event {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String getZapPubkey(String lnurl, String ln_address) {
|
||||
String pointer = ln_address;
|
||||
if (pointer.isEmpty() || pointer.equals("")) {
|
||||
pointer = lnurl;
|
||||
}
|
||||
|
||||
if (pointer.isEmpty() || pointer.equals("")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String[] parts = pointer.split("@");
|
||||
if (parts.length == 2) {
|
||||
String name = parts[0];
|
||||
String domain = parts[1];
|
||||
|
||||
if (!name.matches("^[a-z0-9-_]+$")) return "";
|
||||
|
||||
try {
|
||||
String url = "https://" + domain + "/.well-known/lnurlp/" + name;
|
||||
JSONObject response = getJSONObjectFromURL(url);
|
||||
Boolean allowsNostr = response.getBoolean("allowsNostr");
|
||||
if (allowsNostr) {
|
||||
String nostrPubkey = response.getString("nostrPubkey");
|
||||
return nostrPubkey;
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
// try {
|
||||
// Pair<String, byte[]> words = Bech32.bech32Decode(pointer);
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
protected void saveNote(SQLiteDatabase database, String userPubKey) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("id", id);
|
||||
@ -361,66 +404,127 @@ public class Event {
|
||||
}
|
||||
|
||||
protected void saveReaction(SQLiteDatabase database) throws JSONException {
|
||||
JSONArray pTags = filterTags("p");
|
||||
JSONArray eTags = filterTags("e");
|
||||
String query = "SELECT created_at FROM nostros_reactions WHERE id = ?";
|
||||
@SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {id});
|
||||
|
||||
String reacted_event_id = "";
|
||||
String reacted_user_id = "";
|
||||
if (eTags.length() > 0) {
|
||||
reacted_event_id = eTags.getJSONArray(eTags.length() - 1).getString(1);
|
||||
if (cursor.getCount() == 0) {
|
||||
JSONArray pTags = filterTags("p");
|
||||
JSONArray eTags = filterTags("e");
|
||||
|
||||
String reacted_event_id = "";
|
||||
String reacted_user_id = "";
|
||||
if (eTags.length() > 0) {
|
||||
reacted_event_id = eTags.getJSONArray(eTags.length() - 1).getString(1);
|
||||
}
|
||||
if (pTags.length() > 0) {
|
||||
reacted_user_id = pTags.getJSONArray(pTags.length() - 1).getString(1);
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("id", id);
|
||||
values.put("content", content);
|
||||
values.put("created_at", created_at);
|
||||
values.put("kind", kind);
|
||||
values.put("pubkey", pubkey);
|
||||
values.put("sig", sig);
|
||||
values.put("tags", tags.toString());
|
||||
values.put("positive", !content.equals("-"));
|
||||
values.put("reacted_event_id", reacted_event_id);
|
||||
values.put("reacted_user_id", reacted_user_id);
|
||||
|
||||
database.insert("nostros_reactions", null, values);
|
||||
}
|
||||
if (pTags.length() > 0) {
|
||||
reacted_user_id = pTags.getJSONArray(pTags.length() - 1).getString(1);
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("id", id);
|
||||
values.put("content", content);
|
||||
values.put("created_at", created_at);
|
||||
values.put("kind", kind);
|
||||
values.put("pubkey", pubkey);
|
||||
values.put("sig", sig);
|
||||
values.put("tags", tags.toString());
|
||||
values.put("positive", !content.equals("-"));
|
||||
values.put("reacted_event_id", reacted_event_id);
|
||||
values.put("reacted_user_id", reacted_user_id);
|
||||
|
||||
database.insert("nostros_reactions", null, values);
|
||||
}
|
||||
|
||||
protected void saveUserMeta(SQLiteDatabase database) throws JSONException {
|
||||
JSONObject userContent = new JSONObject(content);
|
||||
String query = "SELECT created_at, valid_nip05, nip05 FROM nostros_users WHERE id = ?";
|
||||
String query = "SELECT created_at, valid_nip05, nip05, zap_pubkey FROM nostros_users WHERE id = ?";
|
||||
@SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {pubkey});
|
||||
|
||||
String nip05 = userContent.optString("nip05");
|
||||
String lud = userContent.optString("lud06");
|
||||
if (lud.isEmpty()) {
|
||||
lud = userContent.optString("lud16");
|
||||
}
|
||||
String lnurl = userContent.optString("lud06");
|
||||
String ln_address = userContent.optString("lud16");
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("name", userContent.optString("name"));
|
||||
values.put("picture", userContent.optString("picture"));
|
||||
values.put("about", userContent.optString("about"));
|
||||
values.put("lnurl", lud);
|
||||
values.put("lnurl", lnurl);
|
||||
values.put("ln_address", ln_address);
|
||||
values.put("nip05", nip05);
|
||||
values.put("main_relay", userContent.optString("main_relay"));
|
||||
values.put("created_at", created_at);
|
||||
if (cursor.getCount() == 0) {
|
||||
values.put("valid_nip05", validateNip05(nip05) ? 1 : 0);
|
||||
values.put("zap_pubkey", getZapPubkey(lnurl, ln_address));
|
||||
values.put("id", pubkey);
|
||||
values.put("valid_nip05", validateNip05(nip05) ? 1 : 0);
|
||||
values.put("blocked", 0);
|
||||
database.insert("nostros_users", null, values);
|
||||
} else if (cursor.moveToFirst() && created_at > cursor.getInt(0)) {
|
||||
if (cursor.getInt(1) == 0 || !cursor.getString(2).equals(nip05)) {
|
||||
values.put("valid_nip05", validateNip05(nip05) ? 1 : 0);
|
||||
}
|
||||
} else if (cursor.moveToFirst()){
|
||||
String whereClause = "id = ?";
|
||||
String[] whereArgs = new String[] {
|
||||
String[] whereArgs = new String[]{
|
||||
this.pubkey
|
||||
};
|
||||
database.update("nostros_users", values, whereClause, whereArgs);
|
||||
if (created_at >= cursor.getInt(0) ||
|
||||
cursor.isNull(1) ||
|
||||
cursor.getInt(1) == 0) {
|
||||
values.put("valid_nip05", validateNip05(nip05) ? 1 : 0);
|
||||
database.update("nostros_users", values, whereClause, whereArgs);
|
||||
}
|
||||
if (created_at >= cursor.getInt(0) ||
|
||||
cursor.isNull(3) ||
|
||||
cursor.getString(3).equals("")) {
|
||||
values.put("zap_pubkey", getZapPubkey(lnurl, ln_address));
|
||||
database.update("nostros_users", values, whereClause, whereArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void saveZap(SQLiteDatabase database) throws JSONException {
|
||||
String query = "SELECT created_at FROM nostros_zaps WHERE id = ?";
|
||||
@SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {id});
|
||||
|
||||
if (cursor.getCount() == 0) {
|
||||
JSONArray pTags = filterTags("p");
|
||||
JSONArray eTags = filterTags("e");
|
||||
JSONArray bolt11Tags = filterTags("bolt11");
|
||||
JSONArray descriptionTags = filterTags("description");
|
||||
|
||||
String zapped_event_id = "";
|
||||
String zapped_user_id = "";
|
||||
String zapper_user_id = "";
|
||||
double amount = 0;
|
||||
if (descriptionTags.length() > 0) {
|
||||
JSONArray tag = descriptionTags.getJSONArray(0);
|
||||
JSONObject description = new JSONObject(tag.getString(1));
|
||||
zapper_user_id = description.getString("pubkey");
|
||||
}
|
||||
if (bolt11Tags.length() > 0) {
|
||||
String lnbc = bolt11Tags.getJSONArray(0).getString(1);
|
||||
amount = getLnAmount(lnbc);
|
||||
}
|
||||
if (eTags.length() > 0) {
|
||||
zapped_event_id = eTags.getJSONArray(eTags.length() - 1).getString(1);
|
||||
}
|
||||
if (pTags.length() > 0) {
|
||||
zapped_user_id = pTags.getJSONArray(pTags.length() - 1).getString(1);
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("id", id);
|
||||
values.put("content", content);
|
||||
values.put("created_at", created_at);
|
||||
values.put("kind", kind);
|
||||
values.put("pubkey", pubkey);
|
||||
values.put("sig", sig);
|
||||
values.put("tags", tags.toString());
|
||||
values.put("amount", amount);
|
||||
values.put("zapped_user_id", zapped_user_id);
|
||||
values.put("zapped_event_id", zapped_event_id);
|
||||
values.put("zapper_user_id", zapper_user_id);
|
||||
|
||||
database.insert("nostros_zaps", null, values);
|
||||
}
|
||||
}
|
||||
|
||||
@ -554,4 +658,24 @@ public class Event {
|
||||
|
||||
return new JSONObject(jsonString);
|
||||
}
|
||||
|
||||
protected static double getLnAmount(String lnbc) {
|
||||
double amount = 0.0;
|
||||
Matcher mili = Pattern.compile("^lnbc(\\d*)m\\S*$").matcher(lnbc);
|
||||
Matcher micro = Pattern.compile("^lnbc(\\d*)u\\S*$").matcher(lnbc);
|
||||
Matcher nano = Pattern.compile("^lnbc(\\d*)n\\S*$").matcher(lnbc);
|
||||
Matcher pico = Pattern.compile("^lnbc(\\d*)p\\S*$").matcher(lnbc);
|
||||
|
||||
if (mili.find()) {
|
||||
amount = 100000 * Integer.parseInt(mili.group(1));
|
||||
} else if (micro.find()) {
|
||||
amount = 100 * Integer.parseInt(micro.group(1));
|
||||
} else if (nano.find()) {
|
||||
amount = 0.1 * Integer.parseInt(nano.group(1));
|
||||
} else if (pico.find()) {
|
||||
amount = 0.0001 * Integer.parseInt(pico.group(1));
|
||||
}
|
||||
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
|
@ -174,9 +174,28 @@ public class DatabaseModule {
|
||||
database.execSQL("ALTER TABLE nostros_group_messages ADD COLUMN user_mentioned INT DEFAULT 0;");
|
||||
} catch (SQLException e) { }
|
||||
try {
|
||||
database.execSQL("ALTER TABLE updated_at ADD COLUMN mode INT DEFAULT 0;");
|
||||
database.execSQL("ALTER TABLE nostros_relays ADD COLUMN updated_at INT DEFAULT 0;");
|
||||
database.execSQL("ALTER TABLE nostros_relays ADD COLUMN mode TEXT;");
|
||||
} catch (SQLException e) { }
|
||||
try {
|
||||
database.execSQL("ALTER TABLE nostros_users ADD COLUMN ln_address TEXT;");
|
||||
database.execSQL("UPDATE nostros_users SET ln_address=lnurl;");
|
||||
database.execSQL("ALTER TABLE nostros_users ADD COLUMN zap_pubkey TEXT;");
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS nostros_zaps(\n" +
|
||||
" id TEXT PRIMARY KEY NOT NULL, \n" +
|
||||
" content TEXT NOT NULL,\n" +
|
||||
" created_at INT NOT NULL,\n" +
|
||||
" kind INT NOT NULL,\n" +
|
||||
" pubkey TEXT NOT NULL,\n" +
|
||||
" sig TEXT NOT NULL,\n" +
|
||||
" tags TEXT NOT NULL,\n" +
|
||||
" amount FLOAT NOT NULL,\n" +
|
||||
" zapped_user_id TEXT NOT NULL,\n" +
|
||||
" zapper_user_id TEXT NOT NULL,\n" +
|
||||
" zapped_event_id TEXT\n" +
|
||||
" );");
|
||||
database.execSQL("CREATE INDEX nostros_nostros_zaps_zapped_event_id_index ON nostros_zaps(zapped_event_id);");
|
||||
} catch (SQLException e) { }
|
||||
}
|
||||
|
||||
public void saveEvent(JSONObject data, String userPubKey, String relayUrl) throws JSONException {
|
||||
|
@ -164,7 +164,8 @@ export const GroupHeaderIcon: React.FC<GroupHeaderIconProps> = ({ groupId }) =>
|
||||
publicKey={user?.id}
|
||||
validNip05={user?.valid_nip05}
|
||||
nip05={user?.nip05}
|
||||
lud06={user?.lnurl}
|
||||
lnurl={user?.lnurl}
|
||||
lnAddress={user?.ln_address}
|
||||
picture={user?.picture}
|
||||
/>
|
||||
</View>
|
||||
|
@ -1,15 +1,26 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import { requestInvoice } from 'lnurl-pay'
|
||||
import { User } from '../../Functions/DatabaseFunctions/Users'
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import { ListRenderItem, StyleSheet, View } from 'react-native'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import RBSheet from 'react-native-raw-bottom-sheet'
|
||||
import { Button, Text, TextInput, useTheme } from 'react-native-paper'
|
||||
import { Button, Divider, Text, TextInput, TouchableRipple, useTheme } from 'react-native-paper'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import LnPreview from '../LnPreview'
|
||||
import { Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { getNpub } from '../../lib/nostr/Nip19'
|
||||
import { formatPubKey } from '../../Functions/RelayFunctions/Users'
|
||||
import { getZaps, Zap } from '../../Functions/DatabaseFunctions/Zaps'
|
||||
import { FlatList, ScrollView } from 'react-native-gesture-handler'
|
||||
import ProfileData from '../ProfileData'
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
import { Kind } from 'nostr-tools'
|
||||
import { getUnixTime } from 'date-fns'
|
||||
import { Event, signEvent } from '../../lib/nostr/Events'
|
||||
import { getRelays, Relay } from '../../Functions/DatabaseFunctions/Relays'
|
||||
import { UserContext } from '../../Contexts/UserContext'
|
||||
import { requestInvoiceWithServiceParams, requestPayServiceParams } from 'lnurl-pay'
|
||||
import axios from 'axios'
|
||||
|
||||
interface LnPaymentProps {
|
||||
open: boolean
|
||||
@ -21,18 +32,37 @@ interface LnPaymentProps {
|
||||
export const LnPayment: React.FC<LnPaymentProps> = ({ open, setOpen, note, user }) => {
|
||||
const theme = useTheme()
|
||||
const { t } = useTranslation('common')
|
||||
const { getSatoshiSymbol } = React.useContext(AppContext)
|
||||
const { getSatoshiSymbol, database, setDisplayUserDrawer } = React.useContext(AppContext)
|
||||
const { relayPool, lastEventId } = React.useContext(RelayPoolContext)
|
||||
const { publicKey, privateKey } = React.useContext(UserContext)
|
||||
const bottomSheetLnPaymentRef = React.useRef<RBSheet>(null)
|
||||
const [monto, setMonto] = useState<string>('')
|
||||
const defaultComment = note?.id ? `Nostr: ${formatPubKey(getNpub(note?.id))}` : ''
|
||||
const [comment, setComment] = useState<string>(defaultComment)
|
||||
const [invoice, setInvoice] = useState<string>()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [isZap, setIsZap] = useState<boolean>(false)
|
||||
const [zapsUpdated, setZapsUpdated] = useState<number>(0)
|
||||
const [zaps, setZaps] = useState<Zap[]>([])
|
||||
const lnurl = useMemo(() => user?.lnurl ?? note?.lnurl, [open])
|
||||
const lnAddress = useMemo(() => user?.ln_address ?? note?.ln_address, [open])
|
||||
const userId = user?.id ?? note?.pubkey
|
||||
const zapPubkey = user?.zap_pubkey ?? note?.zap_pubkey
|
||||
|
||||
useEffect(() => {
|
||||
setMonto('')
|
||||
if (open) {
|
||||
if (database && note?.id) {
|
||||
getZaps(database, note?.id).then((results) => {
|
||||
relayPool?.subscribe('zappers-meta', [
|
||||
{
|
||||
kinds: [Kind.Metadata],
|
||||
authors: results.filter((zap) => !zap.name).map((zap) => zap.zapper_user_id),
|
||||
},
|
||||
])
|
||||
setZaps(results)
|
||||
})
|
||||
}
|
||||
bottomSheetLnPaymentRef.current?.open()
|
||||
} else {
|
||||
bottomSheetLnPaymentRef.current?.close()
|
||||
@ -40,16 +70,65 @@ export const LnPayment: React.FC<LnPaymentProps> = ({ open, setOpen, note, user
|
||||
}, [open])
|
||||
|
||||
useEffect(() => {
|
||||
setComment(defaultComment)
|
||||
}, [note, open])
|
||||
if (database && note?.id) {
|
||||
getZaps(database, note?.id).then((results) => {
|
||||
setZaps(results)
|
||||
setZapsUpdated(getUnixTime(new Date()))
|
||||
})
|
||||
}
|
||||
}, [lastEventId])
|
||||
|
||||
const generateInvoice: () => void = async () => {
|
||||
if (lnurl && monto !== '') {
|
||||
useEffect(() => {
|
||||
bottomSheetLnPaymentRef.current?.forceUpdate()
|
||||
}, [zapsUpdated])
|
||||
|
||||
const generateInvoice: (zap: boolean) => void = async (zap) => {
|
||||
setIsZap(zap)
|
||||
const lud = lnAddress && lnAddress !== '' ? lnAddress : lnurl
|
||||
|
||||
if (lud && lud !== '' && monto !== '') {
|
||||
setLoading(true)
|
||||
requestInvoice({
|
||||
lnUrlOrAddress: lnurl,
|
||||
tokens: parseInt(monto, 10) ?? 0,
|
||||
|
||||
const tokens: number = parseInt(monto, 10) ?? 0
|
||||
let nostr: string
|
||||
|
||||
if (zap && database && privateKey && publicKey && zapPubkey && userId) {
|
||||
const relays: Relay[] = await getRelays(database)
|
||||
const tags = [
|
||||
['p', userId],
|
||||
['amount', (tokens * 1000).toString()],
|
||||
['relays', ...relays.map((relay) => relay.url)],
|
||||
]
|
||||
if (note?.id) tags.push(['e', note.id])
|
||||
|
||||
const event: Event = {
|
||||
content: comment,
|
||||
created_at: getUnixTime(new Date()),
|
||||
kind: 9734,
|
||||
pubkey: publicKey,
|
||||
tags,
|
||||
}
|
||||
const signedEvent = await signEvent(event, privateKey)
|
||||
nostr = JSON.stringify(signedEvent)
|
||||
}
|
||||
|
||||
const serviceParams = await requestPayServiceParams({ lnUrlOrAddress: lud })
|
||||
|
||||
requestInvoiceWithServiceParams({
|
||||
params: serviceParams,
|
||||
lnUrlOrAddress: lud,
|
||||
tokens,
|
||||
comment,
|
||||
fetchGet: async ({ url, params }) => {
|
||||
if (params && nostr && serviceParams.rawData.allowsNostr) {
|
||||
params.nostr = nostr
|
||||
}
|
||||
const response = await axios.get(url, {
|
||||
params,
|
||||
})
|
||||
console.log(response)
|
||||
return response.data
|
||||
},
|
||||
})
|
||||
.then((action) => {
|
||||
if (action.hasValidAmount && action.invoice) {
|
||||
@ -63,6 +142,37 @@ export const LnPayment: React.FC<LnPaymentProps> = ({ open, setOpen, note, user
|
||||
}
|
||||
}
|
||||
|
||||
const renderZapperItem: ListRenderItem<Zap> = ({ item }) => {
|
||||
return (
|
||||
<TouchableRipple onPress={() => setDisplayUserDrawer(item.user_id)}>
|
||||
<View key={item.id} style={styles.zapperRow}>
|
||||
<View style={styles.zapperData}>
|
||||
<ProfileData
|
||||
username={item?.name}
|
||||
publicKey={getNpub(item.id)}
|
||||
validNip05={item?.valid_nip05}
|
||||
nip05={item?.nip05}
|
||||
lnurl={item?.lnurl}
|
||||
lnAddress={item?.ln_address}
|
||||
picture={item?.picture}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.zapperAmount}>
|
||||
<MaterialCommunityIcons
|
||||
style={styles.zapperAmountIcon}
|
||||
name='lightning-bolt'
|
||||
size={15}
|
||||
color={'#F5D112'}
|
||||
/>
|
||||
<Text>
|
||||
{item.amount} {getSatoshiSymbol(15)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</TouchableRipple>
|
||||
)
|
||||
}
|
||||
|
||||
const rbSheetCustomStyles = React.useMemo(() => {
|
||||
return {
|
||||
container: {
|
||||
@ -78,15 +188,28 @@ export const LnPayment: React.FC<LnPaymentProps> = ({ open, setOpen, note, user
|
||||
}
|
||||
}, [])
|
||||
|
||||
return lnurl ? (
|
||||
return (
|
||||
<>
|
||||
<RBSheet
|
||||
ref={bottomSheetLnPaymentRef}
|
||||
closeOnDragDown={true}
|
||||
customStyles={rbSheetCustomStyles}
|
||||
onClose={() => setOpen(false)}
|
||||
onClose={() => {
|
||||
relayPool?.unsubscribe(['zappers-meta'])
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<View style={styles.drawerBottom}>
|
||||
{zaps.length > 0 && (
|
||||
<View style={styles.zappers}>
|
||||
<View style={styles.zappersList}>
|
||||
<ScrollView>
|
||||
<FlatList data={zaps} renderItem={renderZapperItem} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
<Divider />
|
||||
</View>
|
||||
)}
|
||||
<View style={[styles.montoSelection, styles.spacer]}>
|
||||
<Button style={styles.montoButton} mode='outlined' onPress={() => setMonto('1000')}>
|
||||
<Text>1k {getSatoshiSymbol(15)}</Text>
|
||||
@ -117,20 +240,27 @@ export const LnPayment: React.FC<LnPaymentProps> = ({ open, setOpen, note, user
|
||||
style={styles.spacer}
|
||||
mode='contained'
|
||||
disabled={loading || monto === ''}
|
||||
onPress={() => generateInvoice()}
|
||||
loading={loading}
|
||||
onPress={() => generateInvoice(false)}
|
||||
loading={loading && !isZap}
|
||||
>
|
||||
{t('lnPayment.generateInvoice')}
|
||||
{t('lnPayment.anonTip')}
|
||||
</Button>
|
||||
<Button
|
||||
style={styles.spacer}
|
||||
mode='contained'
|
||||
disabled={loading || monto === ''}
|
||||
onPress={() => generateInvoice(true)}
|
||||
loading={loading && isZap}
|
||||
>
|
||||
{t('lnPayment.zap')}
|
||||
</Button>
|
||||
<Button mode='outlined' onPress={() => setOpen(false)}>
|
||||
{t('lnPayment.cancel')}
|
||||
</Button>
|
||||
</View>
|
||||
</RBSheet>
|
||||
<LnPreview invoice={invoice} setInvoice={setInvoice} />
|
||||
<LnPreview invoice={invoice} setInvoice={setInvoice} setOpen={setOpen} />
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
|
||||
@ -154,6 +284,13 @@ const styles = StyleSheet.create({
|
||||
fontFamily: 'Satoshi-Symbol',
|
||||
fontSize: 20,
|
||||
},
|
||||
zappersList: {
|
||||
maxHeight: 200,
|
||||
marginBottom: 16,
|
||||
},
|
||||
zappers: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
montoSelection: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
@ -174,6 +311,20 @@ const styles = StyleSheet.create({
|
||||
alignItems: 'center',
|
||||
padding: 16,
|
||||
},
|
||||
zapperRow: {
|
||||
padding: 16,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
zapperData: {
|
||||
flex: 1,
|
||||
},
|
||||
zapperAmount: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
zapperAmountIcon: {
|
||||
paddingTop: 3,
|
||||
},
|
||||
})
|
||||
|
||||
export default LnPayment
|
||||
|
@ -9,11 +9,12 @@ import { AppContext } from '../../Contexts/AppContext'
|
||||
import { decode, PaymentRequestObject, TagsObject } from 'bolt11'
|
||||
|
||||
interface LnPreviewProps {
|
||||
setOpen: (open: boolean) => void
|
||||
invoice?: string
|
||||
setInvoice: (invoice: string | undefined) => void
|
||||
}
|
||||
|
||||
export const LnPreview: React.FC<LnPreviewProps> = ({ invoice, setInvoice }) => {
|
||||
export const LnPreview: React.FC<LnPreviewProps> = ({ invoice, setInvoice, setOpen }) => {
|
||||
const theme = useTheme()
|
||||
const { t } = useTranslation('common')
|
||||
const { getSatoshiSymbol } = React.useContext(AppContext)
|
||||
@ -63,7 +64,10 @@ export const LnPreview: React.FC<LnPreviewProps> = ({ invoice, setInvoice }) =>
|
||||
closeOnDragDown={true}
|
||||
// height={630}
|
||||
customStyles={rbSheetQrCustomStyles}
|
||||
onClose={() => setInvoice(undefined)}
|
||||
onClose={() => {
|
||||
setInvoice(undefined)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<Card style={styles.qrContainer}>
|
||||
<Card.Content>
|
||||
|
@ -22,8 +22,18 @@ import ProfileData from '../ProfileData'
|
||||
export const MenuItems: React.FC = () => {
|
||||
const [drawerItemIndex, setDrawerItemIndex] = React.useState<number>(-1)
|
||||
const { relays } = React.useContext(RelayPoolContext)
|
||||
const { nPub, publicKey, privateKey, logout, name, picture, validNip05, lnurl, nip05 } =
|
||||
React.useContext(UserContext)
|
||||
const {
|
||||
nPub,
|
||||
publicKey,
|
||||
privateKey,
|
||||
logout,
|
||||
name,
|
||||
picture,
|
||||
validNip05,
|
||||
lnurl,
|
||||
lnAddress,
|
||||
nip05,
|
||||
} = React.useContext(UserContext)
|
||||
const { t } = useTranslation('common')
|
||||
const theme = useTheme()
|
||||
|
||||
@ -83,7 +93,8 @@ export const MenuItems: React.FC = () => {
|
||||
publicKey={publicKey}
|
||||
validNip05={validNip05}
|
||||
nip05={nip05}
|
||||
lud06={lnurl}
|
||||
lnurl={lnurl}
|
||||
lnAddress={lnAddress}
|
||||
picture={picture}
|
||||
/>
|
||||
</TouchableRipple>
|
||||
|
@ -10,7 +10,8 @@ interface NostrosAvatarProps {
|
||||
src?: string
|
||||
name?: string
|
||||
size?: number
|
||||
lud06?: string
|
||||
lnurl?: string
|
||||
lnAddress?: string
|
||||
}
|
||||
|
||||
export const NostrosAvatar: React.FC<NostrosAvatarProps> = ({
|
||||
@ -18,11 +19,13 @@ export const NostrosAvatar: React.FC<NostrosAvatarProps> = ({
|
||||
name,
|
||||
pubKey,
|
||||
size = 40,
|
||||
lud06,
|
||||
lnurl,
|
||||
lnAddress,
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const displayName = name && name !== '' ? name : formatPubKey(pubKey) ?? ''
|
||||
const hasLud06 = lud06 && lud06 !== ''
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
const hasLud06 = (lnurl && lnurl !== '') || (lnAddress && lnAddress !== '')
|
||||
const lud06IconSize = size / 2.85
|
||||
|
||||
return (
|
||||
|
@ -36,10 +36,11 @@ import { REGEX_SOCKET_LINK } from '../../Constants/Relay'
|
||||
import { navigate, push } from '../../lib/Navigation'
|
||||
import { Kind } from 'nostr-tools'
|
||||
import ProfileData from '../ProfileData'
|
||||
import { relayToColor } from '../../Functions/NativeFunctions'
|
||||
import { formatBigNumber, relayToColor } from '../../Functions/NativeFunctions'
|
||||
import { SvgXml } from 'react-native-svg'
|
||||
import { reactionIcon } from '../../Constants/Theme'
|
||||
import LnPayment from '../LnPayment'
|
||||
import { getZapsAmount } from '../../Functions/DatabaseFunctions/Zaps'
|
||||
|
||||
interface NoteCardProps {
|
||||
note?: Note
|
||||
@ -75,6 +76,7 @@ export const NoteCard: React.FC<NoteCardProps> = ({
|
||||
const [userDownvoted, setUserDownvoted] = useState<boolean>(false)
|
||||
const [repliesCount, setRepliesCount] = React.useState<number>(0)
|
||||
const [repostCount, serRepostCount] = React.useState<number>(0)
|
||||
const [zapsAmount, setZapsAmount] = React.useState<number>()
|
||||
const [relays, setRelays] = React.useState<NoteRelay[]>([])
|
||||
const [hide, setHide] = useState<boolean>(isContentWarning(note))
|
||||
const [userReposted, setUserReposted] = useState<boolean>()
|
||||
@ -102,6 +104,9 @@ export const NoteCard: React.FC<NoteCardProps> = ({
|
||||
getRepliesCount(database, note.id).then(setRepliesCount)
|
||||
getRepostCount(database, note.id).then(serRepostCount)
|
||||
isUserReposted(database, note.id, publicKey).then(setUserReposted)
|
||||
if (note.zap_pubkey?.length > 0) {
|
||||
getZapsAmount(database, note.id).then(setZapsAmount)
|
||||
}
|
||||
}
|
||||
getNoteRelays(database, note.id).then(setRelays)
|
||||
}
|
||||
@ -372,7 +377,8 @@ export const NoteCard: React.FC<NoteCardProps> = ({
|
||||
publicKey={note.pubkey}
|
||||
validNip05={note?.valid_nip05}
|
||||
nip05={note?.nip05}
|
||||
lud06={note?.lnurl}
|
||||
lnurl={note?.lnurl}
|
||||
lnAddress={note?.ln_address}
|
||||
picture={showAvatarImage ? note?.picture : undefined}
|
||||
timestamp={note?.created_at}
|
||||
/>
|
||||
@ -415,20 +421,18 @@ export const NoteCard: React.FC<NoteCardProps> = ({
|
||||
{showActionCount && reactionsCount()}
|
||||
</Button>
|
||||
</Surface>
|
||||
{note.lnurl && (
|
||||
<>
|
||||
<Button
|
||||
style={styles.action}
|
||||
icon={() => (
|
||||
<MaterialCommunityIcons name='lightning-bolt' size={24} color={'#F5D112'} />
|
||||
)}
|
||||
onPress={() => setOpenLn(true)}
|
||||
>
|
||||
{''}
|
||||
</Button>
|
||||
{openLn && <LnPayment open={openLn} setOpen={setOpenLn} note={note} />}
|
||||
</>
|
||||
{(note?.lnurl || note?.ln_address) && (
|
||||
<Button
|
||||
style={styles.action}
|
||||
icon={() => (
|
||||
<MaterialCommunityIcons name='lightning-bolt' size={24} color={'#F5D112'} />
|
||||
)}
|
||||
onPress={() => setOpenLn(true)}
|
||||
>
|
||||
{note.zap_pubkey?.length > 0 ? formatBigNumber(zapsAmount) : ''}
|
||||
</Button>
|
||||
)}
|
||||
{openLn && <LnPayment open={openLn} setOpen={setOpenLn} note={note} />}
|
||||
</Card.Content>
|
||||
)}
|
||||
<Card.Content style={styles.relayList}>
|
||||
|
@ -221,7 +221,9 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
|
||||
icon='lightning-bolt'
|
||||
size={28}
|
||||
onPress={() => setOpenLn(true)}
|
||||
disabled={!user?.lnurl}
|
||||
disabled={
|
||||
!user.lnurl && user.lnurl !== '' && !user?.ln_address && user.ln_address !== ''
|
||||
}
|
||||
iconColor='#F5D112'
|
||||
/>
|
||||
<Text>{t('profileCard.invoice')}</Text>
|
||||
|
@ -61,7 +61,8 @@ export const ProfileCard: React.FC<ProfileCardProps> = ({ bottomSheetRef, showIm
|
||||
publicKey={user?.id ?? displayUserDrawer}
|
||||
validNip05={user?.valid_nip05}
|
||||
nip05={user?.nip05}
|
||||
lud06={user?.lnurl}
|
||||
lnurl={user?.lnurl}
|
||||
lnAddress={user?.ln_address}
|
||||
picture={showImages ? user?.picture : undefined}
|
||||
/>
|
||||
</View>
|
||||
|
@ -10,8 +10,9 @@ import { getNpub } from '../../lib/nostr/Nip19'
|
||||
interface ProfileCardProps {
|
||||
username?: string
|
||||
publicKey?: string
|
||||
lud06?: string
|
||||
validNip05?: boolean
|
||||
lnurl?: string
|
||||
lnAddress?: string
|
||||
validNip05?: number
|
||||
nip05?: string
|
||||
picture?: string
|
||||
avatarSize?: number
|
||||
@ -21,7 +22,8 @@ interface ProfileCardProps {
|
||||
export const ProfileData: React.FC<ProfileCardProps> = ({
|
||||
username,
|
||||
publicKey,
|
||||
lud06,
|
||||
lnurl,
|
||||
lnAddress,
|
||||
validNip05,
|
||||
nip05,
|
||||
picture,
|
||||
@ -35,6 +37,7 @@ export const ProfileData: React.FC<ProfileCardProps> = ({
|
||||
timestamp ? formatDistance(fromUnixTime(timestamp), new Date(), { addSuffix: true }) : null,
|
||||
[timestamp],
|
||||
)
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.left}>
|
||||
@ -42,7 +45,8 @@ export const ProfileData: React.FC<ProfileCardProps> = ({
|
||||
name={username}
|
||||
pubKey={nPub}
|
||||
src={picture}
|
||||
lud06={lud06}
|
||||
lnurl={lnurl}
|
||||
lnAddress={lnAddress}
|
||||
size={avatarSize}
|
||||
/>
|
||||
<View style={[styles.contactData, { height: avatarSize }]}>
|
||||
|
@ -113,6 +113,7 @@ export const UploadImage: React.FC<UploadImageProps> = ({
|
||||
mode='contained'
|
||||
onPress={uploadImage}
|
||||
loading={uploadingFile}
|
||||
disabled={uploadingFile}
|
||||
>
|
||||
{t('uploadImage.uploadImage')}
|
||||
</Button>
|
||||
|
@ -32,6 +32,8 @@ export interface UserContextProps {
|
||||
nip05?: string
|
||||
setNip05: (value: string) => void
|
||||
validNip05?: boolean
|
||||
setLnAddress: (value: string) => void
|
||||
lnAddress?: string
|
||||
}
|
||||
|
||||
export interface UserContextProviderProps {
|
||||
@ -49,6 +51,7 @@ export const initialUserContext: UserContextProps = {
|
||||
setPicture: () => {},
|
||||
setAbout: () => {},
|
||||
setLnurl: () => {},
|
||||
setLnAddress: () => {},
|
||||
setNip05: () => {},
|
||||
}
|
||||
|
||||
@ -64,6 +67,7 @@ export const UserContextProvider = ({ children }: UserContextProviderProps): JSX
|
||||
const [picture, setPicture] = useState<string>()
|
||||
const [about, setAbout] = useState<string>()
|
||||
const [lnurl, setLnurl] = useState<string>()
|
||||
const [lnAddress, setLnAddress] = useState<string>()
|
||||
const [nip05, setNip05] = useState<string>()
|
||||
const [validNip05, setValidNip05] = useState<boolean>()
|
||||
|
||||
@ -75,6 +79,7 @@ export const UserContextProvider = ({ children }: UserContextProviderProps): JSX
|
||||
setPicture(result.picture)
|
||||
setAbout(result.about)
|
||||
setLnurl(result.lnurl)
|
||||
setLnAddress(result.ln_address)
|
||||
setNip05(result.nip05)
|
||||
setValidNip05(result.valid_nip05)
|
||||
}
|
||||
@ -93,6 +98,7 @@ export const UserContextProvider = ({ children }: UserContextProviderProps): JSX
|
||||
setPicture(undefined)
|
||||
setAbout(undefined)
|
||||
setLnurl(undefined)
|
||||
setLnAddress(undefined)
|
||||
setNip05(undefined)
|
||||
setValidNip05(undefined)
|
||||
dropTables(database).then(() => {
|
||||
@ -181,6 +187,8 @@ export const UserContextProvider = ({ children }: UserContextProviderProps): JSX
|
||||
nip05,
|
||||
setNip05,
|
||||
validNip05,
|
||||
lnAddress,
|
||||
setLnAddress,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -6,10 +6,12 @@ export interface Note extends Event {
|
||||
name: string
|
||||
picture: string
|
||||
lnurl: string
|
||||
ln_address: string
|
||||
reply_event_id: string
|
||||
user_created_at: number
|
||||
nip05: string
|
||||
valid_nip05: boolean
|
||||
valid_nip05: number
|
||||
zap_pubkey: string
|
||||
repost_id: string
|
||||
blocked: number
|
||||
}
|
||||
@ -41,7 +43,9 @@ export const getMainNotes: (
|
||||
) => Promise<Note[]> = async (db, pubKey, limit, contants, filters) => {
|
||||
let notesQuery = `
|
||||
SELECT
|
||||
nostros_notes.*, nostros_users.nip05, nostros_users.blocked, nostros_users.valid_nip05, nostros_users.lnurl, nostros_users.name, nostros_users.picture, nostros_users.contact, nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
nostros_notes.*, nostros_users.zap_pubkey, nostros_users.nip05, nostros_users.blocked, nostros_users.valid_nip05,
|
||||
nostros_users.ln_address, nostros_users.lnurl, nostros_users.name, nostros_users.picture, nostros_users.contact,
|
||||
nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
LEFT JOIN
|
||||
nostros_users ON nostros_users.id = nostros_notes.pubkey
|
||||
WHERE
|
||||
@ -111,7 +115,9 @@ export const getMentionNotes: (
|
||||
) => Promise<Note[]> = async (db, pubKey, limit) => {
|
||||
const notesQuery = `
|
||||
SELECT
|
||||
nostros_notes.*, nostros_users.nip05, nostros_users.valid_nip05, nostros_users.lnurl, nostros_users.name, nostros_users.picture, nostros_users.contact, nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
nostros_notes.*, nostros_users.zap_pubkey, nostros_users.nip05, nostros_users.valid_nip05, nostros_users.lnurl,
|
||||
nostros_users.ln_address, nostros_users.name, nostros_users.picture, nostros_users.contact,
|
||||
nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
LEFT JOIN
|
||||
nostros_users ON nostros_users.id = nostros_notes.pubkey
|
||||
WHERE (nostros_notes.reply_event_id IN (
|
||||
@ -136,7 +142,9 @@ export const getReactedNotes: (
|
||||
) => Promise<Note[]> = async (db, pubKey, limit) => {
|
||||
const notesQuery = `
|
||||
SELECT
|
||||
nostros_notes.*, nostros_users.nip05, nostros_users.valid_nip05, nostros_users.lnurl, nostros_users.name, nostros_users.picture, nostros_users.contact, nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
nostros_notes.*, nostros_users.zap_pubkey, nostros_users.nip05, nostros_users.valid_nip05, nostros_users.lnurl,
|
||||
nostros_users.ln_address, nostros_users.name, nostros_users.picture, nostros_users.contact,
|
||||
nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
LEFT JOIN
|
||||
nostros_users ON nostros_users.id = nostros_notes.pubkey
|
||||
WHERE nostros_notes.id IN (
|
||||
@ -160,7 +168,9 @@ export const getRepostedNotes: (
|
||||
) => Promise<Note[]> = async (db, pubKey, limit) => {
|
||||
const notesQuery = `
|
||||
SELECT
|
||||
nostros_notes.*, nostros_users.nip05, nostros_users.valid_nip05, nostros_users.lnurl, nostros_users.name, nostros_users.picture, nostros_users.contact, nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
nostros_notes.*, nostros_users.zap_pubkey, nostros_users.nip05, nostros_users.valid_nip05, nostros_users.lnurl,
|
||||
nostros_users.ln_address, nostros_users.name, nostros_users.picture, nostros_users.contact,
|
||||
nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
LEFT JOIN
|
||||
nostros_users ON nostros_users.id = nostros_notes.pubkey
|
||||
WHERE nostros_notes.repost_id IS NOT NULL AND
|
||||
@ -297,7 +307,9 @@ export const getNotes: (
|
||||
) => Promise<Note[]> = async (db, { filters = {}, limit, contacts, includeIds }) => {
|
||||
let notesQuery = `
|
||||
SELECT
|
||||
nostros_notes.*, nostros_users.nip05, nostros_users.valid_nip05, nostros_users.lnurl, nostros_users.name, nostros_users.picture, nostros_users.contact, nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
nostros_notes.*, nostros_users.zap_pubkey, nostros_users.nip05, nostros_users.valid_nip05,
|
||||
nostros_users.ln_address, nostros_users.lnurl, nostros_users.name, nostros_users.picture,
|
||||
nostros_users.contact, nostros_users.created_at as user_created_at FROM nostros_notes
|
||||
LEFT JOIN
|
||||
nostros_users ON nostros_users.id = nostros_notes.pubkey
|
||||
`
|
||||
|
@ -10,11 +10,13 @@ export interface User {
|
||||
contact?: boolean
|
||||
follower?: number
|
||||
lnurl?: string
|
||||
ln_address?: string
|
||||
nip05?: string
|
||||
created_at?: number
|
||||
valid_nip05?: boolean
|
||||
blocked?: number
|
||||
muted_groups?: number
|
||||
zap_pubkey?: string
|
||||
}
|
||||
|
||||
const databaseToEntity: (object: object) => User = (object) => {
|
||||
|
60
frontend/Functions/DatabaseFunctions/Zaps/index.ts
Normal file
60
frontend/Functions/DatabaseFunctions/Zaps/index.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { QuickSQLiteConnection } from 'react-native-quick-sqlite'
|
||||
import { getItems } from '..'
|
||||
import { Event } from '../../../lib/nostr/Events'
|
||||
|
||||
export interface Zap extends Event {
|
||||
amount: boolean
|
||||
zapped_event_id: string
|
||||
zapped_user_id: string
|
||||
zapper_user_id: string
|
||||
name: string
|
||||
picture: string
|
||||
user_id: string
|
||||
valid_nip05: number
|
||||
nip05: string
|
||||
lnurl: string
|
||||
ln_address: string
|
||||
}
|
||||
|
||||
const databaseToEntity: (object: object) => Zap = (object) => {
|
||||
return object as Zap
|
||||
}
|
||||
|
||||
export const getZapsAmount: (
|
||||
db: QuickSQLiteConnection,
|
||||
eventId: string,
|
||||
) => Promise<number> = async (db, eventId) => {
|
||||
const zapsQuery = `
|
||||
SELECT
|
||||
SUM(amount)
|
||||
FROM
|
||||
nostros_zaps
|
||||
WHERE zapped_event_id = "${eventId}"
|
||||
`
|
||||
|
||||
const resultSet = db.execute(zapsQuery)
|
||||
const item: { 'SUM(amount)': number } = resultSet?.rows?.item(0)
|
||||
|
||||
return item['SUM(amount)'] ?? 0
|
||||
}
|
||||
|
||||
export const getZaps: (db: QuickSQLiteConnection, eventId: string) => Promise<Zap[]> = async (
|
||||
db,
|
||||
eventId,
|
||||
) => {
|
||||
const groupsQuery = `
|
||||
SELECT
|
||||
nostros_zaps.*, nostros_users.name, nostros_users.id as user_id, nostros_users.picture, nostros_users.valid_nip05,
|
||||
nostros_users.nip05, nostros_users.lnurl, nostros_users.ln_address
|
||||
FROM
|
||||
nostros_zaps
|
||||
LEFT JOIN
|
||||
nostros_users ON nostros_users.id = nostros_zaps.zapper_user_id
|
||||
WHERE zapped_event_id = "${eventId}"
|
||||
`
|
||||
const resultSet = await db.execute(groupsQuery)
|
||||
const items: object[] = getItems(resultSet)
|
||||
const notes: Zap[] = items.map((object) => databaseToEntity(object))
|
||||
|
||||
return notes
|
||||
}
|
@ -81,6 +81,18 @@ export const validNip21: (string: string | undefined) => boolean = (string) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const formatBigNumber: (num: number | undefined) => string = (num) => {
|
||||
if (num === undefined) return ''
|
||||
|
||||
if (num >= 1_000_000) {
|
||||
return `${(num / 1_000_000).toFixed(1)}M`
|
||||
} else if (num >= 1_000) {
|
||||
return `${(num / 1_000).toFixed(1)}K`
|
||||
} else {
|
||||
return num.toString()
|
||||
}
|
||||
}
|
||||
|
||||
export const randomInt: (min: number, max: number) => number = (min, max) =>
|
||||
Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
|
@ -100,7 +100,9 @@
|
||||
"generateInvoice": "Invoice generieren",
|
||||
"cancel": "Abbrechen",
|
||||
"copy": "Kopieren",
|
||||
"open": "Öffne Wallet"
|
||||
"open": "Öffne Wallet",
|
||||
"anonTip": "Anonymous tip",
|
||||
"zap": "Zap"
|
||||
},
|
||||
"notificationsFeed": {
|
||||
"emptyTitle": "Keine Benachrichtigung.",
|
||||
@ -263,15 +265,16 @@
|
||||
"nsecCopied": "Privater Schlüssel kopiert.",
|
||||
"npubCopied": "Öffentlicher Schlüssel kopiert.",
|
||||
"profilePublished": "Profil veröffentlicht.",
|
||||
"lud06Published": "LUD-06 veröffentlicht.\n\n{{lud06}}",
|
||||
"lud06Published": "LUDs veröffentlicht.",
|
||||
"nip05Published": "NIP-05 veröffentlicht.\n\n{{nip05}}",
|
||||
"picturePublished": "Bild veröffentlicht.",
|
||||
"connectionError": "Verbindungsfehler"
|
||||
},
|
||||
"publishLud06": "Veröffentlicht",
|
||||
"lud06Label": "LNURL / Lightning Addresse",
|
||||
"lud06Label": "LNURL",
|
||||
"lud16Label": "Lightning Addresse",
|
||||
"lud06Description": "Profil verknüpfen mit LNURL oder Lightning Addresse.",
|
||||
"lud06Title": "LUD-06",
|
||||
"lud06Title": "Zaps",
|
||||
"publishNip05": "Veröffentlichen",
|
||||
"nip05Link": "Mehr erfahren",
|
||||
"nip05Description": "Profil mit Domain verbinden.",
|
||||
@ -289,7 +292,7 @@
|
||||
"npub": "Öffentlicher Schlüssel",
|
||||
"copyNPub": "Kopieren",
|
||||
"directory": "Verzeichnis",
|
||||
"lud06": "LUD-06",
|
||||
"lud06": "Zaps",
|
||||
"nip05": "NIP-05",
|
||||
"name": "Name",
|
||||
"about": "Über mich"
|
||||
|
@ -101,7 +101,9 @@
|
||||
"cancel": "Cancel",
|
||||
"npub": "Public key",
|
||||
"copy": "Copy",
|
||||
"open": "Open wallet"
|
||||
"open": "Open wallet",
|
||||
"anonTip": "Anonymous tip",
|
||||
"zap": "Zap"
|
||||
},
|
||||
"notificationsFeed": {
|
||||
"emptyTitle": "You don't have any notifications.",
|
||||
@ -261,15 +263,16 @@
|
||||
"nsecCopied": "Private key copied.",
|
||||
"npubCopied": "Public key copied.",
|
||||
"profilePublished": "Profile published.",
|
||||
"lud06Published": "LUD-06 published.\n\n{{lud06}}",
|
||||
"lud06Published": "LUDs published.",
|
||||
"nip05Published": "NIP-05 published.\n\n{{nip05}}",
|
||||
"picturePublished": "Picture published.",
|
||||
"connectionError": "Connection error."
|
||||
},
|
||||
"publishLud06": "Publish",
|
||||
"lud06Label": "LNURL / Lightning Address",
|
||||
"lud06Label": "LNURL",
|
||||
"lud16Label": "Lightning Address",
|
||||
"lud06Description": "Link your identity with an LNURL or Lightning Address.",
|
||||
"lud06Title": "LUD-06",
|
||||
"lud06Title": "Zaps",
|
||||
"publishNip05": "Publish",
|
||||
"nip05Link": "Learn more.",
|
||||
"nip05Description": "Link your identity with a domain.",
|
||||
@ -287,7 +290,7 @@
|
||||
"npub": "Public key",
|
||||
"copyNPub": "Copy",
|
||||
"directory": "Directory",
|
||||
"lud06": "LUD-06",
|
||||
"lud06": "Zaps",
|
||||
"nip05": "NIP-05",
|
||||
"name": "Name",
|
||||
"about": "Description"
|
||||
|
@ -121,7 +121,9 @@
|
||||
"generateInvoice": "Generar factura",
|
||||
"cancel": "Cancelar",
|
||||
"copy": "Copiar",
|
||||
"open": "Abrir wallet"
|
||||
"open": "Abrir wallet",
|
||||
"anonTip": "Propina anónima",
|
||||
"zap": "Zap"
|
||||
},
|
||||
"notificationsFeed": {
|
||||
"emptyTitle": "No tienes notificationes.",
|
||||
@ -267,15 +269,16 @@
|
||||
"nsecCopied": "Clave secreta copiada.",
|
||||
"npubCopied": "Clave pública copiada.",
|
||||
"profilePublished": "Perfil publicado.",
|
||||
"lud06Published": "LUD-06 publicado.\n\n{{lud06}}",
|
||||
"lud06Published": "LUDs publicados.",
|
||||
"nip05Published": "NIP-05 publicado.\n\n{{nip05}}",
|
||||
"picturePublished": "Imagen publicada.",
|
||||
"connectionError": "Error de conexión"
|
||||
},
|
||||
"publishLud06": "Publicar",
|
||||
"lud06Label": "LNURL / Lightning Address",
|
||||
"lud06Label": "LNURL",
|
||||
"lud16Label": "Lightning Address",
|
||||
"lud06Description": "Vincula tu perfil a LNURL or Lightning Address.",
|
||||
"lud06Title": "LUD-06",
|
||||
"lud06Title": "Zaps",
|
||||
"publishNip05": "Publicar",
|
||||
"nip05Link": "Saber más.",
|
||||
"nip05Description": "Vincula tu perfil con un dominio.",
|
||||
@ -293,7 +296,7 @@
|
||||
"npub": "Clave pública",
|
||||
"copyNPub": "Copiar",
|
||||
"directory": "Directory",
|
||||
"lud06": "LUD-06",
|
||||
"lud06": "Zaps",
|
||||
"nip05": "NIP-05",
|
||||
"name": "Nombre",
|
||||
"about": "Descripción"
|
||||
|
@ -126,7 +126,9 @@
|
||||
"generateInvoice": "Générer une facture",
|
||||
"cancel": "Annuler",
|
||||
"copy": "Copier",
|
||||
"open": "Ouvrir le wallet"
|
||||
"open": "Ouvrir le wallet",
|
||||
"anonTip": "Anonymous tip",
|
||||
"zap": "Zap"
|
||||
},
|
||||
"notificationsFeed": {
|
||||
"emptyTitle": "Vous n'avez pas de notification.",
|
||||
@ -265,15 +267,16 @@
|
||||
"nsecCopied": "Clé privée copiée",
|
||||
"npubCopied": "Clé publique copiée",
|
||||
"profilePublished": "Votre profil a été publié",
|
||||
"lud06Published": "LUD-06 publié.\n\n{{lud06}}",
|
||||
"lud06Published": "LUDs publié.",
|
||||
"nip05Published": "NIP-05 publié.\n\n{{nip05}}",
|
||||
"picturePublished": "Image publiée.",
|
||||
"connectionError": "Erreur de connexion"
|
||||
},
|
||||
"publishLud06": "Publier",
|
||||
"lud06Label": "LNURL / Adresse Lightning",
|
||||
"lud06Label": "LNURL",
|
||||
"lud16Label": "Adresse Lightning",
|
||||
"lud06Description": "Associez votre profil à LNURL ou à l'adresse Lightning.",
|
||||
"lud06Title": "LUD-06",
|
||||
"lud06Title": "Zaps",
|
||||
"publishNip05": "Publier",
|
||||
"nip05Link": "En savoir plus.",
|
||||
"nip05Description": "Associez votre profil à un domaine.",
|
||||
@ -291,7 +294,7 @@
|
||||
"npub": "Clé publique",
|
||||
"copyNPub": "Copier",
|
||||
"directory": "Directory",
|
||||
"lud06": "LUD-06",
|
||||
"lud06": "Zaps",
|
||||
"nip05": "NIP-05",
|
||||
"name": "Nom",
|
||||
"about": "Description"
|
||||
|
@ -122,7 +122,9 @@
|
||||
"cancel": "Отменить",
|
||||
"npub": "Публичный ключ",
|
||||
"copy": "Скопировать",
|
||||
"open": "Открыть кошелек"
|
||||
"open": "Открыть кошелек",
|
||||
"anonTip": "Anonymous tip",
|
||||
"zap": "Zap"
|
||||
},
|
||||
"notificationsFeed": {
|
||||
"emptyTitle": "У Вас нет новых уведомлений",
|
||||
@ -259,7 +261,7 @@
|
||||
"nsecCopied": "Приватный ключ скопирован",
|
||||
"npubCopied": "Публичный ключ скопирован",
|
||||
"profilePublished": "Profile published.",
|
||||
"lud06Published": "LUD-06 published.\n\n{{lud06}}",
|
||||
"lud06Published": "LUDs published.",
|
||||
"nip05Published": "NIP-05 published.\n\n{{nip05}}",
|
||||
"picturePublished": "Picture published.",
|
||||
"connectionError": "Ошибка с подключением",
|
||||
@ -267,9 +269,10 @@
|
||||
"desactive": "Relay desactivated."
|
||||
},
|
||||
"publishLud06": "Опубликовть",
|
||||
"lud06Label": "LNURL / Lightning Address",
|
||||
"lud06Label": "LNURL",
|
||||
"lud16Label": "Lightning Address",
|
||||
"lud06Description": "Link your identity with a LNURL or Lightning Address.",
|
||||
"lud06Title": "LUD-06",
|
||||
"lud06Title": "Zaps",
|
||||
"publishNip05": "Publish",
|
||||
"nip05Link": "Подробнее.",
|
||||
"nip05Description": "Link your identity with a domain.",
|
||||
@ -287,7 +290,7 @@
|
||||
"npub": "Публичный ключ",
|
||||
"copyNPub": "Скопировать",
|
||||
"directory": "Directory",
|
||||
"lud06": "LUD-06",
|
||||
"lud06": "Zaps",
|
||||
"nip05": "NIP-05",
|
||||
"name": "Name",
|
||||
"about": "Описание"
|
||||
|
@ -120,7 +120,9 @@
|
||||
"cancel": "取消",
|
||||
"npub": "公钥",
|
||||
"copy": "复制",
|
||||
"open": "打开钱包"
|
||||
"open": "打开钱包",
|
||||
"anonTip": "Anonymous tip",
|
||||
"zap": "Zap"
|
||||
},
|
||||
"notificationsFeed": {
|
||||
"emptyTitle": "还没有新通知",
|
||||
@ -272,7 +274,7 @@
|
||||
"nsecCopied": "已复制私钥",
|
||||
"npubCopied": "已复制公钥",
|
||||
"profilePublished": "简介已发布",
|
||||
"lud06Published": "LUD-06 已发布 \n\n{{lud06}}",
|
||||
"lud06Published": "LUDs 已发布",
|
||||
"nip05Published": "NIP-05 已发布 \n\n{{nip05}}",
|
||||
"picturePublished": "头像已发布",
|
||||
"connectionError": "连接错误",
|
||||
@ -280,9 +282,10 @@
|
||||
"desactive": "中继已停用"
|
||||
},
|
||||
"publishLud06": "发布",
|
||||
"lud06Label": "LNURL / Lightning 地址",
|
||||
"lud06Label": "LNURL",
|
||||
"lud16Label": "Lightning 地址",
|
||||
"lud06Description": "链接您的身份到 LNURL 或 Lightning 地址",
|
||||
"lud06Title": "LUD-06",
|
||||
"lud06Title": "Zaps",
|
||||
"publishNip05": "发布",
|
||||
"nip05Link": "了解更多",
|
||||
"nip05Description": "链接接到域名",
|
||||
@ -300,7 +303,7 @@
|
||||
"npub": "公钥",
|
||||
"copyNPub": "复制",
|
||||
"directory": "目录",
|
||||
"lud06": "LUD-06",
|
||||
"lud06": "Zaps",
|
||||
"nip05": "NIP-05",
|
||||
"name": "用户名",
|
||||
"about": "简介"
|
||||
|
@ -85,7 +85,7 @@ export const ContactsPage: React.FC = () => {
|
||||
relayPool?.subscribe('contacts-meta', [
|
||||
{
|
||||
kinds: [Kind.Metadata],
|
||||
authors: results.map((user) => user.id),
|
||||
authors: following.map((user) => user.id),
|
||||
},
|
||||
])
|
||||
}
|
||||
@ -196,7 +196,8 @@ export const ContactsPage: React.FC = () => {
|
||||
publicKey={getNpub(item.id)}
|
||||
validNip05={item?.valid_nip05}
|
||||
nip05={item?.nip05}
|
||||
lud06={item?.lnurl}
|
||||
lnurl={item?.lnurl}
|
||||
lnAddress={item?.ln_address}
|
||||
picture={item?.picture}
|
||||
/>
|
||||
</View>
|
||||
@ -225,7 +226,8 @@ export const ContactsPage: React.FC = () => {
|
||||
publicKey={getNpub(item.id)}
|
||||
validNip05={item?.valid_nip05}
|
||||
nip05={item?.nip05}
|
||||
lud06={item?.lnurl}
|
||||
lnurl={item?.lnurl}
|
||||
lnAddress={item?.ln_address}
|
||||
picture={item?.picture}
|
||||
/>
|
||||
</View>
|
||||
|
@ -141,7 +141,8 @@ export const ConversationsFeed: React.FC = () => {
|
||||
publicKey={user.id}
|
||||
validNip05={user?.valid_nip05}
|
||||
nip05={user?.nip05}
|
||||
lud06={user?.lnurl}
|
||||
lnurl={user?.lnurl}
|
||||
lnAddress={user?.ln_address}
|
||||
picture={user?.picture}
|
||||
/>
|
||||
</View>
|
||||
@ -240,7 +241,8 @@ export const ConversationsFeed: React.FC = () => {
|
||||
publicKey={item.id}
|
||||
validNip05={item?.valid_nip05}
|
||||
nip05={item?.nip05}
|
||||
lud06={item?.lnurl}
|
||||
lnurl={item?.lnurl}
|
||||
lnAddress={item?.ln_address}
|
||||
picture={item?.picture}
|
||||
/>
|
||||
</View>
|
||||
|
@ -85,8 +85,8 @@ export const GroupPage: React.FC<GroupPageProps> = ({ route }) => {
|
||||
|
||||
const loadGroupMessages: (subscribe: boolean) => void = (subscribe) => {
|
||||
if (database && publicKey && route.params.groupId) {
|
||||
getGroup(database, route.params.groupId).then(setGroup)
|
||||
updateGroupRead(database, route.params.groupId)
|
||||
getGroup(database, route.params.groupId).then(setGroup)
|
||||
getGroupMessages(database, route.params.groupId, {
|
||||
order: 'DESC',
|
||||
limit: pageSize,
|
||||
@ -175,7 +175,8 @@ export const GroupPage: React.FC<GroupPageProps> = ({ route }) => {
|
||||
publicKey={item?.id}
|
||||
validNip05={item?.valid_nip05}
|
||||
nip05={item?.nip05}
|
||||
lud06={item?.lnurl}
|
||||
lnurl={item?.lnurl}
|
||||
lnAddress={item?.ln_address}
|
||||
picture={item?.picture}
|
||||
/>
|
||||
</View>
|
||||
|
@ -57,22 +57,25 @@ export const HomePage: React.FC = () => {
|
||||
{
|
||||
kinds: [Kind.ChannelMessage],
|
||||
'#e': [publicKey],
|
||||
limit: 20,
|
||||
limit: 30,
|
||||
},
|
||||
{
|
||||
kinds: [Kind.EncryptedDirectMessage],
|
||||
'#p': [publicKey],
|
||||
since: directMessageResults[0]?.created_at ?? 0,
|
||||
limit: 30,
|
||||
},
|
||||
{
|
||||
kinds: [Kind.Text],
|
||||
'#p': [publicKey],
|
||||
since: mentionResults[0]?.created_at ?? 0,
|
||||
limit: 30,
|
||||
},
|
||||
{
|
||||
kinds: [Kind.Text],
|
||||
'#e': [publicKey],
|
||||
since: mentionResults[0]?.created_at ?? 0,
|
||||
limit: 30,
|
||||
},
|
||||
])
|
||||
})
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
View,
|
||||
} from 'react-native'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import { getLastReply, getMainNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { getMainNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
|
||||
import { UserContext } from '../../Contexts/UserContext'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
@ -18,7 +18,6 @@ import { ActivityIndicator, Button, Text } from 'react-native-paper'
|
||||
import NoteCard from '../../Components/NoteCard'
|
||||
import { useTheme } from '@react-navigation/native'
|
||||
import { FlashList, ListRenderItem } from '@shopify/flash-list'
|
||||
import { getLastReaction } from '../../Functions/DatabaseFunctions/Reactions'
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@ -41,7 +40,6 @@ export const MyFeed: React.FC<MyFeedProps> = ({ navigation }) => {
|
||||
const unsubscribe: () => void = () => {
|
||||
relayPool?.unsubscribe([
|
||||
'homepage-contacts-main',
|
||||
'homepage-contacts-meta',
|
||||
'homepage-contacts-replies',
|
||||
'homepage-contacts-reactions',
|
||||
'homepage-contacts-repost',
|
||||
@ -104,32 +102,16 @@ export const MyFeed: React.FC<MyFeedProps> = ({ navigation }) => {
|
||||
setNotes(notes)
|
||||
if (notes.length > 0) {
|
||||
const noteIds = notes.map((note) => note.id ?? '')
|
||||
relayPool?.subscribe('homepage-contacts-meta', [
|
||||
{
|
||||
kinds: [Kind.Metadata],
|
||||
authors: notes.map((note) => note.pubkey ?? ''),
|
||||
},
|
||||
])
|
||||
const authors = notes.map((note) => note.pubkey ?? '')
|
||||
|
||||
const lastReaction = await getLastReaction(database, {
|
||||
eventIds: notes.map((note) => note.id ?? ''),
|
||||
})
|
||||
relayPool?.subscribe('homepage-contacts-reactions', [
|
||||
{
|
||||
kinds: [Kind.Reaction],
|
||||
'#e': noteIds,
|
||||
since: lastReaction?.created_at ?? 0,
|
||||
kinds: [Kind.Metadata],
|
||||
authors,
|
||||
},
|
||||
])
|
||||
|
||||
const lastReply = await getLastReply(database, {
|
||||
eventIds: notes.map((note) => note.id ?? ''),
|
||||
})
|
||||
relayPool?.subscribe('homepage-contacts-replies', [
|
||||
{
|
||||
kinds: [Kind.Text],
|
||||
kinds: [Kind.Reaction, Kind.Text, 9735],
|
||||
'#e': noteIds,
|
||||
since: lastReply?.created_at ?? 0,
|
||||
},
|
||||
])
|
||||
const repostIds = notes
|
||||
|
@ -90,7 +90,7 @@ export const NotePage: React.FC<NotePageProps> = ({ route }) => {
|
||||
])
|
||||
relayPool?.subscribe(`notepage-replies-${route.params.noteId.substring(0, 8)}`, [
|
||||
{
|
||||
kinds: [Kind.Reaction, Kind.Text, Kind.RecommendRelay],
|
||||
kinds: [Kind.Reaction, Kind.Text, Kind.RecommendRelay, 9735],
|
||||
'#e': [route.params.noteId],
|
||||
},
|
||||
])
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from 'react-native'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import SInfo from 'react-native-sensitive-info'
|
||||
import { getLastReply, getMentionNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { getMentionNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import NoteCard from '../../Components/NoteCard'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
import { Kind } from 'nostr-tools'
|
||||
@ -19,7 +19,6 @@ import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityI
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { navigate } from '../../lib/Navigation'
|
||||
import { useFocusEffect } from '@react-navigation/native'
|
||||
import { getLastReaction } from '../../Functions/DatabaseFunctions/Reactions'
|
||||
import { getUnixTime } from 'date-fns'
|
||||
import { Config } from '../../Functions/DatabaseFunctions/Config'
|
||||
import { FlashList, ListRenderItem } from '@shopify/flash-list'
|
||||
@ -46,7 +45,6 @@ export const NotificationsFeed: React.FC = () => {
|
||||
'notification-feed',
|
||||
'notification-replies',
|
||||
'notification-reactions',
|
||||
'notification-meta',
|
||||
])
|
||||
updateLastSeen()
|
||||
}
|
||||
@ -106,23 +104,15 @@ export const NotificationsFeed: React.FC = () => {
|
||||
if (notes.length > 0) {
|
||||
const notedIds = notes.map((note) => note.id ?? '')
|
||||
const authors = notes.map((note) => note.pubkey ?? '')
|
||||
const lastReaction = await getLastReaction(database, { eventIds: notedIds })
|
||||
const lastReply = await getLastReply(database, { eventIds: notedIds })
|
||||
|
||||
relayPool?.subscribe('notification-meta', [
|
||||
relayPool?.subscribe('notification-reactions', [
|
||||
{
|
||||
kinds: [Kind.Metadata],
|
||||
authors,
|
||||
},
|
||||
{
|
||||
kinds: [Kind.Reaction],
|
||||
kinds: [Kind.Text, Kind.Reaction, 9735],
|
||||
'#e': notedIds,
|
||||
since: lastReaction?.created_at ?? 0,
|
||||
},
|
||||
{
|
||||
kinds: [Kind.Text],
|
||||
'#e': notedIds,
|
||||
since: lastReply?.created_at ?? 0,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ export const ProfileConfigPage: React.FC = () => {
|
||||
setAbout,
|
||||
lnurl,
|
||||
setLnurl,
|
||||
lnAddress,
|
||||
setLnAddress,
|
||||
nip05,
|
||||
setNip05,
|
||||
reloadUser,
|
||||
@ -72,8 +74,8 @@ export const ProfileConfigPage: React.FC = () => {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
reloadUser()
|
||||
if (isPublishingProfile) {
|
||||
reloadUser()
|
||||
setIsPublishingProfile(undefined)
|
||||
setShowNotification(isPublishingProfile)
|
||||
bottomSheetPictureRef.current?.close()
|
||||
@ -91,6 +93,7 @@ export const ProfileConfigPage: React.FC = () => {
|
||||
name,
|
||||
about,
|
||||
lud06: lnurl,
|
||||
lud16: lnAddress,
|
||||
nip05,
|
||||
picture,
|
||||
}),
|
||||
@ -149,6 +152,12 @@ export const ProfileConfigPage: React.FC = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const pasteLud16: () => void = () => {
|
||||
Clipboard.getString().then((value) => {
|
||||
setLnAddress(value ?? '')
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<ScrollView horizontal={false} showsVerticalScrollIndicator={false}>
|
||||
@ -164,7 +173,8 @@ export const ProfileConfigPage: React.FC = () => {
|
||||
name={name}
|
||||
pubKey={nPub ?? ''}
|
||||
src={picture}
|
||||
lud06={lnurl}
|
||||
lnurl={lnurl}
|
||||
lnAddress={lnAddress}
|
||||
size={100}
|
||||
/>
|
||||
)}
|
||||
@ -400,9 +410,23 @@ export const ProfileConfigPage: React.FC = () => {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
style={styles.spacer}
|
||||
mode='outlined'
|
||||
multiline
|
||||
label={t('profileConfigPage.lud16Label') ?? ''}
|
||||
onChangeText={setLnAddress}
|
||||
value={lnAddress}
|
||||
right={
|
||||
<TextInput.Icon
|
||||
icon='content-paste'
|
||||
onPress={pasteLud16}
|
||||
forceTextInputFocus={false}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
mode='contained'
|
||||
disabled={!lnurl || lnurl === ''}
|
||||
onPress={() => onPublishUser('lud06Published')}
|
||||
loading={isPublishingProfile !== undefined}
|
||||
>
|
||||
@ -418,7 +442,11 @@ export const ProfileConfigPage: React.FC = () => {
|
||||
onIconPress={() => setShowNotification(undefined)}
|
||||
onDismiss={() => setShowNotification(undefined)}
|
||||
>
|
||||
{t(`profileConfigPage.notifications.${showNotification}`, { nip05, lud06: lnurl })}
|
||||
{t(`profileConfigPage.notifications.${showNotification}`, {
|
||||
nip05,
|
||||
lud06: lnurl,
|
||||
lud16: lnAddress,
|
||||
})}
|
||||
</Snackbar>
|
||||
)}
|
||||
<UploadImage
|
||||
|
@ -55,14 +55,15 @@ export const ProfileConnectPage: React.FC = () => {
|
||||
setPrivateKey(key)
|
||||
}
|
||||
navigate('ProfileLoad')
|
||||
} else if (loginMethod === 'mnemonic') {
|
||||
const words = []
|
||||
for (let index = 1; index <= 12; index++) {
|
||||
words.push(mnemonicWords[index])
|
||||
}
|
||||
setPrivateKey(privateKeyFromSeedWords(words.join(' ')))
|
||||
navigate('ProfileLoad')
|
||||
}
|
||||
} else if (loginMethod === 'mnemonic') {
|
||||
const words = []
|
||||
for (let index = 1; index <= 12; index++) {
|
||||
words.push(mnemonicWords[index].trim())
|
||||
}
|
||||
setPrivateKey(privateKeyFromSeedWords(words.join(' ')))
|
||||
setMnemonicWords({})
|
||||
navigate('ProfileLoad')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,11 +30,7 @@ export const ProfileLoadPage: React.FC = () => {
|
||||
)
|
||||
setTimeout(() => loadMeta(), 1000)
|
||||
return () =>
|
||||
relayPool?.unsubscribe([
|
||||
'profile-load-meta',
|
||||
'profile-load-notes',
|
||||
'profile-load-meta-pets',
|
||||
])
|
||||
relayPool?.unsubscribe(['profile-load-meta', 'profile-load-notes', 'profile-load-others'])
|
||||
}, []),
|
||||
)
|
||||
|
||||
@ -58,7 +54,13 @@ export const ProfileLoadPage: React.FC = () => {
|
||||
if (publicKey && relayPoolReady) {
|
||||
relayPool?.subscribe('profile-load-meta', [
|
||||
{
|
||||
kinds: [Kind.Contacts, Kind.Metadata, Kind.ChannelCreation, Kind.ChannelMetadata, 1002],
|
||||
kinds: [Kind.Metadata, Kind.Contacts],
|
||||
authors: [publicKey],
|
||||
},
|
||||
])
|
||||
relayPool?.subscribe('profile-load-others', [
|
||||
{
|
||||
kinds: [Kind.ChannelCreation, Kind.ChannelMetadata, 1002],
|
||||
authors: [publicKey],
|
||||
},
|
||||
])
|
||||
|
@ -98,7 +98,7 @@ export const ProfilePage: React.FC<ProfilePageProps> = ({ route }) => {
|
||||
if (results.length > 0) {
|
||||
relayPool?.subscribe(`profile-answers${route.params.pubKey.substring(0, 8)}`, [
|
||||
{
|
||||
kinds: [Kind.Reaction, Kind.Text, Kind.RecommendRelay],
|
||||
kinds: [Kind.Reaction, Kind.Text, Kind.RecommendRelay, 9735],
|
||||
'#e': results.map((note) => note.id ?? ''),
|
||||
},
|
||||
])
|
||||
@ -111,7 +111,11 @@ export const ProfilePage: React.FC<ProfilePageProps> = ({ route }) => {
|
||||
const subscribeProfile: () => Promise<void> = async () => {
|
||||
relayPool?.subscribe(`profile-user${route.params.pubKey.substring(0, 8)}`, [
|
||||
{
|
||||
kinds: [Kind.Metadata, Kind.Contacts],
|
||||
kinds: [Kind.Metadata],
|
||||
authors: [route.params.pubKey],
|
||||
},
|
||||
{
|
||||
kinds: [Kind.Contacts],
|
||||
authors: [route.params.pubKey],
|
||||
},
|
||||
])
|
||||
@ -150,15 +154,20 @@ export const ProfilePage: React.FC<ProfilePageProps> = ({ route }) => {
|
||||
>
|
||||
<Surface style={styles.container} elevation={1}>
|
||||
<View style={styles.profileData}>
|
||||
<ProfileData
|
||||
username={user?.name}
|
||||
publicKey={route.params.pubKey}
|
||||
validNip05={user?.valid_nip05}
|
||||
nip05={user?.nip05}
|
||||
lud06={user?.lnurl}
|
||||
picture={user?.picture}
|
||||
/>
|
||||
<Text>{user?.follower && user.follower > 0 ? t('profilePage.isFollower') : ''}</Text>
|
||||
<View style={styles.profilePicture}>
|
||||
<ProfileData
|
||||
username={user?.name}
|
||||
publicKey={route.params.pubKey}
|
||||
validNip05={user?.valid_nip05}
|
||||
nip05={user?.nip05}
|
||||
lnurl={user?.lnurl}
|
||||
lnAddress={user?.ln_address}
|
||||
picture={user?.picture}
|
||||
/>
|
||||
</View>
|
||||
<View>
|
||||
<Text>{user?.follower && user.follower > 0 ? t('profilePage.isFollower') : ''}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View>
|
||||
<Text>{user?.about}</Text>
|
||||
@ -214,6 +223,9 @@ const styles = StyleSheet.create({
|
||||
margin: 16,
|
||||
bottom: 70,
|
||||
},
|
||||
profilePicture: {
|
||||
width: '80%',
|
||||
},
|
||||
list: {
|
||||
padding: 16,
|
||||
},
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from 'react-native'
|
||||
import { FlashList, ListRenderItem } from '@shopify/flash-list'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import { getLastReply, getReactedNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { getReactedNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
|
||||
import { UserContext } from '../../Contexts/UserContext'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
@ -19,7 +19,6 @@ import NoteCard from '../../Components/NoteCard'
|
||||
import { useTheme } from '@react-navigation/native'
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||
import { t } from 'i18next'
|
||||
import { getLastReaction } from '../../Functions/DatabaseFunctions/Reactions'
|
||||
|
||||
interface ReactionsFeedProps {
|
||||
navigation: any
|
||||
@ -39,7 +38,6 @@ export const ReactionsFeed: React.FC<ReactionsFeedProps> = ({ navigation }) => {
|
||||
const unsubscribe: () => void = () => {
|
||||
relayPool?.unsubscribe([
|
||||
'homepage-contacts-main',
|
||||
'homepage-contacts-meta',
|
||||
'homepage-contacts-replies',
|
||||
'homepage-contacts-reactions',
|
||||
'homepage-contacts-repost',
|
||||
@ -99,33 +97,17 @@ export const ReactionsFeed: React.FC<ReactionsFeedProps> = ({ navigation }) => {
|
||||
getReactedNotes(database, publicKey, pageSize).then(async (notes) => {
|
||||
setNotes(notes)
|
||||
if (notes.length > 0) {
|
||||
const noteIds = notes.map((note) => note.id ?? '')
|
||||
relayPool?.subscribe('homepage-contacts-meta', [
|
||||
{
|
||||
kinds: [Kind.Metadata],
|
||||
authors: notes.map((note) => note.pubkey ?? ''),
|
||||
},
|
||||
])
|
||||
const notedIds = notes.map((note) => note.id ?? '')
|
||||
const authors = notes.map((note) => note.pubkey ?? '')
|
||||
|
||||
const lastReaction = await getLastReaction(database, {
|
||||
eventIds: notes.map((note) => note.id ?? ''),
|
||||
})
|
||||
relayPool?.subscribe('homepage-contacts-reactions', [
|
||||
{
|
||||
kinds: [Kind.Reaction],
|
||||
'#e': noteIds,
|
||||
since: lastReaction?.created_at ?? 0,
|
||||
kinds: [Kind.Metadata],
|
||||
authors,
|
||||
},
|
||||
])
|
||||
|
||||
const lastReply = await getLastReply(database, {
|
||||
eventIds: notes.map((note) => note.id ?? ''),
|
||||
})
|
||||
relayPool?.subscribe('homepage-contacts-replies', [
|
||||
{
|
||||
kinds: [Kind.Text],
|
||||
'#e': noteIds,
|
||||
since: lastReply?.created_at ?? 0,
|
||||
kinds: [Kind.Text, Kind.Reaction, 9735],
|
||||
'#e': notedIds,
|
||||
},
|
||||
])
|
||||
|
||||
|
@ -42,7 +42,6 @@ export const RelaysPage: React.FC = () => {
|
||||
{
|
||||
kinds: [1002],
|
||||
authors: [publicKey],
|
||||
limit: 1,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from 'react-native'
|
||||
import { FlashList, ListRenderItem } from '@shopify/flash-list'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import { getLastReply, getRepostedNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { getRepostedNotes, Note } from '../../Functions/DatabaseFunctions/Notes'
|
||||
import { handleInfinityScroll } from '../../Functions/NativeFunctions'
|
||||
import { UserContext } from '../../Contexts/UserContext'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
@ -19,7 +19,6 @@ import NoteCard from '../../Components/NoteCard'
|
||||
import { useTheme } from '@react-navigation/native'
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||
import { t } from 'i18next'
|
||||
import { getLastReaction } from '../../Functions/DatabaseFunctions/Reactions'
|
||||
|
||||
interface RepostsFeedProps {
|
||||
navigation: any
|
||||
@ -39,7 +38,6 @@ export const RepostsFeed: React.FC<RepostsFeedProps> = ({ navigation }) => {
|
||||
const unsubscribe: () => void = () => {
|
||||
relayPool?.unsubscribe([
|
||||
'homepage-contacts-main',
|
||||
'homepage-contacts-meta',
|
||||
'homepage-contacts-replies',
|
||||
'homepage-contacts-reactions',
|
||||
'homepage-contacts-repost',
|
||||
@ -105,26 +103,9 @@ export const RepostsFeed: React.FC<RepostsFeedProps> = ({ navigation }) => {
|
||||
kinds: [Kind.Metadata],
|
||||
authors: notes.map((note) => note.pubkey ?? ''),
|
||||
},
|
||||
])
|
||||
const lastReaction = await getLastReaction(database, {
|
||||
eventIds: notes.map((note) => note.id ?? ''),
|
||||
})
|
||||
relayPool?.subscribe('homepage-contacts-reactions', [
|
||||
{
|
||||
kinds: [Kind.Reaction],
|
||||
kinds: [Kind.Text, Kind.Reaction, 9735],
|
||||
'#e': noteIds,
|
||||
since: lastReaction?.created_at ?? 0,
|
||||
},
|
||||
])
|
||||
|
||||
const lastReply = await getLastReply(database, {
|
||||
eventIds: notes.map((note) => note.id ?? ''),
|
||||
})
|
||||
relayPool?.subscribe('homepage-contacts-replies', [
|
||||
{
|
||||
kinds: [Kind.Text],
|
||||
'#e': noteIds,
|
||||
since: lastReply?.created_at ?? 0,
|
||||
},
|
||||
])
|
||||
const repostIds = notes
|
||||
|
@ -137,7 +137,8 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
|
||||
publicKey={item?.id}
|
||||
validNip05={item?.valid_nip05}
|
||||
nip05={item?.nip05}
|
||||
lud06={item?.lnurl}
|
||||
lnurl={item?.lnurl}
|
||||
lnAddress={item?.ln_address}
|
||||
picture={item?.picture}
|
||||
/>
|
||||
</View>
|
||||
|
@ -7278,11 +7278,6 @@ react-native-svg@^13.7.0:
|
||||
css-select "^5.1.0"
|
||||
css-tree "^1.1.3"
|
||||
|
||||
react-native-swipe-gestures@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.5.tgz#a172cb0f3e7478ccd681fd36b8bfbcdd098bde7c"
|
||||
integrity sha512-Ns7Bn9H/Tyw278+5SQx9oAblDZ7JixyzeOczcBK8dipQk2pD7Djkcfnf1nB/8RErAmMLL9iXgW0QHqiII8AhKw==
|
||||
|
||||
react-native-tab-view@^3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/react-native-tab-view/-/react-native-tab-view-3.3.4.tgz#856d4527f3bbf05e2649302ec80abe9f2f004666"
|
||||
|
Loading…
Reference in New Issue
Block a user