From 9751b6a5539c6c46cf6d6821482082f03d2fe11d Mon Sep 17 00:00:00 2001 From: KoalaSat Date: Wed, 1 Mar 2023 16:53:54 +0100 Subject: [PATCH] Move updates to Java --- .../main/java/com/nostros/NostrosPackage.java | 6 +- .../java/com/nostros/classes/Database.java | 269 ++++++++++++++ .../main/java/com/nostros/classes/Relay.java | 6 +- .../java/com/nostros/classes/Websocket.java | 6 +- .../com/nostros/modules/DatabaseModule.java | 331 +++++------------- .../com/nostros/modules/RelayPoolModule.java | 10 +- frontend/Components/GroupHeaderIcon/index.tsx | 5 +- frontend/Components/ProfileActions/index.tsx | 18 +- frontend/Components/TextContent/index.tsx | 9 +- frontend/Components/UploadImage/index.tsx | 4 +- .../DatabaseFunctions/DirectMessages/index.ts | 17 +- .../DatabaseFunctions/Groups/index.ts | 38 -- .../DatabaseFunctions/Users/index.ts | 33 -- frontend/Pages/ContactsPage/index.tsx | 34 +- frontend/Pages/ConversationPage/index.tsx | 10 +- frontend/Pages/FeedNavigator/index.tsx | 7 +- frontend/Pages/GroupPage/index.tsx | 10 +- frontend/Pages/GroupsFeed/index.tsx | 6 +- frontend/Pages/NotificationsFeed/index.tsx | 12 + frontend/lib/Native/DatabaseModule/index.ts | 16 + 20 files changed, 453 insertions(+), 394 deletions(-) create mode 100644 android/app/src/main/java/com/nostros/classes/Database.java create mode 100644 frontend/lib/Native/DatabaseModule/index.ts diff --git a/android/app/src/main/java/com/nostros/NostrosPackage.java b/android/app/src/main/java/com/nostros/NostrosPackage.java index ee32677..e55c820 100644 --- a/android/app/src/main/java/com/nostros/NostrosPackage.java +++ b/android/app/src/main/java/com/nostros/NostrosPackage.java @@ -4,6 +4,8 @@ import com.facebook.react.ReactPackage; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; +import com.nostros.classes.Database; +import com.nostros.modules.DatabaseModule; import com.nostros.modules.RelayPoolModule; import java.util.ArrayList; @@ -21,7 +23,9 @@ public class NostrosPackage implements ReactPackage { ReactApplicationContext reactContext) { List modules = new ArrayList<>(); - modules.add(new RelayPoolModule(reactContext)); + Database database = new Database(reactContext.getFilesDir().getAbsolutePath()); + modules.add(new DatabaseModule(reactContext, database)); + modules.add(new RelayPoolModule(reactContext, database)); return modules; } diff --git a/android/app/src/main/java/com/nostros/classes/Database.java b/android/app/src/main/java/com/nostros/classes/Database.java new file mode 100644 index 0000000..14e08ea --- /dev/null +++ b/android/app/src/main/java/com/nostros/classes/Database.java @@ -0,0 +1,269 @@ +package com.nostros.classes; + +import android.annotation.SuppressLint; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactMethod; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class Database { + public SQLiteDatabase instance; + + public Database(String absoluteFilesPath) { + instance = SQLiteDatabase.openDatabase( absoluteFilesPath + "/nostros.sqlite", null, SQLiteDatabase.CREATE_IF_NECESSARY); + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_notes(\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" + + " main_event_id TEXT,\n" + + " reply_event_id TEXT\n" + + " );"); + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_users(\n" + + " id TEXT PRIMARY KEY NOT NULL,\n" + + " name TEXT,\n" + + " picture TEXT,\n" + + " about TEXT,\n" + + " main_relay TEXT,\n" + + " contact INT DEFAULT 0,\n" + + " follower INT DEFAULT 0\n" + + " );"); + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_relays(\n" + + " url TEXT PRIMARY KEY NOT NULL,\n" + + " pet INTEGER\n" + + " );"); + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_direct_messages(\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" + + " conversation_id TEXT NOT NULL,\n" + + " read INT DEFAULT 0\n" + + " );"); + try { + instance.execSQL("ALTER TABLE nostros_notes ADD COLUMN user_mentioned INT DEFAULT 0;"); + instance.execSQL("ALTER TABLE nostros_notes ADD COLUMN seen INT DEFAULT 0;"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN lnurl TEXT;"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN created_at INT DEFAULT 0;"); + } catch (SQLException e) { } + try { + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_reactions(\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" + + " positive INT DEFAULT 1,\n" + + " reacted_event_id TEXT,\n" + + " reacted_user_id TEXT\n" + + " );"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN nip05 TEXT;"); + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN valid_nip05 INT DEFAULT 0;"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_notes ADD COLUMN repost_id TEXT;"); + instance.execSQL("ALTER TABLE nostros_relays ADD COLUMN active INT DEFAULT 1;"); + } catch (SQLException e) { } + try { + instance.execSQL("DROP TABLE IF EXISTS nostros_config;"); + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN blocked INT DEFAULT 0;"); + } catch (SQLException e) { } + try { + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_notes_relays(\n" + + " note_id TEXT NOT NULL,\n" + + " pubkey TEXT NOT NULL,\n" + + " relay_url INT NOT NULL,\n" + + " PRIMARY KEY (note_id, relay_url)\n" + + " );"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN pet_at INT;"); + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN follower_at INT;"); + instance.execSQL("ALTER TABLE nostros_relays ADD COLUMN global_feed INT DEFAULT 1;"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_relays ADD COLUMN resilient INT DEFAULT 0;"); + instance.execSQL("ALTER TABLE nostros_relays ADD COLUMN manual INT DEFAULT 1;"); + } catch (SQLException e) { } + try { + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_group_meta(\n" + + " id TEXT PRIMARY KEY NOT NULL, \n" + + " content TEXT,\n" + + " created_at INT,\n" + + " kind INT,\n" + + " pubkey TEXT,\n" + + " sig TEXT,\n" + + " tags TEXT,\n" + + " name TEXT,\n" + + " about TEXT,\n" + + " picture TEXT\n" + + " );"); + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_group_messages(\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" + + " group_id TEXT NOT NULL,\n" + + " hidden INT DEFAULT 0\n" + + " );"); + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN muted_groups INT DEFAULT 0;"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_group_meta ADD COLUMN deleted INT DEFAULT 0;"); + instance.execSQL("ALTER TABLE nostros_group_messages ADD COLUMN read INT DEFAULT 0;"); + instance.execSQL("ALTER TABLE nostros_group_messages ADD COLUMN user_mentioned INT DEFAULT 0;"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_relays ADD COLUMN updated_at INT DEFAULT 0;"); + instance.execSQL("ALTER TABLE nostros_relays ADD COLUMN mode TEXT;"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN ln_address TEXT;"); + instance.execSQL("UPDATE nostros_users SET ln_address=lnurl;"); + instance.execSQL("ALTER TABLE nostros_users ADD COLUMN zap_pubkey TEXT;"); + instance.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" + + " );"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_relays ADD COLUMN deleted_at INT DEFAULT 0;"); + } catch (SQLException e) { } + try { + instance.execSQL("DROP INDEX nostros_notes_notifications_index;"); + } catch (SQLException e) { } + try { + instance.execSQL("CREATE INDEX nostros_zaps_list_index ON nostros_zaps(zapper_user_id);"); + instance.execSQL("CREATE INDEX nostros_zaps_user_index ON nostros_zaps(zapper_user_id, zapped_event_id);"); + instance.execSQL("CREATE INDEX nostros_zaps_most_zapped_index ON nostros_zaps(zapped_user_id, created_at, contact);"); + + instance.execSQL("CREATE INDEX nostros_users_names_index ON nostros_users(id, name); "); + instance.execSQL("CREATE INDEX nostros_users_contacts_index ON nostros_users(id, contact); "); + instance.execSQL("CREATE INDEX nostros_users_blocked_index ON nostros_users(id, blocked); "); + instance.execSQL("CREATE INDEX nostros_users_muted_index ON nostros_users(id, muted_groups); "); + instance.execSQL("CREATE INDEX nostros_users_contact_index ON nostros_users(contact); "); + + instance.execSQL("CREATE INDEX nostros_notes_home_index ON nostros_notes(pubkey, created_at, main_event_id, repost_id); "); + instance.execSQL("CREATE INDEX nostros_notes_notifications_index ON nostros_notes(pubkey, user_mentioned, reply_event_id, created_at); "); + instance.execSQL("CREATE INDEX nostros_notes_reply_index ON nostros_notes(reply_event_id); "); + instance.execSQL("CREATE INDEX nostros_notes_list_index ON nostros_notes(pubkey, created_at); "); + instance.execSQL("CREATE INDEX nostros_notes_repost_count_index ON nostros_notes(pubkey, repost_id, created_at); "); + + instance.execSQL("CREATE INDEX nostros_group_messages_mentions_index ON nostros_group_messages(group_id, pubkey, created_at);"); + instance.execSQL("CREATE INDEX nostros_group_messages_group_index ON nostros_group_messages(group_id, created_at);"); + instance.execSQL("CREATE INDEX nostros_group_messages_feed_index ON nostros_group_messages(user_mentioned, read, group_id);"); + + instance.execSQL("CREATE INDEX nostros_notes_relays_notes_index ON nostros_notes_relays(note_id, relay_url);"); + instance.execSQL("CREATE INDEX nostros_notes_relays_users_index ON nostros_notes_relays(pubkey, relay_url);"); + + instance.execSQL("CREATE INDEX nostros_direct_messages_feed_index ON nostros_direct_messages(pubkey, created_at); "); + instance.execSQL("CREATE INDEX nostros_direct_messages_notification_index ON nostros_direct_messages(pubkey, read); "); + instance.execSQL("CREATE INDEX nostros_direct_messages_conversation_index ON nostros_direct_messages(created_at, conversation_id); "); + + // Previous + instance.execSQL("CREATE INDEX nostros_users_contact_follower_index ON nostros_users(contact, follower); "); + instance.execSQL("CREATE INDEX nostros_reactions_created_at_reacted_event_id_index ON nostros_reactions(created_at, reacted_event_id); "); + instance.execSQL("CREATE INDEX nostros_notes_pubkey_index ON nostros_notes(pubkey); "); + instance.execSQL("CREATE INDEX nostros_notes_main_event_id_index ON nostros_notes(main_event_id); "); + instance.execSQL("CREATE INDEX nostros_direct_messages_pubkey_index ON nostros_direct_messages(pubkey); "); + instance.execSQL("CREATE INDEX nostros_direct_messages_conversation_id_index ON nostros_direct_messages(conversation_id); "); + instance.execSQL("CREATE INDEX nostros_reactions_reacted_event_id_index ON nostros_reactions(reacted_event_id); "); + instance.execSQL("CREATE INDEX nostros_users_contact_index ON nostros_users(contact); "); + instance.execSQL("CREATE INDEX nostros_reactions_pubkey_index ON nostros_reactions(pubkey); "); + instance.execSQL("CREATE INDEX nostros_nostros_zaps_zapped_event_id_index ON nostros_zaps(zapped_event_id);"); + } catch (SQLException e) { } + try { + instance.execSQL("CREATE TABLE IF NOT EXISTS nostros_lists(\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" + + " );"); + instance.execSQL("CREATE INDEX nostros_nostros_list_index ON nostros_lists(kind, pubkey);"); + } catch (SQLException e) { } + try { + instance.execSQL("ALTER TABLE nostros_lists ADD COLUMN list_tag TEXT;"); + } catch (SQLException e) { } + } + + public void saveEvent(JSONObject data, String userPubKey, String relayUrl) throws JSONException { + Event event = new Event(data); + event.save(instance, userPubKey, relayUrl); + } + + public void saveRelay(Relay relay) { + relay.save(this); + } + + public void deleteRelay(String relayUrl) { + String whereClause = "url = ?"; + String[] whereArgs = new String[] { + relayUrl + }; + ContentValues values = new ContentValues(); + values.put("deleted_at", System.currentTimeMillis() / 1000L); + instance.update ("nostros_relays", values, whereClause, whereArgs); + } + + public List getRelays(ReactApplicationContext reactContext) { + List relayList = new ArrayList<>(); + String query = "SELECT url, active, global_feed FROM nostros_relays WHERE deleted_at = 0 AND active = 1;"; + @SuppressLint("Recycle") Cursor cursor = instance.rawQuery(query, new String[] {}); + if (cursor.getCount() > 0) { + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + try { + String relayUrl = cursor.getString(0); + int active = cursor.getInt(1); + int globalFeed = cursor.getInt(2); + Relay relay = new Relay(relayUrl, active, globalFeed,this, reactContext); + relayList.add(relay); + } catch (IOException e) { + Log.d("WebSocket", e.toString()); + } + cursor.moveToNext(); + } + } + return relayList; + } +} diff --git a/android/app/src/main/java/com/nostros/classes/Relay.java b/android/app/src/main/java/com/nostros/classes/Relay.java index 9fb2e79..f259620 100644 --- a/android/app/src/main/java/com/nostros/classes/Relay.java +++ b/android/app/src/main/java/com/nostros/classes/Relay.java @@ -16,7 +16,7 @@ public class Relay { public int active; public int globalFeed; - public Relay(String serverUrl, int isActive, int showGlobalFeed, DatabaseModule database, ReactApplicationContext reactContext) throws IOException { + public Relay(String serverUrl, int isActive, int showGlobalFeed, Database database, ReactApplicationContext reactContext) throws IOException { webSocket = new Websocket(serverUrl, database, reactContext); url = serverUrl; active = isActive; @@ -47,12 +47,12 @@ public class Relay { webSocket.connect(userPubKey); } - public void save(SQLiteDatabase database) { + public void save(Database database) { ContentValues values = new ContentValues(); values.put("url", url); values.put("active", active); values.put("global_feed", globalFeed); values.put("deleted_at", 0); - database.replace("nostros_relays", null, values); + database.instance.replace("nostros_relays", null, values); } } diff --git a/android/app/src/main/java/com/nostros/classes/Websocket.java b/android/app/src/main/java/com/nostros/classes/Websocket.java index 25dc35b..6461e7c 100644 --- a/android/app/src/main/java/com/nostros/classes/Websocket.java +++ b/android/app/src/main/java/com/nostros/classes/Websocket.java @@ -19,13 +19,13 @@ import java.io.IOException; public class Websocket { private WebSocket webSocket; - private DatabaseModule database; + private Database database; private String url; private String pubKey; private ReactApplicationContext context; - public Websocket(String serverUrl, DatabaseModule databaseModule, ReactApplicationContext reactContext) { - database = databaseModule; + public Websocket(String serverUrl, Database databaseEntity, ReactApplicationContext reactContext) { + database = databaseEntity; url = serverUrl; context = reactContext; } diff --git a/android/app/src/main/java/com/nostros/modules/DatabaseModule.java b/android/app/src/main/java/com/nostros/modules/DatabaseModule.java index 210a59e..6441fe0 100644 --- a/android/app/src/main/java/com/nostros/modules/DatabaseModule.java +++ b/android/app/src/main/java/com/nostros/modules/DatabaseModule.java @@ -7,8 +7,13 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.util.Log; +import androidx.annotation.NonNull; + import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.bridge.ReactContextBaseJavaModule; +import com.facebook.react.bridge.ReactMethod; +import com.nostros.classes.Database; import com.nostros.classes.Event; import com.nostros.classes.Relay; @@ -19,253 +24,103 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -public class DatabaseModule { - public SQLiteDatabase database; +public class DatabaseModule extends ReactContextBaseJavaModule { + private Database database; + private ReactApplicationContext context; - DatabaseModule(String absoluteFilesPath) { - database = SQLiteDatabase.openDatabase( absoluteFilesPath + "/nostros.sqlite", null, SQLiteDatabase.CREATE_IF_NECESSARY); - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_notes(\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" + - " main_event_id TEXT,\n" + - " reply_event_id TEXT\n" + - " );"); - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_users(\n" + - " id TEXT PRIMARY KEY NOT NULL,\n" + - " name TEXT,\n" + - " picture TEXT,\n" + - " about TEXT,\n" + - " main_relay TEXT,\n" + - " contact INT DEFAULT 0,\n" + - " follower INT DEFAULT 0\n" + - " );"); - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_relays(\n" + - " url TEXT PRIMARY KEY NOT NULL,\n" + - " pet INTEGER\n" + - " );"); - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_direct_messages(\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" + - " conversation_id TEXT NOT NULL,\n" + - " read INT DEFAULT 0\n" + - " );"); - try { - database.execSQL("ALTER TABLE nostros_notes ADD COLUMN user_mentioned INT DEFAULT 0;"); - database.execSQL("ALTER TABLE nostros_notes ADD COLUMN seen INT DEFAULT 0;"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_users ADD COLUMN lnurl TEXT;"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_users ADD COLUMN created_at INT DEFAULT 0;"); - } catch (SQLException e) { } - try { - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_reactions(\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" + - " positive INT DEFAULT 1,\n" + - " reacted_event_id TEXT,\n" + - " reacted_user_id TEXT\n" + - " );"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_users ADD COLUMN nip05 TEXT;"); - database.execSQL("ALTER TABLE nostros_users ADD COLUMN valid_nip05 INT DEFAULT 0;"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_notes ADD COLUMN repost_id TEXT;"); - database.execSQL("ALTER TABLE nostros_relays ADD COLUMN active INT DEFAULT 1;"); - } catch (SQLException e) { } - try { - database.execSQL("DROP TABLE IF EXISTS nostros_config;"); - database.execSQL("ALTER TABLE nostros_users ADD COLUMN blocked INT DEFAULT 0;"); - } catch (SQLException e) { } - try { - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_notes_relays(\n" + - " note_id TEXT NOT NULL,\n" + - " pubkey TEXT NOT NULL,\n" + - " relay_url INT NOT NULL,\n" + - " PRIMARY KEY (note_id, relay_url)\n" + - " );"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_users ADD COLUMN pet_at INT;"); - database.execSQL("ALTER TABLE nostros_users ADD COLUMN follower_at INT;"); - database.execSQL("ALTER TABLE nostros_relays ADD COLUMN global_feed INT DEFAULT 1;"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_relays ADD COLUMN resilient INT DEFAULT 0;"); - database.execSQL("ALTER TABLE nostros_relays ADD COLUMN manual INT DEFAULT 1;"); - } catch (SQLException e) { } - try { - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_group_meta(\n" + - " id TEXT PRIMARY KEY NOT NULL, \n" + - " content TEXT,\n" + - " created_at INT,\n" + - " kind INT,\n" + - " pubkey TEXT,\n" + - " sig TEXT,\n" + - " tags TEXT,\n" + - " name TEXT,\n" + - " about TEXT,\n" + - " picture TEXT\n" + - " );"); - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_group_messages(\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" + - " group_id TEXT NOT NULL,\n" + - " hidden INT DEFAULT 0\n" + - " );"); - database.execSQL("ALTER TABLE nostros_users ADD COLUMN muted_groups INT DEFAULT 0;"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_group_meta ADD COLUMN deleted INT DEFAULT 0;"); - 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 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" + - " );"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_relays ADD COLUMN deleted_at INT DEFAULT 0;"); - } catch (SQLException e) { } - try { - database.execSQL("DROP INDEX nostros_notes_notifications_index;"); - } catch (SQLException e) { } - try { - database.execSQL("CREATE INDEX nostros_zaps_list_index ON nostros_zaps(zapper_user_id);"); - database.execSQL("CREATE INDEX nostros_zaps_user_index ON nostros_zaps(zapper_user_id, zapped_event_id);"); - database.execSQL("CREATE INDEX nostros_zaps_most_zapped_index ON nostros_zaps(zapped_user_id, created_at, contact);"); - - database.execSQL("CREATE INDEX nostros_users_names_index ON nostros_users(id, name); "); - database.execSQL("CREATE INDEX nostros_users_contacts_index ON nostros_users(id, contact); "); - database.execSQL("CREATE INDEX nostros_users_blocked_index ON nostros_users(id, blocked); "); - database.execSQL("CREATE INDEX nostros_users_muted_index ON nostros_users(id, muted_groups); "); - database.execSQL("CREATE INDEX nostros_users_contact_index ON nostros_users(contact); "); - - database.execSQL("CREATE INDEX nostros_notes_home_index ON nostros_notes(pubkey, created_at, main_event_id, repost_id); "); - database.execSQL("CREATE INDEX nostros_notes_notifications_index ON nostros_notes(pubkey, user_mentioned, reply_event_id, created_at); "); - database.execSQL("CREATE INDEX nostros_notes_reply_index ON nostros_notes(reply_event_id); "); - database.execSQL("CREATE INDEX nostros_notes_list_index ON nostros_notes(pubkey, created_at); "); - database.execSQL("CREATE INDEX nostros_notes_repost_count_index ON nostros_notes(pubkey, repost_id, created_at); "); - - database.execSQL("CREATE INDEX nostros_group_messages_mentions_index ON nostros_group_messages(group_id, pubkey, created_at);"); - database.execSQL("CREATE INDEX nostros_group_messages_group_index ON nostros_group_messages(group_id, created_at);"); - database.execSQL("CREATE INDEX nostros_group_messages_feed_index ON nostros_group_messages(user_mentioned, read, group_id);"); - - database.execSQL("CREATE INDEX nostros_notes_relays_notes_index ON nostros_notes_relays(note_id, relay_url);"); - database.execSQL("CREATE INDEX nostros_notes_relays_users_index ON nostros_notes_relays(pubkey, relay_url);"); - - database.execSQL("CREATE INDEX nostros_direct_messages_feed_index ON nostros_direct_messages(pubkey, created_at); "); - database.execSQL("CREATE INDEX nostros_direct_messages_notification_index ON nostros_direct_messages(pubkey, read); "); - database.execSQL("CREATE INDEX nostros_direct_messages_conversation_index ON nostros_direct_messages(created_at, conversation_id); "); - - // Previous - database.execSQL("CREATE INDEX nostros_users_contact_follower_index ON nostros_users(contact, follower); "); - database.execSQL("CREATE INDEX nostros_reactions_created_at_reacted_event_id_index ON nostros_reactions(created_at, reacted_event_id); "); - database.execSQL("CREATE INDEX nostros_notes_pubkey_index ON nostros_notes(pubkey); "); - database.execSQL("CREATE INDEX nostros_notes_main_event_id_index ON nostros_notes(main_event_id); "); - database.execSQL("CREATE INDEX nostros_direct_messages_pubkey_index ON nostros_direct_messages(pubkey); "); - database.execSQL("CREATE INDEX nostros_direct_messages_conversation_id_index ON nostros_direct_messages(conversation_id); "); - database.execSQL("CREATE INDEX nostros_reactions_reacted_event_id_index ON nostros_reactions(reacted_event_id); "); - database.execSQL("CREATE INDEX nostros_users_contact_index ON nostros_users(contact); "); - database.execSQL("CREATE INDEX nostros_reactions_pubkey_index ON nostros_reactions(pubkey); "); - database.execSQL("CREATE INDEX nostros_nostros_zaps_zapped_event_id_index ON nostros_zaps(zapped_event_id);"); - } catch (SQLException e) { } - try { - database.execSQL("CREATE TABLE IF NOT EXISTS nostros_lists(\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" + - " );"); - database.execSQL("CREATE INDEX nostros_nostros_list_index ON nostros_lists(kind, pubkey);"); - } catch (SQLException e) { } - try { - database.execSQL("ALTER TABLE nostros_lists ADD COLUMN list_tag TEXT;"); - } catch (SQLException e) { } + public DatabaseModule(ReactApplicationContext reactContext, Database databaseEntity) { + database = databaseEntity; + context = reactContext; } - public void saveEvent(JSONObject data, String userPubKey, String relayUrl) throws JSONException { - Event event = new Event(data); - event.save(database, userPubKey, relayUrl); + @Override + public String getName() { + return "DatabaseModule"; } - public void saveRelay(Relay relay) { - relay.save(database); - } - - public void deleteRelay(String relayUrl) { - String whereClause = "url = ?"; + @ReactMethod + public void updateConversationRead(String conversationId) { + String whereClause = "conversation_id = ?"; String[] whereArgs = new String[] { - relayUrl + conversationId }; ContentValues values = new ContentValues(); - values.put("deleted_at", System.currentTimeMillis() / 1000L); - database.update ("nostros_relays", values, whereClause, whereArgs); + values.put("read", 1); + database.instance.update("nostros_direct_messages", values, whereClause, whereArgs); } - public List getRelays(ReactApplicationContext reactContext) { - List relayList = new ArrayList<>(); - String query = "SELECT url, active, global_feed FROM nostros_relays WHERE deleted_at = 0 AND active = 1;"; - @SuppressLint("Recycle") Cursor cursor = database.rawQuery(query, new String[] {}); - if (cursor.getCount() > 0) { - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - try { - String relayUrl = cursor.getString(0); - int active = cursor.getInt(1); - int globalFeed = cursor.getInt(2); - Relay relay = new Relay(relayUrl, active, globalFeed,this, reactContext); - relayList.add(relay); - } catch (IOException e) { - Log.d("WebSocket", e.toString()); - } - cursor.moveToNext(); - } - } - return relayList; + @ReactMethod + public void updateAllDirectMessagesRead() { + String whereClause = ""; + String[] whereArgs = new String[] {}; + ContentValues values = new ContentValues(); + values.put("read", 1); + database.instance.update("nostros_direct_messages", values, whereClause, whereArgs); + } + + @ReactMethod + public void updateUserContact(String userId, boolean contact, Callback callback) { + String whereClause = "id = ?"; + String[] whereArgs = new String[] { userId }; + ContentValues values = new ContentValues(); + values.put("contact", contact ? 1 : 0); + database.instance.update("nostros_users", values, whereClause, whereArgs); + callback.invoke(); + } + + @ReactMethod + public void updateUserBlock(String userId, boolean blocked, Callback callback) { + String whereClause = "id = ?"; + String[] whereArgs = new String[] { userId }; + ContentValues values = new ContentValues(); + values.put("blocked", blocked ? 1 : 0); + database.instance.update("nostros_users", values, whereClause, whereArgs); + callback.invoke(); + } + + @ReactMethod + public void updateUserMutesGroups(String userId, boolean muted, Callback callback) { + String whereClause = "id = ?"; + String[] whereArgs = new String[] { userId }; + ContentValues values = new ContentValues(); + values.put("muted_groups", muted ? 1 : 0); + database.instance.update("nostros_users", values, whereClause, whereArgs); + callback.invoke(); + } + + @ReactMethod + public void updateAllGroupMessagesRead() { + String whereClause = ""; + String[] whereArgs = new String[] { }; + ContentValues values = new ContentValues(); + values.put("read", 1); + database.instance.update("nostros_group_messages", values, whereClause, whereArgs); + } + + @ReactMethod + public void updateGroupRead(String groupId) { + String whereClause = "id = ?"; + String[] whereArgs = new String[] { groupId }; + ContentValues values = new ContentValues(); + values.put("read", 1); + database.instance.update("nostros_group_messages", values, whereClause, whereArgs); + } + + @ReactMethod + public void deleteGroup(String groupId) { + String whereClause = "id = ?"; + String[] whereArgs = new String[] { groupId }; + ContentValues values = new ContentValues(); + values.put("deleted", 1); + database.instance.update("nostros_group_meta", values, whereClause, whereArgs); + } + + @ReactMethod + public void activateGroup(String groupId) { + String whereClause = "id = ?"; + String[] whereArgs = new String[] { groupId }; + ContentValues values = new ContentValues(); + values.put("deleted", 0); + database.instance.update("nostros_group_meta", values, whereClause, whereArgs); } } diff --git a/android/app/src/main/java/com/nostros/modules/RelayPoolModule.java b/android/app/src/main/java/com/nostros/modules/RelayPoolModule.java index b51eabe..6b18e02 100644 --- a/android/app/src/main/java/com/nostros/modules/RelayPoolModule.java +++ b/android/app/src/main/java/com/nostros/modules/RelayPoolModule.java @@ -1,5 +1,6 @@ package com.nostros.modules; +import android.database.sqlite.SQLiteDatabase; import android.util.Log; import com.facebook.react.bridge.Callback; @@ -7,6 +8,7 @@ import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; +import com.nostros.classes.Database; import com.nostros.classes.Relay; import java.io.IOException; @@ -16,11 +18,11 @@ import java.util.ListIterator; public class RelayPoolModule extends ReactContextBaseJavaModule { protected List relays; private String userPubKey; - private DatabaseModule database; + private Database database; private ReactApplicationContext context; - public RelayPoolModule(ReactApplicationContext reactContext) { - database = new DatabaseModule(reactContext.getFilesDir().getAbsolutePath()); + public RelayPoolModule(ReactApplicationContext reactContext, Database databaseEntity) { + database = databaseEntity; context = reactContext; } @@ -72,7 +74,7 @@ public class RelayPoolModule extends ReactContextBaseJavaModule { relay.connect(userPubKey); relay.setActive(active); relay.setGlobalFeed(globalFeed); - relay.save(database.database); + relay.save(database); this.relays.set(index, relay); relayExists = true; } diff --git a/frontend/Components/GroupHeaderIcon/index.tsx b/frontend/Components/GroupHeaderIcon/index.tsx index ad8b6f0..2a313ce 100644 --- a/frontend/Components/GroupHeaderIcon/index.tsx +++ b/frontend/Components/GroupHeaderIcon/index.tsx @@ -11,7 +11,7 @@ import { TouchableRipple, useTheme, } from 'react-native-paper' -import { deleteGroup, getGroup, Group } from '../../Functions/DatabaseFunctions/Groups' +import { getGroup, Group } from '../../Functions/DatabaseFunctions/Groups' import { AppContext } from '../../Contexts/AppContext' import { validImageUrl } from '../../Functions/NativeFunctions' import FastImage from 'react-native-fast-image' @@ -27,6 +27,7 @@ import { goBack } from '../../lib/Navigation' import { getUser, User } from '../../Functions/DatabaseFunctions/Users' import ProfileData from '../ProfileData' import GroupShare from '../GroupShare' +import DatabaseModule from '../../lib/Native/DatabaseModule' interface GroupHeaderIconProps { groupId: string @@ -71,7 +72,7 @@ export const GroupHeaderIcon: React.FC = ({ groupId }) => const onDeleteGroup: () => void = () => { if (database && group?.id) { - deleteGroup(database, group?.id) + DatabaseModule.deleteGroup(group?.id) bottomSheetActionsGroupRef.current?.close() goBack() } diff --git a/frontend/Components/ProfileActions/index.tsx b/frontend/Components/ProfileActions/index.tsx index c757964..92dc837 100644 --- a/frontend/Components/ProfileActions/index.tsx +++ b/frontend/Components/ProfileActions/index.tsx @@ -5,14 +5,7 @@ import { Button, IconButton, List, Snackbar, Text, useTheme } from 'react-native import { AppContext } from '../../Contexts/AppContext' import { RelayPoolContext } from '../../Contexts/RelayPoolContext' import { UserContext } from '../../Contexts/UserContext' -import { - addUser, - getUser, - updateUserBlock, - updateUserContact, - updateUserMutesGroups, - User, -} from '../../Functions/DatabaseFunctions/Users' +import { addUser, getUser, User } from '../../Functions/DatabaseFunctions/Users' import { populatePets, username } from '../../Functions/RelayFunctions/Users' import LnPayment from '../LnPayment' import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons' @@ -25,6 +18,7 @@ import ProfileShare from '../ProfileShare' import { ScrollView } from 'react-native-gesture-handler' import { Kind } from 'nostr-tools' import { getUnixTime } from 'date-fns' +import DatabaseModule from '../../lib/Native/DatabaseModule' interface ProfileActionsProps { user: User @@ -69,7 +63,7 @@ export const ProfileActions: React.FC = ({ }) .then(() => { if (database) { - updateUserMutesGroups(database, user.id, true).then(() => { + DatabaseModule.updateUserMutesGroups(user.id, true, () => { setIsMuted(true) bottomSheetMuteRef.current?.close() }) @@ -108,7 +102,7 @@ export const ProfileActions: React.FC = ({ const onChangeBlockUser: () => void = () => { if (database && publicKey) { addUser(user.id, database).then(() => { - updateUserBlock(user.id, database, !isBlocked).then(() => { + DatabaseModule.updateUserBlock(user.id, !isBlocked, () => { loadUser() setShowNotificationRelay(isBlocked ? 'userUnblocked' : 'userBlocked') }) @@ -118,7 +112,7 @@ export const ProfileActions: React.FC = ({ const removeContact: () => void = () => { if (relayPool && database && publicKey) { - updateUserContact(user.id, database, false).then(() => { + DatabaseModule.updateUserContact(user.id, false, () => { populatePets(relayPool, database, publicKey) setIsContact(false) setShowNotification('contactRemoved') @@ -128,7 +122,7 @@ export const ProfileActions: React.FC = ({ const addContact: () => void = () => { if (relayPool && database && publicKey) { - updateUserContact(user.id, database, true).then(() => { + DatabaseModule.updateUserContact(user.id, true, () => { populatePets(relayPool, database, publicKey) setIsContact(true) setShowNotification('contactAdded') diff --git a/frontend/Components/TextContent/index.tsx b/frontend/Components/TextContent/index.tsx index d122040..0e75169 100644 --- a/frontend/Components/TextContent/index.tsx +++ b/frontend/Components/TextContent/index.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useState } from 'react' import ParsedText from 'react-native-parsed-text' import { Event } from '../../lib/nostr/Events' -import { Linking, StyleSheet, View } from 'react-native' +import { Clipboard, Linking, StyleSheet, View } from 'react-native' import { AppContext } from '../../Contexts/AppContext' import { getUser, User } from '../../Functions/DatabaseFunctions/Users' import { formatPubKey } from '../../Functions/RelayFunctions/Users' @@ -21,6 +21,7 @@ interface TextContentProps { showPreview?: boolean onPressUser?: (user: User) => void numberOfLines?: number + copyText?: boolean } export const TextContent: React.FC = ({ @@ -29,6 +30,7 @@ export const TextContent: React.FC = ({ showPreview = true, onPressUser = () => {}, numberOfLines, + copyText = false, }) => { const theme = useTheme() const { t } = useTranslation('common') @@ -153,6 +155,10 @@ export const TextContent: React.FC = ({ return matchingString } + const onLongPress: () => void = () => { + if (copyText) Clipboard.setString(text) + } + const preview = React.useMemo(() => { if (!showPreview) return <> @@ -265,6 +271,7 @@ export const TextContent: React.FC = ({ ]} childrenProps={{ allowFontScaling: false }} numberOfLines={numberOfLines} + onLongPress={onLongPress} > {text} diff --git a/frontend/Components/UploadImage/index.tsx b/frontend/Components/UploadImage/index.tsx index 9b64d10..1a86ec9 100644 --- a/frontend/Components/UploadImage/index.tsx +++ b/frontend/Components/UploadImage/index.tsx @@ -13,7 +13,7 @@ interface UploadImageProps { setImageUri: (uri: string) => void uploadingFile: boolean setUploadingFile: (uploading: boolean) => void - onError: () => void + onError?: () => void } export const UploadImage: React.FC = ({ @@ -22,7 +22,7 @@ export const UploadImage: React.FC = ({ setImageUri, uploadingFile, setUploadingFile, - onError, + onError = () => {}, }) => { const { getImageHostingService } = useContext(AppContext) const theme = useTheme() diff --git a/frontend/Functions/DatabaseFunctions/DirectMessages/index.ts b/frontend/Functions/DatabaseFunctions/DirectMessages/index.ts index aa5f874..3250c32 100644 --- a/frontend/Functions/DatabaseFunctions/DirectMessages/index.ts +++ b/frontend/Functions/DatabaseFunctions/DirectMessages/index.ts @@ -1,4 +1,4 @@ -import { QueryResult, QuickSQLiteConnection } from 'react-native-quick-sqlite' +import { QuickSQLiteConnection } from 'react-native-quick-sqlite' import { getItems } from '..' import { Event, evetDatabaseToEntity } from '../../../lib/nostr/Events' @@ -29,21 +29,6 @@ export const getRawUserConversation: ( return notes } -export const updateConversationRead: ( - conversationId: string, - db: QuickSQLiteConnection, -) => Promise = async (conversationId, db) => { - const userQuery = `UPDATE nostros_direct_messages SET read = ? WHERE conversation_id = ?` - return db.execute(userQuery, [1, conversationId]) -} - -export const updateAllDirectMessagesRead: ( - db: QuickSQLiteConnection, -) => Promise = async (db) => { - const userQuery = `UPDATE nostros_direct_messages SET read = ?` - return db.execute(userQuery, [1]) -} - export const getDirectMessagesCount: ( db: QuickSQLiteConnection, pubKey: string, diff --git a/frontend/Functions/DatabaseFunctions/Groups/index.ts b/frontend/Functions/DatabaseFunctions/Groups/index.ts index 4ebd3ed..ce45feb 100644 --- a/frontend/Functions/DatabaseFunctions/Groups/index.ts +++ b/frontend/Functions/DatabaseFunctions/Groups/index.ts @@ -27,13 +27,6 @@ const databaseToGroupMessage: (object: any) => GroupMessage = (object = {}) => { return object as GroupMessage } -export const updateAllGroupMessagesRead: ( - db: QuickSQLiteConnection, -) => Promise = async (db) => { - const userQuery = `UPDATE nostros_group_messages SET read = ?` - return db.execute(userQuery, [1]) -} - export const getGroups: (db: QuickSQLiteConnection) => Promise = async (db) => { const groupsQuery = ` SELECT @@ -130,37 +123,6 @@ export const deleteGroupMessages: ( return db.execute(deleteQuery, [pubkey]) } -export const deleteGroup: ( - db: QuickSQLiteConnection, - groupId: string, -) => Promise = async (db, groupId) => { - const userQuery = `UPDATE nostros_group_meta SET deleted = 1 WHERE id = ?;` - return db.execute(userQuery, [groupId]) -} - -export const updateAllDirectMessagesRead: ( - db: QuickSQLiteConnection, -) => Promise = async (db) => { - const userQuery = `UPDATE nostros_group_messages SET read = ?` - return db.execute(userQuery, [1]) -} - -export const activateGroup: ( - db: QuickSQLiteConnection, - groupId: string, -) => Promise = async (db, groupId) => { - const userQuery = `UPDATE nostros_group_meta SET deleted = 0 WHERE id = ?;` - return db.execute(userQuery, [groupId]) -} - -export const updateGroupRead: ( - db: QuickSQLiteConnection, - groupId: string, -) => Promise = async (db, groupId) => { - const userQuery = `UPDATE nostros_group_messages SET read = ? WHERE group_id = ?` - return db.execute(userQuery, [1, groupId]) -} - export const getUserGroupMessagesCount: ( db: QuickSQLiteConnection, publicKey: string, diff --git a/frontend/Functions/DatabaseFunctions/Users/index.ts b/frontend/Functions/DatabaseFunctions/Users/index.ts index 5d8d295..851b5c6 100644 --- a/frontend/Functions/DatabaseFunctions/Users/index.ts +++ b/frontend/Functions/DatabaseFunctions/Users/index.ts @@ -23,39 +23,6 @@ const databaseToEntity: (object: object) => User = (object) => { return object as User } -export const updateUserContact: ( - userId: string, - db: QuickSQLiteConnection, - contact: boolean, -) => Promise = async (userId, db, contact) => { - const userQuery = `UPDATE nostros_users SET contact = ? WHERE id = ?` - - await addUser(userId, db) - return db.execute(userQuery, [contact ? 1 : 0, userId]) -} - -export const updateUserBlock: ( - userId: string, - db: QuickSQLiteConnection, - blocked: boolean, -) => Promise = async (userId, db, blocked) => { - const userQuery = `UPDATE nostros_users SET blocked = ? WHERE id = ?` - - await addUser(userId, db) - return db.execute(userQuery, [blocked ? 1 : 0, userId]) -} - -export const updateUserMutesGroups: ( - db: QuickSQLiteConnection, - userId: string, - muted: boolean, -) => Promise = 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 = async ( pubkey, db, diff --git a/frontend/Pages/ContactsPage/index.tsx b/frontend/Pages/ContactsPage/index.tsx index 703592b..09c6692 100644 --- a/frontend/Pages/ContactsPage/index.tsx +++ b/frontend/Pages/ContactsPage/index.tsx @@ -5,13 +5,7 @@ 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' +import { getBlocked, getFollowersAndFollowing, User } from '../../Functions/DatabaseFunctions/Users' import { RelayPoolContext } from '../../Contexts/RelayPoolContext' import { populatePets } from '../../Functions/RelayFunctions/Users' import { getNip19Key, getNpub } from '../../lib/nostr/Nip19' @@ -33,6 +27,7 @@ import ProfileData from '../../Components/ProfileData' import { handleInfinityScroll } from '../../Functions/NativeFunctions' import { queryProfile } from 'nostr-tools/nip05' import { navigate } from '../../lib/Navigation' +import DatabaseModule from '../../lib/Native/DatabaseModule' export const ContactsPage: React.FC = () => { const { t } = useTranslation('common') @@ -136,17 +131,12 @@ export const ContactsPage: React.FC = () => { } if (hexKey) { - updateUserContact(hexKey, database, true) - .then(() => { - populatePets(relayPool, database, publicKey) - loadUsers() - setIsAddingContact(false) - setShowNotification('contactAdded') - }) - .catch(() => { - setIsAddingContact(false) - setShowNotification('addContactError') - }) + DatabaseModule.updateUserContact(hexKey, true, () => { + populatePets(relayPool, database, publicKey) + loadUsers() + setIsAddingContact(false) + setShowNotification('contactAdded') + }) } else { setIsAddingContact(false) setShowNotification('addContactError') @@ -162,7 +152,7 @@ export const ContactsPage: React.FC = () => { const removeContact: (user: User) => void = (user) => { if (relayPool && database && publicKey) { - updateUserContact(user.id, database, false).then(() => { + DatabaseModule.updateUserContact(user.id, false, () => { populatePets(relayPool, database, publicKey) setShowNotification('contactRemoved') loadUsers() @@ -172,9 +162,9 @@ export const ContactsPage: React.FC = () => { const addContact: (user: User) => void = (user) => { if (relayPool && database && publicKey) { - updateUserContact(user.id, database, true).then(() => { + DatabaseModule.updateUserContact(user.id, true, () => { populatePets(relayPool, database, publicKey) - setShowNotification('contactAdded') + setShowNotification('contactRemoved') loadUsers() }) } @@ -182,7 +172,7 @@ export const ContactsPage: React.FC = () => { const unblock: (user: User) => void = (user) => { if (relayPool && database && publicKey) { - updateUserBlock(user.id, database, false).then(() => { + DatabaseModule.updateUserBlock(user.id, false, () => { setShowNotification('contactUnblocked') loadUsers() }) diff --git a/frontend/Pages/ConversationPage/index.tsx b/frontend/Pages/ConversationPage/index.tsx index 754d84e..58f3b59 100644 --- a/frontend/Pages/ConversationPage/index.tsx +++ b/frontend/Pages/ConversationPage/index.tsx @@ -12,11 +12,7 @@ import { import { AppContext } from '../../Contexts/AppContext' import { RelayPoolContext } from '../../Contexts/RelayPoolContext' import { Event } from '../../lib/nostr/Events' -import { - DirectMessage, - getDirectMessages, - updateConversationRead, -} from '../../Functions/DatabaseFunctions/DirectMessages' +import { DirectMessage, getDirectMessages } from '../../Functions/DatabaseFunctions/DirectMessages' import { getUser, User } from '../../Functions/DatabaseFunctions/Users' import { useTranslation } from 'react-i18next' import { username, usernamePubKey, usersToTags } from '../../Functions/RelayFunctions/Users' @@ -41,6 +37,7 @@ import NostrosAvatar from '../../Components/NostrosAvatar' import UploadImage from '../../Components/UploadImage' import { Swipeable } from 'react-native-gesture-handler' import { getETags } from '../../Functions/RelayFunctions/Events' +import DatabaseModule from '../../lib/Native/DatabaseModule' interface ConversationPageProps { route: { params: { pubKey: string; conversationId: string } } @@ -87,7 +84,7 @@ export const ConversationPage: React.FC = ({ route }) => const loadDirectMessages: (subscribe: boolean) => void = (subscribe) => { if (database && publicKey && privateKey) { const conversationId = route.params?.conversationId - updateConversationRead(conversationId, database) + DatabaseModule.updateConversationRead(conversationId) setRefreshBottomBarAt(getUnixTime(new Date())) getUser(otherPubKey, database).then((user) => { if (user) setOtherUser(user) @@ -254,6 +251,7 @@ export const ConversationPage: React.FC = ({ route }) => content={message?.content} event={message} onPressUser={(user) => setDisplayUserDrawer(user.id)} + copyText /> ) : ( {t('groupPage.replyText')} diff --git a/frontend/Pages/FeedNavigator/index.tsx b/frontend/Pages/FeedNavigator/index.tsx index 3efbd09..7581407 100644 --- a/frontend/Pages/FeedNavigator/index.tsx +++ b/frontend/Pages/FeedNavigator/index.tsx @@ -18,14 +18,13 @@ import ConfigPage from '../ConfigPage' import { RelayPoolContext } from '../../Contexts/RelayPoolContext' import { AppContext } from '../../Contexts/AppContext' import RelayCard from '../../Components/RelayCard' -import { updateAllDirectMessagesRead } from '../../Functions/DatabaseFunctions/DirectMessages' import { getUnixTime } from 'date-fns' import ContactsPage from '../ContactsPage' import GroupPage from '../GroupPage' import GroupHeaderIcon from '../../Components/GroupHeaderIcon' import NoteActions from '../../Components/NoteActions' -import { updateAllGroupMessagesRead } from '../../Functions/DatabaseFunctions/Groups' import QrReaderPage from '../QrReaderPage' +import DatabaseModule from '../../lib/Native/DatabaseModule' export const HomeNavigator: React.FC = () => { const theme = useTheme() @@ -71,12 +70,12 @@ export const HomeNavigator: React.FC = () => { } const onMesssagesPressCheckAll: () => void = () => { - if (database) updateAllDirectMessagesRead(database) + if (database) DatabaseModule.updateAllDirectMessagesRead() setRefreshBottomBarAt(getUnixTime(new Date())) } const onGroupsPressCheckAll: () => void = () => { - if (database) updateAllGroupMessagesRead(database) + if (database) DatabaseModule.updateAllGroupMessagesRead() setRefreshBottomBarAt(getUnixTime(new Date())) } diff --git a/frontend/Pages/GroupPage/index.tsx b/frontend/Pages/GroupPage/index.tsx index 5bb4c88..b9a98b8 100644 --- a/frontend/Pages/GroupPage/index.tsx +++ b/frontend/Pages/GroupPage/index.tsx @@ -31,16 +31,13 @@ import { Kind } from 'nostr-tools' import { formatDate, handleInfinityScroll } from '../../Functions/NativeFunctions' import NostrosAvatar from '../../Components/NostrosAvatar' import UploadImage from '../../Components/UploadImage' -import { - getGroupMessages, - GroupMessage, - updateGroupRead, -} from '../../Functions/DatabaseFunctions/Groups' +import { getGroupMessages, GroupMessage } from '../../Functions/DatabaseFunctions/Groups' import { RelayFilters } from '../../lib/nostr/RelayPool/intex' import { getUsers, User } from '../../Functions/DatabaseFunctions/Users' import ProfileData from '../../Components/ProfileData' import { ScrollView, Swipeable } from 'react-native-gesture-handler' import { getETags } from '../../Functions/RelayFunctions/Events' +import DatabaseModule from '../../lib/Native/DatabaseModule' interface GroupPageProps { route: { params: { groupId: string } } @@ -82,7 +79,7 @@ export const GroupPage: React.FC = ({ route }) => { const loadGroupMessages: (subscribe: boolean) => void = (subscribe) => { if (database && publicKey && route.params.groupId) { - updateGroupRead(database, route.params.groupId) + DatabaseModule.updateGroupRead(route.params.groupId) getGroupMessages(database, route.params.groupId, { order: 'DESC', limit: pageSize, @@ -312,6 +309,7 @@ export const GroupPage: React.FC = ({ route }) => { content={message?.content} event={message} onPressUser={(user) => setDisplayUserDrawer(user.id)} + copyText /> ) : ( {t('groupPage.replyText')} diff --git a/frontend/Pages/GroupsFeed/index.tsx b/frontend/Pages/GroupsFeed/index.tsx index 1388d83..70b9253 100644 --- a/frontend/Pages/GroupsFeed/index.tsx +++ b/frontend/Pages/GroupsFeed/index.tsx @@ -24,7 +24,6 @@ import { getUnixTime } from 'date-fns' import { useFocusEffect } from '@react-navigation/native' import { AppContext } from '../../Contexts/AppContext' import { - activateGroup, addGroup, getGroupMessagesCount, getGroupMessagesMentionsCount, @@ -38,6 +37,7 @@ import { FlashList, ListRenderItem } from '@shopify/flash-list' import { RelayFilters } from '../../lib/nostr/RelayPool/intex' import { validNip21 } from '../../Functions/NativeFunctions' import { getNip19Key } from '../../lib/nostr/Nip19' +import DatabaseModule from '../../lib/Native/DatabaseModule' export const GroupsFeed: React.FC = () => { const { t } = useTranslation('common') @@ -151,11 +151,11 @@ export const GroupsFeed: React.FC = () => { const key = getNip19Key(searchGroup) if (key) { addGroup(database, searchGroup, '', '').then(() => loadGroups()) - activateGroup(database, searchGroup) + DatabaseModule.activateGroup(searchGroup) } } else { addGroup(database, searchGroup, '', '').then(() => loadGroups()) - activateGroup(database, searchGroup) + DatabaseModule.activateGroup(searchGroup) } setSearchGroup(undefined) bottomSheetSearchRef.current?.close() diff --git a/frontend/Pages/NotificationsFeed/index.tsx b/frontend/Pages/NotificationsFeed/index.tsx index 18f4a41..ecbce63 100644 --- a/frontend/Pages/NotificationsFeed/index.tsx +++ b/frontend/Pages/NotificationsFeed/index.tsx @@ -46,6 +46,7 @@ export const NotificationsFeed: React.FC = () => { 'notification-feed', 'notification-replies', 'notification-reactions', + 'notification-reposts', ]) updateLastSeen() } @@ -119,6 +120,9 @@ export const NotificationsFeed: React.FC = () => { if (notes.length > 0) { const notedIds = notes.map((note) => note.id ?? '') const authors = notes.map((note) => note.pubkey ?? '') + const repostIds = notes + .filter((note) => note.repost_id) + .map((note) => note.repost_id ?? '') relayPool?.subscribe('notification-reactions', [ { @@ -130,6 +134,14 @@ export const NotificationsFeed: React.FC = () => { '#e': notedIds, }, ]) + if (repostIds.length > 0) { + relayPool?.subscribe('notification-reposts', [ + { + kinds: [Kind.Text], + ids: repostIds, + }, + ]) + } } }) } diff --git a/frontend/lib/Native/DatabaseModule/index.ts b/frontend/lib/Native/DatabaseModule/index.ts new file mode 100644 index 0000000..d0d296c --- /dev/null +++ b/frontend/lib/Native/DatabaseModule/index.ts @@ -0,0 +1,16 @@ +import { NativeModules } from 'react-native' +const { DatabaseModule } = NativeModules + +interface DatabaseModuleInterface { + updateConversationRead: (conversationId: string) => void + updateAllDirectMessagesRead: () => void + updateUserContact: (userId: string, contact: boolean, callback: () => void) => void + updateUserBlock: (userId: string, blocked: boolean, callback: () => void) => void + updateUserMutesGroups: (userId: string, muted: boolean, callback: () => void) => void + updateAllGroupMessagesRead: () => void + updateGroupRead: (groupId: string) => void + deleteGroup: (groupId: string) => void + activateGroup: (groupId: string) => void +} + +export default DatabaseModule as DatabaseModuleInterface