blocked users (#281)

This commit is contained in:
KoalaSat 2023-02-08 16:19:36 +00:00 committed by GitHub
commit e321c66725
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 141 additions and 25 deletions

View File

@ -84,6 +84,18 @@ export const getFollowersCount: (db: QuickSQLiteConnection) => Promise<number> =
return item['COUNT(*)'] ?? 0
}
export const getBlocked: (db: QuickSQLiteConnection) => Promise<User[]> = async (db) => {
const userQuery = 'SELECT * FROM nostros_users WHERE blocked = 1 ORDER BY created_at DESC'
const resultSet = db.execute(userQuery)
if (resultSet.rows && resultSet.rows.length > 0) {
const items: object[] = getItems(resultSet)
const users: User[] = items.map((object) => databaseToEntity(object))
return users
} else {
return []
}
}
export const getFollowersAndFollowing: (db: QuickSQLiteConnection) => Promise<User[]> = async (
db,
) => {

View File

@ -131,7 +131,8 @@
"keyCopied": "Öffentlichen Schlüssel kopiert",
"contactAdded": "Kontakt hinzugefügt",
"addContactError": "Ein Fehler ist aufgetreten.",
"contactRemoved": "Abo wurde entfernt"
"contactRemoved": "Abo wurde entfernt",
"contactUnblocked": "Profile unblocked."
},
"emptyTitleFollowing": "Du folgst niemandem",
"emptyDescriptionFollowing": "Folge anderen, und sieh hier was sie posten",
@ -145,6 +146,8 @@
"addContactTitle": "Kontakt hinzufügen",
"followers": "({{count}}) folgen mir",
"following": "Ich folge ({{count}})",
"blocked": "Blocked ({{count}})",
"unblock": "Unblock",
"stopFollowing": "Abo stoppen",
"follow": "Folgen"
},

View File

