From 8ae9165efad14418ec969f19dcc83104ae8562ae Mon Sep 17 00:00:00 2001 From: koalasat Date: Fri, 25 Jul 2025 18:16:35 +0200 Subject: [PATCH] Configuration and fixes --- .../main/java/com/robosats/MainActivity.kt | 20 +++++++++++-- .../main/java/com/robosats/WebAppInterface.kt | 13 ++++++++- .../java/com/robosats/models/NostrClient.kt | 28 +++++++++++-------- .../robosats/services/NotificationsService.kt | 5 ++-- .../TopBar/NotificationsDrawer/index.tsx | 5 ++-- .../src/components/SettingsForm/index.tsx | 15 ++++------ frontend/src/models/Settings.model.ts | 6 ++-- 7 files changed, 59 insertions(+), 33 deletions(-) diff --git a/android/app/src/main/java/com/robosats/MainActivity.kt b/android/app/src/main/java/com/robosats/MainActivity.kt index d3365e7f..065703f9 100644 --- a/android/app/src/main/java/com/robosats/MainActivity.kt +++ b/android/app/src/main/java/com/robosats/MainActivity.kt @@ -97,12 +97,23 @@ class MainActivity : AppCompatActivity() { } } + /** + * Initialize Notifications service + */ + fun initializeNotifications() { + startForegroundService( + Intent( + this, + NotificationsService::class.java, + ), + ) + } /** * Initialize Notifications service */ - private fun initializeNotifications() { - startForegroundService( + fun stopNotifications() { + stopService( Intent( this, NotificationsService::class.java, @@ -319,7 +330,8 @@ class MainActivity : AppCompatActivity() { // Now it's safe to load the local HTML file webView.loadUrl("file:///android_asset/index.html") - initializeNotifications() + val notifications = EncryptedStorage.getEncryptedStorage("settings_notifications") + if (notifications != "false") initializeNotifications() webView.post { try { @@ -425,6 +437,8 @@ class MainActivity : AppCompatActivity() { WebStorage.getInstance().deleteAllData() + stopNotifications() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CookieManager.getInstance().removeSessionCookies(null) } diff --git a/android/app/src/main/java/com/robosats/WebAppInterface.kt b/android/app/src/main/java/com/robosats/WebAppInterface.kt index 37452cf7..3b147d93 100644 --- a/android/app/src/main/java/com/robosats/WebAppInterface.kt +++ b/android/app/src/main/java/com/robosats/WebAppInterface.kt @@ -2,12 +2,14 @@ package com.robosats import android.annotation.SuppressLint import android.content.Context +import android.content.Intent import android.util.Log import android.webkit.JavascriptInterface import android.webkit.WebView import android.widget.Toast import com.robosats.models.EncryptedStorage import com.robosats.models.NostrClient +import com.robosats.services.NotificationsService import com.robosats.tor.TorKmpManager.getTorKmpObject import okhttp3.Call import okhttp3.Callback @@ -171,6 +173,7 @@ class WebAppInterface(private val context: Context, private val webView: WebView val client: OkHttpClient = Builder() .connectTimeout(60, TimeUnit.SECONDS) // Set connection timeout .readTimeout(30, TimeUnit.SECONDS) // Set read timeout + .proxy(getTorKmpObject().proxy) .build() @@ -356,7 +359,15 @@ class WebAppInterface(private val context: Context, private val webView: WebView EncryptedStorage.setEncryptedStorage(sanitizedKey, sanitizedValue) - if (key == "federation_relays") NostrClient.refresh() + if (key == "garage_slots") NostrClient.refresh() + if (key == "settings_notifications") { + val serviceIntent = Intent(context, NotificationsService::class.java) + if (value == "true") { + context.startForegroundService(serviceIntent) + } else { + context.stopService(serviceIntent) + } + } // Safely encode and return the result resolvePromise(uuid, key) diff --git a/android/app/src/main/java/com/robosats/models/NostrClient.kt b/android/app/src/main/java/com/robosats/models/NostrClient.kt index 1e6e6dea..dfbd3727 100644 --- a/android/app/src/main/java/com/robosats/models/NostrClient.kt +++ b/android/app/src/main/java/com/robosats/models/NostrClient.kt @@ -14,6 +14,7 @@ import org.json.JSONObject object NostrClient { private var subscriptionNotificationId = "robosatsNotificationId" + private var authors = garagePubKeys() fun init() { RelayPool.register(Client) @@ -29,11 +30,9 @@ object NostrClient { } fun refresh() { - val federationRelays = EncryptedStorage.getEncryptedStorage("federation_relays") - val relayPool = RelayPool.getAll().map { it.url } - if (federationRelays.toSet() != relayPool.toSet()) { - stop() - start() + val pubKeys = garagePubKeys() + if (authors.toSet() != pubKeys.toSet()) { + subscribeToInbox() } } @@ -55,7 +54,7 @@ object NostrClient { fun garagePubKeys(): List { val garageString = EncryptedStorage.getEncryptedStorage("garage_slots") - var authors = emptyList() + var pubKeys = emptyList() if (garageString.isNotEmpty()) { val garage = JSONObject(garageString) @@ -66,12 +65,12 @@ object NostrClient { val slot = garage.getJSONObject(key) // Get the value associated with the key val hexPubKey = slot.getString("nostrPubKey") if (hexPubKey.isNotEmpty()) { - authors = authors.plus(hexPubKey) + pubKeys = pubKeys.plus(hexPubKey) } } } - return authors + return pubKeys } fun getRobotKeyPair(hexPubKey: String): KeyPair { @@ -110,8 +109,10 @@ object NostrClient { if (federationRelays.isNotEmpty()) { val relaysUrls = JSONArray(federationRelays) - for (i in 0 until relaysUrls.length()) { - val url = relaysUrls.getString(i) + val relayList = (0 until relaysUrls.length()).map { relaysUrls.getString(it) } + val randomRelays = relayList.shuffled().take(3) + + for (url in randomRelays) { Client.sendFilterOnlyIfDisconnected() if (RelayPool.getRelays(url).isEmpty()) { RelayPool.addRelay( @@ -126,15 +127,20 @@ object NostrClient { } } } + } private fun subscribeToInbox() { val garageString = EncryptedStorage.getEncryptedStorage("garage_slots") if (garageString.isNotEmpty()) { - val authors = garagePubKeys() + authors = garagePubKeys() if (authors.isNotEmpty()) { + Log.d( + "RobosatsNostrClient", + "Relay subscription authors: ${authors.size}", + ) Client.sendFilter( subscriptionNotificationId, listOf( diff --git a/android/app/src/main/java/com/robosats/services/NotificationsService.kt b/android/app/src/main/java/com/robosats/services/NotificationsService.kt index 837c685f..f0aad18f 100644 --- a/android/app/src/main/java/com/robosats/services/NotificationsService.kt +++ b/android/app/src/main/java/com/robosats/services/NotificationsService.kt @@ -13,7 +13,6 @@ import android.graphics.Path import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities -import android.net.Uri import android.os.IBinder import android.util.Base64 import android.util.Log @@ -22,7 +21,9 @@ import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationChannelGroupCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.graphics.createBitmap import com.robosats.Connectivity +import com.robosats.MainActivity import com.robosats.R import com.robosats.RoboIdentities import com.robosats.models.EncryptedStorage @@ -45,8 +46,6 @@ import org.json.JSONObject import java.util.Timer import java.util.TimerTask import java.util.concurrent.ConcurrentHashMap -import androidx.core.graphics.createBitmap -import com.robosats.MainActivity class NotificationsService : Service() { private var channelRelaysId = "RelaysConnections" diff --git a/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx b/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx index 5446ef8a..0d7ace2d 100644 --- a/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx +++ b/frontend/src/basic/TopBar/NotificationsDrawer/index.tsx @@ -51,7 +51,7 @@ const NotificationsDrawer = ({ }: NotificationsDrawerProps): React.JSX.Element => { const theme = useTheme(); const navigate = useNavigate(); - const { page, settings, navigateToPage } = useContext(AppContext); + const { page, settings, navigateToPage, client } = useContext(AppContext); const { federation } = useContext(FederationContext); const { garage, slotUpdatedAt } = useContext(GarageContext); @@ -118,11 +118,10 @@ const NotificationsDrawer = ({ setLastNotification((last) => { if (last < event.created_at) { setSnakevent(event); - setOpenSnak(true); systemClient.setItem('last_notification', event.created_at.toString()); - console.log(event); const orderStatus = event.tags.find((t) => t[0] === 'status')?.[1]; if (orderStatus) playSound(parseInt(orderStatus, 10)); + if (client !== 'mobile') setOpenSnak(true); return event.created_at; } else { diff --git a/frontend/src/components/SettingsForm/index.tsx b/frontend/src/components/SettingsForm/index.tsx index 65661a98..afe36875 100644 --- a/frontend/src/components/SettingsForm/index.tsx +++ b/frontend/src/components/SettingsForm/index.tsx @@ -247,19 +247,16 @@ const SettingsForm = ({ dense = false }: SettingsFormProps): React.JSX.Element = { - setSettings({ ...settings, stopNotifications }); - systemClient.setItem( - 'settings_stop_notifications', - String(settings.stopNotifications), - ); + value={settings.androidNotifications} + onChange={(_e, androidNotifications) => { + setSettings({ ...settings, androidNotifications }); + systemClient.setItem('settings_notifications', String(androidNotifications)); }} > - + {t('On')} - + {t('Off')} diff --git a/frontend/src/models/Settings.model.ts b/frontend/src/models/Settings.model.ts index 36f30944..3aab755d 100644 --- a/frontend/src/models/Settings.model.ts +++ b/frontend/src/models/Settings.model.ts @@ -59,8 +59,8 @@ class BaseSettings { this.network = result && result !== '' ? (result as 'mainnet' | 'testnet') : this.network; }); - systemClient.getItem('settings_stop_notifications').then((result) => { - this.stopNotifications = client === 'mobile' && result === 'true'; + systemClient.getItem('settings_notifications').then((result) => { + this.androidNotifications = result === 'true'; }); systemClient.getItem('settings_use_proxy').then((result) => { @@ -87,7 +87,7 @@ class BaseSettings { public unsafeClient: boolean = false; public selfhostedClient: boolean = false; public useProxy: boolean = false; - public stopNotifications: boolean = false; + public androidNotifications: boolean = false; } export default BaseSettings;