diff --git a/frontend/Functions/DatabaseFunctions/Users/index.ts b/frontend/Functions/DatabaseFunctions/Users/index.ts index 1af92c3..2e74a1f 100644 --- a/frontend/Functions/DatabaseFunctions/Users/index.ts +++ b/frontend/Functions/DatabaseFunctions/Users/index.ts @@ -84,6 +84,18 @@ export const getFollowersCount: (db: QuickSQLiteConnection) => Promise = return item['COUNT(*)'] ?? 0 } +export const getBlocked: (db: QuickSQLiteConnection) => Promise = 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 = async ( db, ) => { diff --git a/frontend/Locales/de.json b/frontend/Locales/de.json index 9b88705..23368c7 100644 --- a/frontend/Locales/de.json +++ b/frontend/Locales/de.json @@ -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" }, diff --git a/frontend/Locales/en.json b/frontend/Locales/en.json index fec21bd..1fb8b60 100644 --- a/frontend/Locales/en.json +++ b/frontend/Locales/en.json @@ -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", diff --git a/frontend/Locales/es.json b/frontend/Locales/es.json index e0896db..02f48c4 100644 --- a/frontend/Locales/es.json +++ b/frontend/Locales/es.json @@ -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", diff --git a/frontend/Locales/fr.json b/frontend/Locales/fr.json index be4cd52..07e6fb2 100644 --- a/frontend/Locales/fr.json +++ b/frontend/Locales/fr.json @@ -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", diff --git a/frontend/Locales/ru.json b/frontend/Locales/ru.json index bee6289..e806f39 100644 --- a/frontend/Locales/ru.json +++ b/frontend/Locales/ru.json @@ -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", diff --git a/frontend/Locales/zhCn.json b/frontend/Locales/zhCn.json index b964eb4..3b33795 100644 --- a/frontend/Locales/zhCn.json +++ b/frontend/Locales/zhCn.json @@ -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", diff --git a/frontend/Pages/ContactsFeed/index.tsx b/frontend/Pages/ContactsFeed/index.tsx index e91b25a..6e57d43 100644 --- a/frontend/Pages/ContactsFeed/index.tsx +++ b/frontend/Pages/ContactsFeed/index.tsx @@ -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([]) const [following, setFollowing] = useState([]) + const [blocked, setBlocked] = useState([]) const [contactInput, setContactInput] = useState() const [isAddingContact, setIsAddingContact] = useState(false) const [showNotification, setShowNotification] = useState() @@ -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 = ({ index, item }) => { return ( { ) } + const renderBlockedItem: ListRenderItem = ({ index, item }) => { + return ( + { + setDisplayUserDrawer(item.id) + bottomSheetProfileRef.current?.open() + }} + > + + + + + + + + + + ) + } + const bottomSheetStyles = React.useMemo(() => { return { container: { @@ -250,7 +286,6 @@ export const ContactsFeed: React.FC = () => { onScroll={onScroll} ListEmptyComponent={ListEmptyComponentFollowing} horizontal={false} - ListFooterComponent={} /> ) @@ -293,7 +328,37 @@ export const ContactsFeed: React.FC = () => { onScroll={onScroll} ItemSeparatorComponent={Divider} showsVerticalScrollIndicator={false} - ListFooterComponent={} + /> + + ) + + const ListEmptyComponentBlocked = ( + + + + {t('contactsFeed.emptyTitleBlocked')} + + + {t('contactsFeed.emptyDescriptionBlocked')} + + + ) + + const Blocked: JSX.Element = ( + + ) @@ -301,6 +366,7 @@ export const ContactsFeed: React.FC = () => { const renderScene: Record = { following: Following, followers: Followers, + blocked: Blocked, } return ( @@ -334,6 +400,20 @@ export const ContactsFeed: React.FC = () => { + + setTabKey('blocked')}> + + {t('contactsFeed.blocked', { count: blocked.length })} + + + {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, }, diff --git a/frontend/Pages/SendPage/index.tsx b/frontend/Pages/SendPage/index.tsx index 7289d0d..8bf9996 100644 --- a/frontend/Pages/SendPage/index.tsx +++ b/frontend/Pages/SendPage/index.tsx @@ -238,7 +238,7 @@ export const SendPage: React.FC = ({ route }) => { {userSuggestions.length > 0 ? ( - + {userSuggestions.map((user, index) => renderContactItem(user, index))} ) : (