@ -131,7 +131,8 @@
"keyCopied": "Public key copied.",
"contactAdded": "Profile followed.",
"addContactError": "There was an error publishing your changes.",
"contactRemoved": "Profile unfollowed."
"contactRemoved": "Profile unfollowed.",
"contactUnblocked": "Profile unblocked."
},
"emptyTitleFollowing": "You are not following anyone.",
"emptyDescriptionFollowing": "Follow other profiles to see content.",
@ -146,7 +147,9 @@
"followers": "Followers ({{count}})",
"following": "Following ({{count}})",
"stopFollowing": "Stop Following",
"follow": "Follow"
"follow": "Follow",
"blocked": "Blocked ({{count}})",
"unblock": "Unblock"
},
"aboutPage": {
"gitHub": "GitHub",

View File

@ -131,7 +131,8 @@
"keyCopied": "Clave pública copiada.",
"contactAdded": "Perfil seguido.",
"addContactError": "Se ha producido un error al publicar tus cambios.",
"contactRemoved": "Has dejado de seguir a un perfil"
"contactRemoved": "Has dejado de seguir a un perfil",
"contactUnblocked": "Perfil desbloqueado."
},
"emptyTitleFollowing": "No sigues a nadie",
"emptyDescriptionFollowing": "Sigue otros perfiles para ver contenido aquí.",
@ -145,6 +146,8 @@
"addContactTitle": "Añadir contacto",
"followers": "Seguidores ({{count}})",
"following": "Siguiendo ({{count}})",
"blocked": "Bloqueados ({{count}})",
"unblock": "Desbloquear",
"stopFollowing": "Dejar de seguir",
"follow": "Seguir"
},
@ -188,9 +191,9 @@
},
"relaysPage": {
"resilienceTitle": "Resilience (experimental)",
"resilienceDescription": "The nostr protocol provides a good amount of tools to build a desentralized network, but unlike other desentralized protocols such as Bitcoin, it's not inherent by defaul on his usage.\n\nTo ahcieve such goal, clients and relays should activally colaborate. Nostros resiliense mode tries to, first, expose these concerns to their users, and secondly, start experimening with new pattern and way to help on network' resilency.\n\nFunctional implementation will soon endup appearing on Nostros by default.",
"resilienceDescription": "The Nostr protocol provides a good number of tools to build a decentralized network, but unlike other decentralized protocols such as Bitcoin, it is not inherently bound to its use. To achieve the goal of a decentralized network, clients (e.g., Nostros) and relays (content servers) should actively cooperate. The Nostros resilience mode seeks first to raise awareness of this among users, and second to test new patterns and methods to improve the resilience of the network.\nA working implementation for this will soon be applied in Nostros.",
"resilienceCategories": "Resilience exposure",
"resilienceCategoriesDescription": "A first approach Nostros is testing is randomly calculate (based on received contats' events) what are the most resilient relays. It tries to avoid both relays with too much events (centralization) and relays with almost no events (battery draining).\n\nThe goal is to generate a list of 5 relays and cover all contacts. But if this is not possible, it will search among other realys, warning to the user about it.",
"resilienceCategoriesDescription": "An initial approach that Nostros is testing is to randomly calculate (based on received contact events) which relays are most balanced. This tries to avoid both relays with too many events (centralization) and relays with almost no events (battery discharge).\n\nThe goal is to create a list of 5 relays that cover all contacts. However, if this is not possible, Nostros will search among other relays and warn the user about it.",
"centralized": "Centralizado",
"small": "Pequeño",
"contacts": "Nº contactos",

View File

@ -132,7 +132,8 @@
"keyCopied": "Clé publique copiée.",
"contactAdded": "Profil suivi.",
"addContactError": "Une erreur s'est produite lors de vos modifications.",
"contactRemoved": "Vous ne suivez plus ce profil"
"contactRemoved": "Vous ne suivez plus ce profil",
"contactUnblocked": "Profile unblocked."
},
"emptyTitleFollowing": "Vous ne suivez personne",
"emptyDescriptionFollowing": "Suivez les autres profils pour voir le contenu ici.",
@ -146,6 +147,8 @@
"addContactTitle": "Ajouter un contact",
"followers": "Abonnés ({{count}})",
"following": "Abonnements ({{count}})",
"blocked": "Blocked ({{count}})",
"unblock": "Unblock",
"stopFollowing": "Cessez de suivre",
"follow": "Suivre"
},
@ -189,9 +192,9 @@
},
"relaysPage": {
"resilienceTitle": "Resilience (experimental)",
"resilienceDescription": "The nostr protocol provides a good amount of tools to build a desentralized network, but unlike other desentralized protocols such as Bitcoin, it's not inherent by defaul on his usage.\n\nTo ahcieve such goal, clients and relays should activally colaborate. Nostros resiliense mode tries to, first, expose these concerns to their users, and secondly, start experimening with new pattern and way to help on network' resilency.\n\nFunctional implementation will soon endup appearing on Nostros by default.",
"resilienceDescription": "The Nostr protocol provides a good number of tools to build a decentralized network, but unlike other decentralized protocols such as Bitcoin, it is not inherently bound to its use. To achieve the goal of a decentralized network, clients (e.g., Nostros) and relays (content servers) should actively cooperate. The Nostros resilience mode seeks first to raise awareness of this among users, and second to test new patterns and methods to improve the resilience of the network.\nA working implementation for this will soon be applied in Nostros.",
"resilienceCategories": "Resilience exposure",
"resilienceCategoriesDescription": "A first approach Nostros is testing is randomly calculate (based on received contats' events) what are the most resilient relays. It tries to avoid both relays with too much events (centralization) and relays with almost no events (battery draining).\n\nThe goal is to generate a list of 5 relays and cover all contacts. But if this is not possible, it will search among other realys, warning to the user about it.",
"resilienceCategoriesDescription": "An initial approach that Nostros is testing is to randomly calculate (based on received contact events) which relays are most balanced. This tries to avoid both relays with too many events (centralization) and relays with almost no events (battery discharge).\n\nThe goal is to create a list of 5 relays that cover all contacts. However, if this is not possible, Nostros will search among other relays and warn the user about it.",
"centralized": "Centralized",
"small": "Small",
"contacts": "# Contacts",

View File

@ -131,7 +131,8 @@
"keyCopied": "Публичный ключ скопирован.",
"contactAdded": "Вы подписались.",
"addContactError": "Произошла ошибка при публикации изменений.",
"contactRemoved": "Вы отписались."
"contactRemoved": "Вы отписались.",
"contactUnblocked": "Profile unblocked."
},
"emptyTitleFollowing": "Вы ни на кого не подписаны",
"emptyDescriptionFollowing": "Подпишитесь на другие профили, чтобы увидеть их заметки",
@ -145,6 +146,8 @@
"addContactTitle": "Add contact",
"followers": "Подписчики ({{count}})",
"following": "Подписки ({{count}})",
"blocked": "Blocked ({{count}})",
"unblock": "Unblock",
"stopFollowing": "Отписаться",
"follow": "Подписаться"
},
@ -188,9 +191,9 @@
},
"relaysPage": {
"resilienceTitle": "Resilience (experimental)",
"resilienceDescription": "The nostr protocol provides a good amount of tools to build a desentralized network, but unlike other desentralized protocols such as Bitcoin, it's not inherent by defaul on his usage.\n\nTo ahcieve such goal, clients and relays should activally colaborate. Nostros resiliense mode tries to, first, expose these concerns to their users, and secondly, start experimening with new pattern and way to help on network' resilency.\n\nFunctional implementation will soon endup appearing on Nostros by default.",
"resilienceDescription": "The Nostr protocol provides a good number of tools to build a decentralized network, but unlike other decentralized protocols such as Bitcoin, it is not inherently bound to its use. To achieve the goal of a decentralized network, clients (e.g., Nostros) and relays (content servers) should actively cooperate. The Nostros resilience mode seeks first to raise awareness of this among users, and second to test new patterns and methods to improve the resilience of the network.\nA working implementation for this will soon be applied in Nostros.",
"resilienceCategories": "Resilience exposure",
"resilienceCategoriesDescription": "A first approach Nostros is testing is randomly calculate (based on received contats' events) what are the most resilient relays. It tries to avoid both relays with too much events (centralization) and relays with almost no events (battery draining).\n\nThe goal is to generate a list of 5 relays and cover all contacts. But if this is not possible, it will search among other realys, warning to the user about it.",
"resilienceCategoriesDescription": "An initial approach that Nostros is testing is to randomly calculate (based on received contact events) which relays are most balanced. This tries to avoid both relays with too many events (centralization) and relays with almost no events (battery discharge).\n\nThe goal is to create a list of 5 relays that cover all contacts. However, if this is not possible, Nostros will search among other relays and warn the user about it.",
"centralized": "Centralized",
"small": "Small",
"contacts": "# Contacts",

View File

@ -129,7 +129,8 @@
"keyCopied": "已复制公钥",
"contactAdded": "已关注",
"addContactError": "发布更新时发生了错误",
"contactRemoved": "已取消关注"
"contactRemoved": "已取消关注",
"contactUnblocked": "Profile unblocked."
},
"emptyTitleFollowing": "您还没有关注任何用户",
"emptyDescriptionFollowing": "关注一些用户以查看内容",
@ -144,7 +145,9 @@
"followers": "关注者 ({{count}})",
"following": "关注中 ({{count}})",
"stopFollowing": "正在关注",
"follow": "回关"
"follow": "回关",
"blocked": "Blocked ({{count}})",
"unblock": "Unblock"
},
"aboutPage": {
"gitHub": "GitHub",

View File

@ -1,19 +1,14 @@
import React, { useContext, useEffect, useState } from 'react'
import {
ActivityIndicator,
Dimensions,
NativeScrollEvent,
NativeSyntheticEvent,
StyleSheet,
View,
} from 'react-native'
import { Dimensions, NativeScrollEvent, NativeSyntheticEvent, StyleSheet, View } from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import { AppContext } from '../../Contexts/AppContext'
import { Kind } from 'nostr-tools'
import { useTranslation } from 'react-i18next'
import { FlashList, ListRenderItem } from '@shopify/flash-list'
import {
getBlocked,
getFollowersAndFollowing,
updateUserBlock,
updateUserContact,
User,
} from '../../Functions/DatabaseFunctions/Users'
@ -51,6 +46,7 @@ export const ContactsFeed: React.FC = () => {
// State
const [followers, setFollowers] = useState<User[]>([])
const [following, setFollowing] = useState<User[]>([])
const [blocked, setBlocked] = useState<User[]>([])
const [contactInput, setContactInput] = useState<string>()
const [isAddingContact, setIsAddingContact] = useState<boolean>(false)
const [showNotification, setShowNotification] = useState<undefined | string>()
@ -72,6 +68,9 @@ export const ContactsFeed: React.FC = () => {
const loadUsers: () => void = () => {
if (database && publicKey) {
getBlocked(database).then((results) => {
if (results) setBlocked(results)
})
getFollowersAndFollowing(database).then((results) => {
const followers: User[] = []
const following: User[] = []
@ -169,6 +168,15 @@ export const ContactsFeed: React.FC = () => {
}
}
const unblock: (user: User) => void = (user) => {
if (relayPool && database && publicKey) {
updateUserBlock(user.id, database, false).then(() => {
setShowNotification('contactUnblocked')
loadUsers()
})
}
}
const renderContactItem: ListRenderItem<User> = ({ index, item }) => {
return (
<TouchableRipple
@ -199,6 +207,34 @@ export const ContactsFeed: React.FC = () => {
)
}
const renderBlockedItem: ListRenderItem<User> = ({ index, item }) => {
return (
<TouchableRipple
onPress={() => {
setDisplayUserDrawer(item.id)
bottomSheetProfileRef.current?.open()
}}
>
<View key={item.id} style={styles.contactRow}>
<View style={styles.profileData}>
<ProfileData
username={item?.name}
publicKey={getNpub(item.id)}
validNip05={item?.valid_nip05}
nip05={item?.nip05}
lud06={item?.lnurl}
picture={item?.picture}
avatarSize={40}
/>
</View>
<View>
<Button onPress={() => unblock(item)}>{t('contactsFeed.unblock')}</Button>
</View>
</View>
</TouchableRipple>
)
}
const bottomSheetStyles = React.useMemo(() => {
return {
container: {
@ -250,7 +286,6 @@ export const ContactsFeed: React.FC = () => {
onScroll={onScroll}
ListEmptyComponent={ListEmptyComponentFollowing}
horizontal={false}
ListFooterComponent={<ActivityIndicator animating={true} />}
/>
</View>
)
@ -293,7 +328,37 @@ export const ContactsFeed: React.FC = () => {
onScroll={onScroll}
ItemSeparatorComponent={Divider}
showsVerticalScrollIndicator={false}
ListFooterComponent={<ActivityIndicator animating={true} />}
/>
</View>
)
const ListEmptyComponentBlocked = (
<View style={styles.blankNoButton}>
<MaterialCommunityIcons
name='account-group-outline'
size={64}
style={styles.center}
color={theme.colors.onPrimaryContainer}
/>
<Text variant='headlineSmall' style={styles.center}>
{t('contactsFeed.emptyTitleBlocked')}
</Text>
<Text variant='bodyMedium' style={styles.center}>
{t('contactsFeed.emptyDescriptionBlocked')}
</Text>
</View>
)
const Blocked: JSX.Element = (
<View style={styles.container}>
<FlashList
estimatedItemSize={71}
showsVerticalScrollIndicator={false}
data={blocked.slice(0, pageSize)}
renderItem={renderBlockedItem}
onScroll={onScroll}
ListEmptyComponent={ListEmptyComponentBlocked}
horizontal={false}
/>
</View>
)
@ -301,6 +366,7 @@ export const ContactsFeed: React.FC = () => {
const renderScene: Record<string, JSX.Element> = {
following: Following,
followers: Followers,
blocked: Blocked,
}
return (
@ -334,6 +400,20 @@ export const ContactsFeed: React.FC = () => {
</Text>
</TouchableRipple>
</View>
<View
style={[
styles.tab,
tabKey === 'blocked'
? { ...styles.tabActive, borderBottomColor: theme.colors.primary }
: {},
]}
>
<TouchableRipple style={styles.textWrapper} onPress={() => setTabKey('blocked')}>
<Text style={styles.tabText}>
{t('contactsFeed.blocked', { count: blocked.length })}
</Text>
</TouchableRipple>
</View>
</View>
{renderScene[tabKey]}
{privateKey && (
@ -477,6 +557,12 @@ const styles = StyleSheet.create({
marginTop: 75,
padding: 16,
},
blankNoButton: {
justifyContent: 'space-between',
height: 192,
marginTop: 75,
padding: 16,
},
tabLabel: {
margin: 8,
},

View File

@ -238,7 +238,7 @@ export const SendPage: React.FC<SendPageProps> = ({ route }) => {
</View>
<View style={styles.actions}>
{userSuggestions.length > 0 ? (
<View style={styles.contactsList}>
<View style={[styles.contactsList, { backgroundColor: theme.colors.background }]}>
{userSuggestions.map((user, index) => renderContactItem(user, index))}
</View>
) : (