mirror of
https://github.com/KoalaSat/nostros.git
synced 2024-09-28 22:30:41 +00:00
NIP-65 Publish relay list
This commit is contained in:
parent
fd711c0c35
commit
b6532b83e1
3
.github/workflows/notify.yml
vendored
3
.github/workflows/notify.yml
vendored
@ -29,5 +29,4 @@ jobs:
|
||||
~/go/bin/noscl relay add "wss://nostr.ono.re"
|
||||
~/go/bin/noscl relay add "wss://relay.grunch.dev"
|
||||
~/go/bin/noscl relay add "wss://nostr.developer.li"
|
||||
~/go/bin/noscl publish '${{ github.event.release.body }}'
|
||||
~/go/bin/noscl publish ${{ github.event.release.body }}
|
||||
~/go/bin/noscl publish "${{ github.event.release.body }}"
|
||||
|
@ -71,7 +71,9 @@ public class Event {
|
||||
} else if (kind.equals("43")) {
|
||||
hideGroupMessage(database);
|
||||
} else if (kind.equals("44")) {
|
||||
blockUser(database);
|
||||
muteUser(database);
|
||||
} else if (kind.equals("1002")) {
|
||||
saveRelays(database);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
@ -209,13 +211,14 @@ public class Event {
|
||||
database.replace("nostros_notes", null, values);
|
||||
}
|
||||
|
||||
protected void blockUser(SQLiteDatabase database) throws JSONException {
|
||||
protected void muteUser(SQLiteDatabase database) throws JSONException {
|
||||
JSONArray pTags = filterTags("p");
|
||||
String groupId = pTags.getJSONArray(0).getString(1);
|
||||
String query = "SELECT id FROM nostros_users WHERE id = ?";
|
||||
@SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {groupId});
|
||||
|
||||
if (cursor.getCount() == 0) {
|
||||
} else {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("muted_groups", 1);
|
||||
String whereClause = "id = ?";
|
||||
@ -409,7 +412,7 @@ public class Event {
|
||||
values.put("valid_nip05", validateNip05(nip05) ? 1 : 0);
|
||||
values.put("blocked", 0);
|
||||
database.insert("nostros_users", null, values);
|
||||
} else if (cursor.moveToFirst() && (cursor.isNull(0) || created_at > cursor.getInt(0))) {
|
||||
} 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);
|
||||
}
|
||||
@ -422,23 +425,71 @@ public class Event {
|
||||
}
|
||||
|
||||
protected void savePets(SQLiteDatabase database) throws JSONException {
|
||||
String queryLast = "SELECT pet_at FROM nostros_users ORDER BY pet_at DESC LIMIT 1";
|
||||
Cursor cursorLast = database.rawQuery(queryLast, new String[] {});
|
||||
if (cursorLast.moveToFirst() && created_at > cursorLast.getInt(0)) {
|
||||
ContentValues valuesIntial = new ContentValues();
|
||||
valuesIntial.put("contact", 0);
|
||||
String whereClauseInitial = "id = ?";
|
||||
database.update("nostros_users", valuesIntial, whereClauseInitial, new String[]{});
|
||||
|
||||
for (int i = 0; i < tags.length(); ++i) {
|
||||
JSONArray tag = tags.getJSONArray(i);
|
||||
String petId = tag.getString(1);
|
||||
String name = "";
|
||||
if (tag.length() >= 4) {
|
||||
name = tag.getString(3);
|
||||
}
|
||||
String query = "SELECT * FROM nostros_users WHERE id = ?";
|
||||
Cursor cursor = database.rawQuery(query, new String[] {petId});
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("pet_at", created_at);
|
||||
values.put("contact", 1);
|
||||
values.put("name", name);
|
||||
values.put("blocked", 0);
|
||||
|
||||
if (cursor.getCount() == 0) {
|
||||
values.put("id", petId);
|
||||
database.insert("nostros_users", null, values);
|
||||
} else {
|
||||
String whereClause = "id = ?";
|
||||
String[] whereArgs = new String[] {
|
||||
petId
|
||||
};
|
||||
database.update("nostros_users", values, whereClause, whereArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void saveRelays(SQLiteDatabase database) throws JSONException {
|
||||
for (int i = 0; i < tags.length(); ++i) {
|
||||
JSONArray tag = tags.getJSONArray(i);
|
||||
String petId = tag.getString(1);
|
||||
String name = "";
|
||||
if (tag.length() >= 4) {
|
||||
name = tag.getString(3);
|
||||
String url = tag.getString(1);
|
||||
String mode = "";
|
||||
if (tag.length() > 1) {
|
||||
mode = tag.getString(2);
|
||||
}
|
||||
String query = "SELECT * FROM nostros_users WHERE id = ?";
|
||||
Cursor cursor = database.rawQuery(query, new String[] {petId});
|
||||
|
||||
String query = "SELECT updated_at FROM nostros_relays WHERE url = ?";
|
||||
Cursor cursor = database.rawQuery(query, new String[] {url});
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("url", url);
|
||||
values.put("mode", mode);
|
||||
|
||||
if (cursor.getCount() == 0) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("id", petId);
|
||||
values.put("name", name);
|
||||
values.put("contact", true);
|
||||
values.put("blocked", 0);
|
||||
values.put("pet_at", created_at);
|
||||
database.insert("nostros_users", null, values);
|
||||
values.put("active", 0);
|
||||
values.put("global_feed", 0);
|
||||
values.put("manual", 1);
|
||||
database.insert("nostros_relays", null, values);
|
||||
} else if (cursor.moveToFirst() && created_at > cursor.getInt(0)) {
|
||||
values.put("updated_at", created_at);
|
||||
String whereClause = "url = ?";
|
||||
String[] whereArgs = new String[] {
|
||||
url
|
||||
};
|
||||
database.update("nostros_relays", values, whereClause, whereArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +173,10 @@ public class DatabaseModule {
|
||||
database.execSQL("ALTER TABLE nostros_group_messages ADD COLUMN read INT DEFAULT 0;");
|
||||
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 mode TEXT;");
|
||||
} catch (SQLException e) { }
|
||||
}
|
||||
|
||||
public void saveEvent(JSONObject data, String userPubKey, String relayUrl) throws JSONException {
|
||||
|
@ -27,6 +27,12 @@ export const MenuItems: React.FC = () => {
|
||||
const { t } = useTranslation('common')
|
||||
const theme = useTheme()
|
||||
|
||||
const [activerelays, setActiveRelays] = React.useState<number>(0)
|
||||
|
||||
React.useEffect(() => {
|
||||
setActiveRelays(relays.filter((relay) => relay.active).length)
|
||||
}, [relays])
|
||||
|
||||
const onPressLogout: () => void = () => {
|
||||
logout()
|
||||
}
|
||||
@ -110,12 +116,12 @@ export const MenuItems: React.FC = () => {
|
||||
onPress={() => onPressItem('relays', 0)}
|
||||
onTouchEnd={() => setDrawerItemIndex(-1)}
|
||||
right={() =>
|
||||
relays.length < 1 ? (
|
||||
activerelays < 1 ? (
|
||||
<Text style={{ color: theme.colors.error }}>{t('menuItems.notConnected')}</Text>
|
||||
) : (
|
||||
<Text style={{ color: theme.colors.inversePrimary }}>
|
||||
<Text style={{ color: '#7ADC70' }}>
|
||||
{t('menuItems.connectedRelays', {
|
||||
number: relays.filter((relay) => relay.active).length,
|
||||
number: activerelays,
|
||||
})}
|
||||
</Text>
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { t } from 'i18next'
|
||||
import * as React from 'react'
|
||||
import { StyleSheet, View, ListRenderItem, Switch, FlatList } from 'react-native'
|
||||
import { IconButton, List, Snackbar, Text, useTheme } from 'react-native-paper'
|
||||
import { Button, IconButton, List, Snackbar, Text, useTheme } from 'react-native-paper'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
import { UserContext } from '../../Contexts/UserContext'
|
||||
@ -10,6 +10,7 @@ import {
|
||||
getUser,
|
||||
updateUserBlock,
|
||||
updateUserContact,
|
||||
updateUserMutesGroups,
|
||||
User,
|
||||
} from '../../Functions/DatabaseFunctions/Users'
|
||||
import { populatePets, username } from '../../Functions/RelayFunctions/Users'
|
||||
@ -22,6 +23,8 @@ import { relayToColor } from '../../Functions/NativeFunctions'
|
||||
import { Relay } from '../../Functions/DatabaseFunctions/Relays'
|
||||
import ProfileShare from '../ProfileShare'
|
||||
import { ScrollView } from 'react-native-gesture-handler'
|
||||
import { Kind } from 'nostr-tools'
|
||||
import { getUnixTime } from 'date-fns'
|
||||
|
||||
interface ProfileActionsProps {
|
||||
user: User
|
||||
@ -40,10 +43,12 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
|
||||
const { relayPool, updateRelayItem } = React.useContext(RelayPoolContext)
|
||||
const [isContact, setIsContact] = React.useState<boolean>()
|
||||
const [isBlocked, setIsBlocked] = React.useState<boolean>()
|
||||
const [isMuted, setIsMuted] = React.useState<boolean>()
|
||||
const [showNotification, setShowNotification] = React.useState<undefined | string>()
|
||||
const [showNotificationRelay, setShowNotificationRelay] = React.useState<undefined | string>()
|
||||
const bottomSheetRelaysRef = React.useRef<RBSheet>(null)
|
||||
const bottomSheetShareRef = React.useRef<RBSheet>(null)
|
||||
const bottomSheetMuteRef = React.useRef<RBSheet>(null)
|
||||
const [userRelays, setUserRelays] = React.useState<NoteRelay[]>([])
|
||||
const [openLn, setOpenLn] = React.useState<boolean>(false)
|
||||
|
||||
@ -52,6 +57,27 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
|
||||
loadRelays()
|
||||
}, [])
|
||||
|
||||
const muteUser: () => void = () => {
|
||||
if (publicKey && relayPool && database && user.id) {
|
||||
relayPool
|
||||
?.sendEvent({
|
||||
content: '',
|
||||
created_at: getUnixTime(new Date()),
|
||||
kind: Kind.ChannelMuteUser,
|
||||
pubkey: publicKey,
|
||||
tags: [['p', user.id]],
|
||||
})
|
||||
.then(() => {
|
||||
if (database) {
|
||||
updateUserMutesGroups(database, user.id, true).then(() => {
|
||||
setIsMuted(true)
|
||||
bottomSheetMuteRef.current?.close()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const loadRelays: () => void = () => {
|
||||
if (database) {
|
||||
getUserRelays(database, user.id).then((results) => {
|
||||
@ -69,6 +95,7 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
|
||||
setUser(result)
|
||||
setIsContact(result.contact)
|
||||
setIsBlocked(result.blocked !== undefined && result.blocked > 0)
|
||||
setIsMuted(result.muted_groups !== undefined && result.muted_groups > 0)
|
||||
} else if (user.id === publicKey) {
|
||||
setUser({
|
||||
id: publicKey,
|
||||
@ -203,12 +230,13 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
|
||||
<View style={styles.mainLayout}>
|
||||
<View style={styles.actionButton}>
|
||||
<IconButton
|
||||
icon={user?.blocked && user?.blocked > 0 ? 'account-cancel' : 'account-cancel-outline'}
|
||||
icon='account-cancel-outline'
|
||||
iconColor={isBlocked ? theme.colors.error : theme.colors.onSecondaryContainer}
|
||||
size={28}
|
||||
onPress={onChangeBlockUser}
|
||||
/>
|
||||
<Text>
|
||||
{t(user?.blocked && user?.blocked > 0 ? 'profileCard.unblock' : 'profileCard.block')}
|
||||
<Text style={isBlocked ? { color: theme.colors.error } : {}}>
|
||||
{t(isBlocked ? 'profileCard.blocked' : 'profileCard.block')}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.actionButton}>
|
||||
@ -228,6 +256,20 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
|
||||
<Text>{t('profileCard.relaysTitle')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.mainLayout}>
|
||||
<View style={styles.actionButton}>
|
||||
<IconButton
|
||||
icon={isMuted ? 'volume-off' : 'volume-high'}
|
||||
iconColor={isMuted ? theme.colors.error : theme.colors.onSecondaryContainer}
|
||||
size={28}
|
||||
onPress={() => !isMuted && bottomSheetMuteRef.current?.open()}
|
||||
disabled={user.id === publicKey}
|
||||
/>
|
||||
<Text style={isMuted ? { color: theme.colors.error } : {}}>
|
||||
{t(isMuted ? 'profileCard.muted' : 'profileCard.mute')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<RBSheet ref={bottomSheetRelaysRef} closeOnDragDown={true} customStyles={bottomSheetStyles}>
|
||||
<View>
|
||||
<Text variant='titleLarge'>{t('profileCard.relaysTitle')}</Text>
|
||||
@ -265,6 +307,30 @@ export const ProfileActions: React.FC<ProfileActionsProps> = ({
|
||||
<RBSheet ref={bottomSheetShareRef} closeOnDragDown={true} customStyles={bottomSheetStyles}>
|
||||
<ProfileShare user={user} />
|
||||
</RBSheet>
|
||||
<RBSheet ref={bottomSheetMuteRef} closeOnDragDown={true} customStyles={bottomSheetStyles}>
|
||||
<View style={styles.muteContainer}>
|
||||
<Text variant='titleLarge'>
|
||||
{t('profileCard.muteUser', { username: username(user) })}
|
||||
</Text>
|
||||
<View style={[styles.warning, { backgroundColor: '#683D00' }]}>
|
||||
<Text variant='titleSmall' style={[styles.warningTitle, { color: '#FFDCBB' }]}>
|
||||
{t('profileCard.muteWarningTitle')}
|
||||
</Text>
|
||||
<Text style={{ color: '#FFDCBB' }}>{t('profileCard.muteWarning')}</Text>
|
||||
</View>
|
||||
<Button style={styles.buttonSpacer} mode='contained' onPress={muteUser}>
|
||||
{t('profileCard.muteForever', { username: username(user) })}
|
||||
</Button>
|
||||
<Button
|
||||
mode='outlined'
|
||||
onPress={() => {
|
||||
bottomSheetMuteRef.current?.close()
|
||||
}}
|
||||
>
|
||||
{t('profileCard.cancel')}
|
||||
</Button>
|
||||
</View>
|
||||
</RBSheet>
|
||||
<LnPayment setOpen={setOpenLn} open={openLn} user={user} />
|
||||
{showNotification && (
|
||||
<Snackbar
|
||||
@ -307,6 +373,22 @@ const styles = StyleSheet.create({
|
||||
paddingLeft: 16,
|
||||
textAlign: 'center',
|
||||
},
|
||||
warning: {
|
||||
borderRadius: 4,
|
||||
padding: 16,
|
||||
marginTop: 16,
|
||||
marginBottom: 16,
|
||||
},
|
||||
warningTitle: {
|
||||
marginBottom: 8,
|
||||
},
|
||||
buttonSpacer: {
|
||||
marginTop: 16,
|
||||
marginBottom: 16,
|
||||
},
|
||||
muteContainer: {
|
||||
paddingRight: 16,
|
||||
},
|
||||
})
|
||||
|
||||
export default ProfileActions
|
||||
|
@ -6,6 +6,8 @@ import debounce from 'lodash.debounce'
|
||||
import { getActiveRelays, getRelays, Relay } from '../Functions/DatabaseFunctions/Relays'
|
||||
import { UserContext } from './UserContext'
|
||||
import { randomInt } from '../Functions/NativeFunctions'
|
||||
import { getUnixTime } from 'date-fns'
|
||||
import { Event } from '../../lib/nostr/Events'
|
||||
|
||||
export interface RelayPoolContextProps {
|
||||
relayPoolReady: boolean
|
||||
@ -54,6 +56,19 @@ export const RelayPoolContextProvider = ({
|
||||
const [relays, setRelays] = React.useState<Relay[]>([])
|
||||
const [displayRelayDrawer, setDisplayrelayDrawer] = React.useState<string>()
|
||||
|
||||
const sendRelays: (relayList: Relay[]) => void = (relayList) => {
|
||||
if (publicKey && relayList.length > 0) {
|
||||
const event: Event = {
|
||||
content: '',
|
||||
created_at: getUnixTime(new Date()),
|
||||
kind: 1002,
|
||||
pubkey: publicKey,
|
||||
tags: relayList.map((relay) => ['r', relay.url, relay.mode ?? '']),
|
||||
}
|
||||
relayPool?.sendEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
const changeEventIdHandler: (event: WebsocketEvent) => void = (event) => {
|
||||
setLastEventId(event.eventId)
|
||||
}
|
||||
@ -92,15 +107,15 @@ export const RelayPoolContextProvider = ({
|
||||
}
|
||||
}
|
||||
|
||||
const loadRelays: () => Promise<void> = async () => {
|
||||
return await new Promise<void>((resolve, _reject) => {
|
||||
const loadRelays: () => Promise<Relay[]> = async () => {
|
||||
return await new Promise<Relay[]>((resolve, _reject) => {
|
||||
if (database) {
|
||||
getRelays(database).then((results) => {
|
||||
setRelays(results)
|
||||
resolve()
|
||||
resolve(results)
|
||||
})
|
||||
} else {
|
||||
resolve()
|
||||
resolve([])
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -118,7 +133,7 @@ export const RelayPoolContextProvider = ({
|
||||
return await new Promise((resolve, _reject) => {
|
||||
if (relayPool && database && publicKey) {
|
||||
relayPool.update(relay.url, relay.active ?? 1, relay.global_feed ?? 1, () => {
|
||||
loadRelays().then(resolve)
|
||||
loadRelays().then(() => resolve())
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -129,7 +144,10 @@ export const RelayPoolContextProvider = ({
|
||||
return await new Promise((resolve, _reject) => {
|
||||
if (relayPool && database && publicKey) {
|
||||
relayPool.add(relay.url, () => {
|
||||
loadRelays().then(resolve)
|
||||
loadRelays().then((results) => {
|
||||
sendRelays(results)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -140,7 +158,10 @@ export const RelayPoolContextProvider = ({
|
||||
return await new Promise((resolve, _reject) => {
|
||||
if (relayPool && database && publicKey) {
|
||||
relayPool.remove(relay.url, () => {
|
||||
loadRelays().then(resolve)
|
||||
loadRelays().then((results) => {
|
||||
sendRelays(results)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -37,9 +37,9 @@ export const updateConversationRead: (
|
||||
return db.execute(userQuery, [1, conversationId])
|
||||
}
|
||||
|
||||
export const updateAllRead: (db: QuickSQLiteConnection) => Promise<QueryResult | null> = async (
|
||||
db,
|
||||
) => {
|
||||
export const updateAllDirectMessagesRead: (
|
||||
db: QuickSQLiteConnection,
|
||||
) => Promise<QueryResult | null> = async (db) => {
|
||||
const userQuery = `UPDATE nostros_direct_messages SET read = ?`
|
||||
return db.execute(userQuery, [1])
|
||||
}
|
||||
|
@ -146,6 +146,13 @@ export const deleteGroup: (
|
||||
return db.execute(userQuery, [groupId])
|
||||
}
|
||||
|
||||
export const updateAllDirectMessagesRead: (
|
||||
db: QuickSQLiteConnection,
|
||||
) => Promise<QueryResult | null> = async (db) => {
|
||||
const userQuery = `UPDATE nostros_group_messages SET read = ?`
|
||||
return db.execute(userQuery, [1])
|
||||
}
|
||||
|
||||
export const activateGroup: (
|
||||
db: QuickSQLiteConnection,
|
||||
groupId: string,
|
||||
|
@ -10,6 +10,7 @@ export interface Relay {
|
||||
global_feed?: number
|
||||
resilient?: number
|
||||
manual?: number
|
||||
mode?: 'read' | 'write' | ''
|
||||
}
|
||||
|
||||
export interface RelayInfo {
|
||||
|
@ -14,6 +14,7 @@ export interface User {
|
||||
created_at?: number
|
||||
valid_nip05?: boolean
|
||||
blocked?: number
|
||||
muted_groups?: number
|
||||
}
|
||||
|
||||
const databaseToEntity: (object: object) => User = (object) => {
|
||||
@ -42,6 +43,17 @@ export const updateUserBlock: (
|
||||
return db.execute(userQuery, [blocked ? 1 : 0, userId])
|
||||
}
|
||||
|
||||
export const updateUserMutesGroups: (
|
||||
db: QuickSQLiteConnection,
|
||||
userId: string,
|
||||
muted: boolean,
|
||||
) => Promise<QueryResult | null> = async (db, userId, muted) => {
|
||||
const userQuery = `UPDATE nostros_users SET muted_groups = ? WHERE id = ?`
|
||||
|
||||
await addUser(userId, db)
|
||||
return db.execute(userQuery, [muted ? 1 : 0, userId])
|
||||
}
|
||||
|
||||
export const getUser: (pubkey: string, db: QuickSQLiteConnection) => Promise<User | null> = async (
|
||||
pubkey,
|
||||
db,
|
||||
|
@ -370,7 +370,10 @@
|
||||
"share": "Teilen",
|
||||
"unblock": "Erlauben",
|
||||
"unfollow": "Abo entfernen",
|
||||
"relaysTitle": "Relays"
|
||||
"relaysTitle": "Relays",
|
||||
"blocked": "Blocked",
|
||||
"mute": "Mute",
|
||||
"muted": "Muted"
|
||||
},
|
||||
"conversationsFeed": {
|
||||
"openMessage": "Unterhaltung beginnen",
|
||||
|
@ -313,7 +313,8 @@
|
||||
},
|
||||
"groupPage": {
|
||||
"typeMessage": "Type message",
|
||||
"replyText": "Message"
|
||||
"replyText": "Message",
|
||||
"admin": "ADMIN"
|
||||
},
|
||||
"groupHeaderIcon": {
|
||||
"delete": "Leave group",
|
||||
@ -368,7 +369,15 @@
|
||||
"block": "Block",
|
||||
"unblock": "Unblock",
|
||||
"share": "Share",
|
||||
"relaysTitle": "Relays"
|
||||
"relaysTitle": "Relays",
|
||||
"blocked": "Blocked",
|
||||
"mute": "Mute",
|
||||
"muted": "Muted",
|
||||
"muteUser": "Mute {{username}} on chats",
|
||||
"muteWarningTitle": "Important",
|
||||
"muteWarning": "This action will hide the messages of this user on all chats and cannot be undone.",
|
||||
"muteForever": "Mute {{username}} forever",
|
||||
"cancel": "Cancel"
|
||||
},
|
||||
"conversationsFeed": {
|
||||
"openMessage": "Start conversation",
|
||||
|
@ -352,7 +352,10 @@
|
||||
"share": "Share",
|
||||
"unblock": "Desbloquear",
|
||||
"unfollow": "Siguiendo",
|
||||
"relaysTitle": "Relays"
|
||||
"relaysTitle": "Relays",
|
||||
"blocked": "Bloqueado",
|
||||
"mute": "Silenciar",
|
||||
"muted": "Silenciado"
|
||||
},
|
||||
"conversationsFeed": {
|
||||
"openMessage": "Comenzar conversación",
|
||||
|
@ -336,7 +336,10 @@
|
||||
"block": "Bloquer",
|
||||
"unblock": "Débloquer",
|
||||
"unfollow": "Abonné",
|
||||
"relaysTitle": "Relays"
|
||||
"relaysTitle": "Relays",
|
||||
"blocked": "Blocked",
|
||||
"mute": "Mute",
|
||||
"muted": "Muted"
|
||||
},
|
||||
"conversationsFeed": {
|
||||
"openMessage": "Commencer la conversation",
|
||||
|
@ -344,7 +344,10 @@
|
||||
"unblock": "Desbloquear",
|
||||
"unfollow": "Following",
|
||||
"share": "Share",
|
||||
"relaysTitle": "Relays"
|
||||
"relaysTitle": "Relays",
|
||||
"blocked": "Blocked",
|
||||
"mute": "Mute",
|
||||
"muted": "Muted"
|
||||
},
|
||||
"conversationsFeed": {
|
||||
"openMessage": "Start conversation",
|
||||
|
@ -350,7 +350,10 @@
|
||||
"share": "分享",
|
||||
"copyNip05": "复制 NIP-05",
|
||||
"copyNPub": "复制公钥",
|
||||
"relaysTitle": "中继"
|
||||
"relaysTitle": "中继",
|
||||
"blocked": "Blocked",
|
||||
"mute": "Mute",
|
||||
"muted": "Muted"
|
||||
},
|
||||
"conversationsFeed": {
|
||||
"openMessage": "发送私信",
|
||||
|
@ -18,7 +18,7 @@ import ConfigPage from '../ConfigPage'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
import RelayCard from '../../Components/RelayCard'
|
||||
import { updateAllRead } from '../../Functions/DatabaseFunctions/DirectMessages'
|
||||
import { updateAllDirectMessagesRead } from '../../Functions/DatabaseFunctions/DirectMessages'
|
||||
import { getUnixTime } from 'date-fns'
|
||||
import ContactsPage from '../ContactsPage'
|
||||
import GroupPage from '../GroupPage'
|
||||
@ -60,8 +60,13 @@ export const HomeNavigator: React.FC = () => {
|
||||
bottomSheetRef.current?.open()
|
||||
}
|
||||
|
||||
const onPressCheckAll: () => void = () => {
|
||||
if (database) updateAllRead(database)
|
||||
const onMesssagesPressCheckAll: () => void = () => {
|
||||
if (database) updateAllDirectMessagesRead(database)
|
||||
setRefreshBottomBarAt(getUnixTime(new Date()))
|
||||
}
|
||||
|
||||
const onGroupsPressCheckAll: () => void = () => {
|
||||
if (database) updateAllDirectMessagesRead(database)
|
||||
setRefreshBottomBarAt(getUnixTime(new Date()))
|
||||
}
|
||||
|
||||
@ -119,7 +124,18 @@ export const HomeNavigator: React.FC = () => {
|
||||
/>
|
||||
)}
|
||||
{['Landing'].includes(route.name) && historyKey?.includes('messages-') && (
|
||||
<Appbar.Action icon='check-all' isLeading onPress={() => onPressCheckAll()} />
|
||||
<Appbar.Action
|
||||
icon='check-all'
|
||||
isLeading
|
||||
onPress={() => onMesssagesPressCheckAll()}
|
||||
/>
|
||||
)}
|
||||
{['Landing'].includes(route.name) && historyKey?.includes('groups-') && (
|
||||
<Appbar.Action
|
||||
icon='check-all'
|
||||
isLeading
|
||||
onPress={() => onGroupsPressCheckAll()}
|
||||
/>
|
||||
)}
|
||||
{['Group'].includes(route.name) && (
|
||||
<GroupHeaderIcon groupId={route.params?.groupId} />
|
||||
|
@ -32,7 +32,9 @@ import { handleInfinityScroll } from '../../Functions/NativeFunctions'
|
||||
import NostrosAvatar from '../../Components/NostrosAvatar'
|
||||
import UploadImage from '../../Components/UploadImage'
|
||||
import {
|
||||
getGroup,
|
||||
getGroupMessages,
|
||||
Group,
|
||||
GroupMessage,
|
||||
updateGroupRead,
|
||||
} from '../../Functions/DatabaseFunctions/Groups'
|
||||
@ -53,6 +55,7 @@ export const GroupPage: React.FC<GroupPageProps> = ({ route }) => {
|
||||
const { relayPool, lastEventId } = useContext(RelayPoolContext)
|
||||
const { publicKey, privateKey, name, picture, validNip05 } = useContext(UserContext)
|
||||
const [pageSize, setPageSize] = useState<number>(initialPageSize)
|
||||
const [group, setGroup] = useState<Group>()
|
||||
const [groupMessages, setGroupMessages] = useState<GroupMessage[]>([])
|
||||
const [sendingMessages, setSendingMessages] = useState<GroupMessage[]>([])
|
||||
const [reply, setReply] = useState<GroupMessage>()
|
||||
@ -82,6 +85,7 @@ 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)
|
||||
getGroupMessages(database, route.params.groupId, {
|
||||
order: 'DESC',
|
||||
@ -294,6 +298,11 @@ export const GroupPage: React.FC<GroupPageProps> = ({ route }) => {
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.cardContentDate}>
|
||||
{item.pubkey === group?.pubkey && (
|
||||
<View style={[styles.warning, { backgroundColor: '#683D00' }]}>
|
||||
<Text style={{ color: '#FFDCBB' }}>{t('groupPage.admin')}</Text>
|
||||
</View>
|
||||
)}
|
||||
{message?.pending && (
|
||||
<View style={styles.cardContentPending}>
|
||||
<MaterialCommunityIcons
|
||||
@ -589,6 +598,12 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
paddingLeft: 16,
|
||||
},
|
||||
warning: {
|
||||
borderRadius: 4,
|
||||
paddingLeft: 5,
|
||||
paddingRight: 5,
|
||||
marginRight: 5,
|
||||
},
|
||||
input: {
|
||||
flexDirection: 'column-reverse',
|
||||
marginTop: 16,
|
||||
|
@ -96,11 +96,10 @@ export const GroupsFeed: React.FC = () => {
|
||||
results.forEach((group) => {
|
||||
if (group.id) {
|
||||
getGroupMessagesMentionsCount(database, group.id).then((count) => {
|
||||
if (count > 0)
|
||||
setNewMentions((prev) => {
|
||||
if (group.id) prev[group.id] = count
|
||||
return prev
|
||||
})
|
||||
setNewMentions((prev) => {
|
||||
if (group.id) prev[group.id] = count
|
||||
return prev
|
||||
})
|
||||
})
|
||||
getGroupMessagesCount(database, group.id).then((count) => {
|
||||
setNewMessage((prev) => {
|
||||
|
@ -58,7 +58,7 @@ export const ProfileLoadPage: React.FC = () => {
|
||||
if (publicKey && relayPoolReady) {
|
||||
relayPool?.subscribe('profile-load-meta', [
|
||||
{
|
||||
kinds: [Kind.Contacts, Kind.Metadata],
|
||||
kinds: [Kind.Contacts, Kind.Metadata, Kind.ChannelCreation, Kind.ChannelMetadata, 1002],
|
||||
authors: [publicKey],
|
||||
},
|
||||
])
|
||||
|
@ -2,7 +2,7 @@ import React, { useContext, useState } from 'react'
|
||||
import { FlatList, ListRenderItem, ScrollView, StyleSheet, View } from 'react-native'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RelayPoolContext } from '../../Contexts/RelayPoolContext'
|
||||
import { getRelays, Relay } from '../../Functions/DatabaseFunctions/Relays'
|
||||
import { Relay } from '../../Functions/DatabaseFunctions/Relays'
|
||||
import { REGEX_SOCKET_LINK } from '../../Constants/Relay'
|
||||
import {
|
||||
List,
|
||||
@ -20,48 +20,38 @@ import RBSheet from 'react-native-raw-bottom-sheet'
|
||||
import { relayToColor } from '../../Functions/NativeFunctions'
|
||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons'
|
||||
import { useFocusEffect } from '@react-navigation/native'
|
||||
import { AppContext } from '../../Contexts/AppContext'
|
||||
|
||||
export const defaultRelays = [
|
||||
'wss://brb.io',
|
||||
'wss://damus.io',
|
||||
'wss://nostr-pub.wellorder.net',
|
||||
'wss://nostr.swiss-enigma.ch',
|
||||
'wss://nostr.onsats.org',
|
||||
'wss://nostr-pub.semisol.dev',
|
||||
'wss://nostr.openchain.fr',
|
||||
'wss://relay.nostr.info',
|
||||
'wss://nostr.oxtr.dev',
|
||||
'wss://nostr.ono.re',
|
||||
'wss://relay.grunch.dev',
|
||||
'wss://nostr.developer.li',
|
||||
]
|
||||
import { UserContext } from '../../Contexts/UserContext'
|
||||
|
||||
export const RelaysPage: React.FC = () => {
|
||||
const defaultRelayInput = React.useMemo(() => 'wss://', [])
|
||||
const { updateRelayItem, addRelayItem, relayPool, setDisplayrelayDrawer } =
|
||||
const { updateRelayItem, addRelayItem, relayPool, setDisplayrelayDrawer, relays } =
|
||||
useContext(RelayPoolContext)
|
||||
const { database } = useContext(AppContext)
|
||||
const { publicKey } = useContext(UserContext)
|
||||
const { t } = useTranslation('common')
|
||||
const theme = useTheme()
|
||||
const bottomSheetAddRef = React.useRef<RBSheet>(null)
|
||||
const bottomSheetResilenseRef = React.useRef<RBSheet>(null)
|
||||
const [relays, setRelays] = React.useState<Relay[]>([])
|
||||
const [addRelayInput, setAddRelayInput] = useState<string>(defaultRelayInput)
|
||||
const [showNotification, setShowNotification] = useState<string>()
|
||||
|
||||
useFocusEffect(
|
||||
React.useCallback(() => {
|
||||
relayPool?.unsubscribeAll()
|
||||
updateRelays()
|
||||
if (publicKey) {
|
||||
relayPool?.subscribe('relays', [
|
||||
{
|
||||
kinds: [1002],
|
||||
authors: [publicKey],
|
||||
limit: 1,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
return () => {}
|
||||
return () => relayPool?.unsubscribe(['relays'])
|
||||
}, []),
|
||||
)
|
||||
|
||||
const updateRelays: () => void = () => {
|
||||
if (database) getRelays(database).then(setRelays)
|
||||
}
|
||||
React.useEffect(() => {}, [relays])
|
||||
|
||||
const addRelay: (url: string) => void = (url) => {
|
||||
if (!relayList.find((relay) => relay.url === url)) {
|
||||
@ -70,7 +60,6 @@ export const RelaysPage: React.FC = () => {
|
||||
active: 1,
|
||||
global_feed: 1,
|
||||
}).then(() => {
|
||||
updateRelays()
|
||||
setShowNotification('add')
|
||||
})
|
||||
}
|
||||
@ -79,7 +68,6 @@ export const RelaysPage: React.FC = () => {
|
||||
const activeRelay: (relay: Relay) => void = (relay) => {
|
||||
relay.active = 1
|
||||
updateRelayItem(relay).then(() => {
|
||||
updateRelays()
|
||||
setShowNotification('active')
|
||||
})
|
||||
}
|
||||
@ -88,7 +76,6 @@ export const RelaysPage: React.FC = () => {
|
||||
relay.active = 0
|
||||
relay.global_feed = 0
|
||||
updateRelayItem(relay).then(() => {
|
||||
updateRelays()
|
||||
setShowNotification('desactive')
|
||||
})
|
||||
}
|
||||
@ -97,7 +84,6 @@ export const RelaysPage: React.FC = () => {
|
||||
relay.active = 1
|
||||
relay.global_feed = 1
|
||||
updateRelayItem(relay).then(() => {
|
||||
updateRelays()
|
||||
setShowNotification('globalFeedActive')
|
||||
})
|
||||
}
|
||||
@ -105,7 +91,6 @@ export const RelaysPage: React.FC = () => {
|
||||
const desactiveGlobalFeedRelay: (relay: Relay) => void = (relay) => {
|
||||
relay.global_feed = 0
|
||||
updateRelayItem(relay).then(() => {
|
||||
updateRelays()
|
||||
setShowNotification('globalFeedActiveUnactive')
|
||||
})
|
||||
}
|
||||
@ -145,8 +130,6 @@ export const RelaysPage: React.FC = () => {
|
||||
return 0
|
||||
})
|
||||
|
||||
const myRelays = relayList.filter((relay) => !defaultRelays.includes(relay.url))
|
||||
|
||||
const renderItem: ListRenderItem<Relay> = ({ item, index }) => {
|
||||
return (
|
||||
<List.Item
|
||||
@ -200,7 +183,7 @@ export const RelaysPage: React.FC = () => {
|
||||
right={() => (
|
||||
<>
|
||||
{type === 'centralized' && (
|
||||
<Text style={[styles.smallRelay, { color: theme.colors.errorContainer }]}>
|
||||
<Text style={[styles.smallRelay, { color: theme.colors.error }]}>
|
||||
{relayPool?.resilientAssignation.centralizedRelays[item] &&
|
||||
t('relaysPage.centralized')}
|
||||
</Text>
|
||||
@ -258,33 +241,9 @@ export const RelaysPage: React.FC = () => {
|
||||
data={Object.keys(relayPool?.resilientAssignation.smallRelays ?? {})}
|
||||
renderItem={(data) => renderResilienceItem(data.item, data.index, 'small')}
|
||||
/>
|
||||
{myRelays.length > 0 && (
|
||||
<>
|
||||
<View style={styles.titleWrapper}>
|
||||
<Text style={styles.title} variant='titleMedium'>
|
||||
{t('relaysPage.myList')}
|
||||
</Text>
|
||||
<Divider />
|
||||
</View>
|
||||
<List.Item
|
||||
title={t('relaysPage.relayName')}
|
||||
right={() => (
|
||||
<>
|
||||
<Text style={styles.listHeader}>{t('relaysPage.globalFeed')}</Text>
|
||||
<Text style={styles.listHeader}>{t('relaysPage.active')}</Text>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<FlatList
|
||||
showsVerticalScrollIndicator={false}
|
||||
data={myRelays}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<View style={styles.titleWrapper}>
|
||||
<Text style={styles.title} variant='titleMedium'>
|
||||
{t('relaysPage.recommended')}
|
||||
{t('relaysPage.myList')}
|
||||
</Text>
|
||||
<Divider />
|
||||
</View>
|
||||
@ -297,17 +256,7 @@ export const RelaysPage: React.FC = () => {
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<FlatList
|
||||
showsVerticalScrollIndicator={false}
|
||||
data={defaultRelays.map(
|
||||
(url) =>
|
||||
relays.find((relay) => relay.url === url && relay.active && relay.active > 0) ?? {
|
||||
url,
|
||||
},
|
||||
)}
|
||||
renderItem={renderItem}
|
||||
style={styles.list}
|
||||
/>
|
||||
<FlatList showsVerticalScrollIndicator={false} data={relays} renderItem={renderItem} />
|
||||
</ScrollView>
|
||||
<AnimatedFAB
|
||||
style={styles.fab}
|
||||
|
Loading…
Reference in New Issue
Block a user