diff --git a/android/app/build.gradle b/android/app/build.gradle
index 9132b0867..06b86844c 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -104,6 +104,7 @@ android {
proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
}
}
+
}
task copyFiatUnits(type: Copy) {
@@ -117,6 +118,8 @@ dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation files("../../node_modules/rn-ldk/android/libs/LDK-release.aar")
+ implementation 'androidx.core:core-ktx:1.13.1'
+ implementation 'androidx.core:core-remoteviews:1.1.0'
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
@@ -129,7 +132,9 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.4.0'
implementation "androidx.work:work-runtime:2.9.0"
+ implementation "androidx.work:work-runtime-ktx:2.9.0"
}
apply plugin: 'com.google.gms.google-services' // Google Services plugin
-apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
\ No newline at end of file
+apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle")
+apply plugin: 'org.jetbrains.kotlin.android'; applyNativeModulesAppBuildGradle(project)
\ No newline at end of file
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 121d60b5a..ade4b6cb1 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
getPackages() {
- @SuppressWarnings("UnnecessaryLocalVariable")
- List packages = new PackageList(this).getPackages();
- return packages;
- }
-
- @Override
- protected String getJSMainModuleName() {
- return "index";
- }
-
- @Override
- protected boolean isNewArchEnabled() {
- return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
- }
-
- @Override
- protected Boolean isHermesEnabled() {
- return BuildConfig.IS_HERMES_ENABLED;
- }
- };
-
- @Override
- public ReactNativeHost getReactNativeHost() {
- return mReactNativeHost;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance();
- sharedI18nUtilInstance.allowRTL(getApplicationContext(), true);
- SoLoader.init(this, /* native exopackage */ false);
-
- if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
- DefaultNewArchitectureEntryPoint.load();
- }
-
- SharedPreferences sharedPref = getApplicationContext().getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE);
- String isDoNotTrackEnabled = sharedPref.getString("donottrack", "0");
-
- if (!isDoNotTrackEnabled.equals("1")) {
- Bugsnag.start(this);
- }
-
-
- // Schedule periodic widget updates
- PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(WidgetUpdateWorker.class, 4, TimeUnit.MINUTES).build();
- WorkManager.getInstance(this).enqueueUniquePeriodicWork("UpdateWidgetWork", ExistingPeriodicWorkPolicy.REPLACE, workRequest);
-
- }
-}
diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.kt b/android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.kt
new file mode 100644
index 000000000..0df09249c
--- /dev/null
+++ b/android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.kt
@@ -0,0 +1,57 @@
+package io.bluewallet.bluewallet
+
+import android.app.Application
+import android.content.Context
+import com.bugsnag.android.Bugsnag
+import com.facebook.react.PackageList
+import com.facebook.react.ReactApplication
+import com.facebook.react.ReactInstanceManager
+import com.facebook.react.ReactNativeHost
+import com.facebook.react.ReactPackage
+import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
+import com.facebook.react.defaults.DefaultReactNativeHost
+import com.facebook.soloader.SoLoader
+import com.facebook.react.modules.i18nmanager.I18nUtil
+
+class MainApplication : Application(), ReactApplication {
+
+ private val mReactNativeHost = object : DefaultReactNativeHost(this) {
+ override fun getUseDeveloperSupport() = BuildConfig.DEBUG
+
+ override fun getPackages(): List {
+ val packages = PackageList(this).packages
+ // Packages that cannot be autolinked yet can be added manually here, for example:
+ return packages
+ }
+
+ override fun getJSMainModuleName() = "index"
+
+ override val isNewArchEnabled = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
+
+ override val isHermesEnabled = BuildConfig.IS_HERMES_ENABLED
+ }
+
+ override fun getReactNativeHost() = mReactNativeHost
+
+ override fun onCreate() {
+ super.onCreate()
+ val sharedI18nUtilInstance = I18nUtil.getInstance()
+ sharedI18nUtilInstance.allowRTL(applicationContext, true)
+ SoLoader.init(this, /* native exopackage */ false)
+ if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
+ // If you opted-in for the New Architecture, we load the native entry point for this app.
+ DefaultNewArchitectureEntryPoint.load()
+ }
+
+ val sharedPref = applicationContext.getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE)
+
+ // Retrieve the "donottrack" value. Default to "0" if not found.
+ val isDoNotTrackEnabled = sharedPref.getString("donottrack", "0")
+
+ // Check if do not track is not enabled and initialize Bugsnag if so
+ if (isDoNotTrackEnabled != "1") {
+ // Initialize Bugsnag or your error tracking here
+ Bugsnag.start(this)
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.java b/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.java
deleted file mode 100644
index 3a35aa2d8..000000000
--- a/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package io.bluewallet.bluewallet;
-
-import android.content.Context;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONObject;
-
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URL;
-
-public class MarketAPI {
-
- private static final String TAG = "MarketAPI";
- private static final String HARD_CODED_JSON = "{\n" +
- " \"USD\": {\n" +
- " \"endPointKey\": \"USD\",\n" +
- " \"locale\": \"en-US\",\n" +
- " \"source\": \"Kraken\",\n" +
- " \"symbol\": \"$\",\n" +
- " \"country\": \"United States (US Dollar)\"\n" +
- " }\n" +
- "}";
-
- public static String fetchPrice(Context context, String currency) {
- try {
- JSONObject json = new JSONObject(HARD_CODED_JSON);
- JSONObject currencyInfo = json.getJSONObject(currency);
- String source = currencyInfo.getString("source");
- String endPointKey = currencyInfo.getString("endPointKey");
-
- String urlString = buildURLString(source, endPointKey);
- Log.d(TAG, "Fetching from URL: " + urlString);
- URI uri = new URI(urlString);
- URL url = uri.toURL();
- HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
- urlConnection.setRequestMethod("GET");
- urlConnection.connect();
-
- int responseCode = urlConnection.getResponseCode();
- if (responseCode != 200) {
- return null;
- }
-
- InputStreamReader reader = new InputStreamReader(urlConnection.getInputStream());
- StringBuilder jsonResponse = new StringBuilder();
- int read;
- char[] buffer = new char[1024];
- while ((read = reader.read(buffer)) != -1) {
- jsonResponse.append(buffer, 0, read);
- }
-
- return parseJSONBasedOnSource(jsonResponse.toString(), source, endPointKey);
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-
- private static String buildURLString(String source, String endPointKey) {
- switch (source) {
- case "Yadio":
- return "https://api.yadio.io/json/" + endPointKey;
- case "YadioConvert":
- return "https://api.yadio.io/convert/1/BTC/" + endPointKey;
- case "Exir":
- return "https://api.exir.io/v1/ticker?symbol=btc-irt";
- case "wazirx":
- return "https://api.wazirx.com/api/v2/tickers/btcinr";
- case "Bitstamp":
- return "https://www.bitstamp.net/api/v2/ticker/btc" + endPointKey.toLowerCase();
- case "Coinbase":
- return "https://api.coinbase.com/v2/prices/BTC-" + endPointKey.toUpperCase() + "/buy";
- case "CoinGecko":
- return "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=" + endPointKey.toLowerCase();
- case "BNR":
- return "https://www.bnr.ro/nbrfxrates.xml";
- case "Kraken":
- return "https://api.kraken.com/0/public/Ticker?pair=XXBTZ" + endPointKey.toUpperCase();
- default:
- return "https://api.coindesk.com/v1/bpi/currentprice/" + endPointKey + ".json";
- }
- }
-
- private static String parseJSONBasedOnSource(String jsonString, String source, String endPointKey) {
- try {
- JSONObject json = new JSONObject(jsonString);
- switch (source) {
- case "Yadio":
- JSONObject rateDict = json.getJSONObject(endPointKey);
- return rateDict.getString("price");
- case "YadioConvert":
- return json.getString("rate");
- case "CoinGecko":
- JSONObject bitcoinDict = json.getJSONObject("bitcoin");
- return bitcoinDict.getString(endPointKey.toLowerCase());
- case "Exir":
- return json.getString("last");
- case "Bitstamp":
- return json.getString("last");
- case "wazirx":
- JSONObject tickerDict = json.getJSONObject("ticker");
- return tickerDict.getString("buy");
- case "Coinbase":
- JSONObject data = json.getJSONObject("data");
- return data.getString("amount");
- case "Kraken":
- JSONObject result = json.getJSONObject("result");
- JSONObject tickerData = result.getJSONObject("XXBTZ" + endPointKey.toUpperCase());
- JSONArray c = tickerData.getJSONArray("c");
- return c.getString(0);
- default:
- return null;
- }
- } catch (Exception e) {
- e.printStackTrace();
- return null;
- }
- }
-}
diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.kt b/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.kt
new file mode 100644
index 000000000..819767458
--- /dev/null
+++ b/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.kt
@@ -0,0 +1,99 @@
+package io.bluewallet.bluewallet
+
+import android.content.Context
+import android.util.Log
+import org.json.JSONObject
+import java.io.InputStreamReader
+import java.net.HttpURLConnection
+import java.net.URL
+
+object MarketAPI {
+
+ private const val TAG = "MarketAPI"
+ private const val HARD_CODED_JSON = "{\n" +
+ " \"USD\": {\n" +
+ " \"endPointKey\": \"USD\",\n" +
+ " \"locale\": \"en-US\",\n" +
+ " \"source\": \"Kraken\",\n" +
+ " \"symbol\": \"$\",\n" +
+ " \"country\": \"United States (US Dollar)\"\n" +
+ " }\n" +
+ "}"
+
+ var baseUrl: String? = null
+
+ fun fetchPrice(context: Context, currency: String): String? {
+ return try {
+ val json = JSONObject(HARD_CODED_JSON)
+ val currencyInfo = json.getJSONObject(currency)
+ val source = currencyInfo.getString("source")
+ val endPointKey = currencyInfo.getString("endPointKey")
+
+ val urlString = buildURLString(source, endPointKey)
+ Log.d(TAG, "Fetching price from URL: $urlString")
+
+ val url = URL(urlString)
+ val urlConnection = url.openConnection() as HttpURLConnection
+ urlConnection.requestMethod = "GET"
+ urlConnection.connect()
+
+ val responseCode = urlConnection.responseCode
+ if (responseCode != 200) {
+ Log.e(TAG, "Failed to fetch price. Response code: $responseCode")
+ return null
+ }
+
+ val reader = InputStreamReader(urlConnection.inputStream)
+ val jsonResponse = StringBuilder()
+ val buffer = CharArray(1024)
+ var read: Int
+ while (reader.read(buffer).also { read = it } != -1) {
+ jsonResponse.append(buffer, 0, read)
+ }
+
+ parseJSONBasedOnSource(jsonResponse.toString(), source, endPointKey)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error fetching price", e)
+ null
+ }
+ }
+
+ private fun buildURLString(source: String, endPointKey: String): String {
+ return if (baseUrl != null) {
+ baseUrl + endPointKey
+ } else {
+ when (source) {
+ "Yadio" -> "https://api.yadio.io/json/$endPointKey"
+ "YadioConvert" -> "https://api.yadio.io/convert/1/BTC/$endPointKey"
+ "Exir" -> "https://api.exir.io/v1/ticker?symbol=btc-irt"
+ "wazirx" -> "https://api.wazirx.com/api/v2/tickers/btcinr"
+ "Bitstamp" -> "https://www.bitstamp.net/api/v2/ticker/btc${endPointKey.lowercase()}"
+ "Coinbase" -> "https://api.coinbase.com/v2/prices/BTC-${endPointKey.uppercase()}/buy"
+ "CoinGecko" -> "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=${endPointKey.lowercase()}"
+ "BNR" -> "https://www.bnr.ro/nbrfxrates.xml"
+ "Kraken" -> "https://api.kraken.com/0/public/Ticker?pair=XXBTZ${endPointKey.uppercase()}"
+ else -> "https://api.coindesk.com/v1/bpi/currentprice/$endPointKey.json"
+ }
+ }
+ }
+
+ private fun parseJSONBasedOnSource(jsonString: String, source: String, endPointKey: String): String? {
+ return try {
+ val json = JSONObject(jsonString)
+ when (source) {
+ "Yadio" -> json.getJSONObject(endPointKey).getString("price")
+ "YadioConvert" -> json.getString("rate")
+ "CoinGecko" -> json.getJSONObject("bitcoin").getString(endPointKey.lowercase())
+ "Exir" -> json.getString("last")
+ "Bitstamp" -> json.getString("last")
+ "wazirx" -> json.getJSONObject("ticker").getString("buy")
+ "Coinbase" -> json.getJSONObject("data").getString("amount")
+ "Kraken" -> json.getJSONObject("result").getJSONObject("XXBTZ${endPointKey.uppercase()}").getJSONArray("c").getString(0)
+ else -> null
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error parsing price", e)
+ null
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateManager.java b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateManager.java
deleted file mode 100644
index 3e06a53b2..000000000
--- a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateManager.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package io.bluewallet.bluewallet;
-
-import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-import android.view.View;
-import android.widget.RemoteViews;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-
-import java.text.DateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-public class WidgetUpdateManager extends Worker {
- private static final String TAG = "WidgetUpdateManager";
- private static final String PREFS_NAME = "BitcoinPriceWidgetPrefs";
- private static final String PREF_PREFIX_KEY = "appwidget_";
- private static final String PREFS_LAST_UPDATE_TIME_KEY = "lastUpdateTime_";
- private static final String PREFS_LAST_PRICE_KEY = "lastPrice_";
- private static final String PREFS_PREV_PRICE_KEY = "prevPrice_";
- private static final String PREFS_PREV_UPDATE_TIME_KEY = "prevUpdateTime_";
-
- public WidgetUpdateManager(Context context, WorkerParameters params) {
- super(context, params);
- }
-
- @Override
- public Result doWork() {
- Context context = getApplicationContext();
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, BitcoinPriceWidget.class));
-
- for (int appWidgetId : appWidgetIds) {
- updateAppWidget(context, appWidgetManager, appWidgetId);
- }
- return Result.success();
- }
-
- public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
- SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
- String previousPrice = prefs.getString(PREFS_PREV_PRICE_KEY + appWidgetId, "N/A");
- String lastPrice = prefs.getString(PREFS_LAST_PRICE_KEY + appWidgetId, "N/A");
- String lastUpdateTime = prefs.getString(PREFS_LAST_UPDATE_TIME_KEY + appWidgetId, "N/A");
- String previousUpdateTime = prefs.getString(PREFS_PREV_UPDATE_TIME_KEY + appWidgetId, "N/A");
-
- Log.d(TAG, "Fetch completed with price: " + lastPrice + " at " + lastUpdateTime + ". Previous price: " + previousPrice + " at " + previousUpdateTime);
-
- String price = MarketAPI.fetchPrice(context, "USD");
- if (price != null) {
- Log.d(TAG, "Fetch completed with price: " + price);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(PREFS_PREV_PRICE_KEY + appWidgetId, lastPrice);
- editor.putString(PREFS_PREV_UPDATE_TIME_KEY + appWidgetId, lastUpdateTime);
- editor.putString(PREFS_LAST_PRICE_KEY + appWidgetId, price);
- editor.putString(PREFS_LAST_UPDATE_TIME_KEY + appWidgetId, DateFormat.getTimeInstance(DateFormat.SHORT).format(new Date()));
- editor.apply();
-
- RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
- views.setTextViewText(R.id.price_value, price);
- views.setTextViewText(R.id.last_updated_time, DateFormat.getTimeInstance(DateFormat.SHORT).format(new Date()));
- views.setViewVisibility(R.id.price_value, View.VISIBLE);
- views.setViewVisibility(R.id.last_updated_label, View.VISIBLE);
- views.setViewVisibility(R.id.last_updated_time, View.VISIBLE);
-
- if (!"N/A".equals(previousPrice)) {
- views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE);
- views.setViewVisibility(R.id.previous_price, View.VISIBLE);
- } else {
- views.setViewVisibility(R.id.price_arrow_container, View.GONE);
- views.setViewVisibility(R.id.previous_price, View.GONE);
- }
- appWidgetManager.updateAppWidget(appWidgetId, views);
- } else {
- Log.e(TAG, "Failed to fetch Bitcoin price");
- }
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.java b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.java
deleted file mode 100644
index d62aecd1a..000000000
--- a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package io.bluewallet.bluewallet;
-
-import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Log;
-import android.view.View;
-import android.widget.RemoteViews;
-
-import androidx.annotation.NonNull;
-import androidx.work.Worker;
-import androidx.work.WorkerParameters;
-import androidx.work.OneTimeWorkRequest;
-
-import java.text.NumberFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-
-public class WidgetUpdateWorker extends Worker {
-
- private static final String TAG = "WidgetUpdateWorker";
- private static final String PREFS_NAME = "BitcoinPriceWidgetPrefs";
- private static final String PREF_PREFIX_KEY_CURRENT = "appwidget_current_";
- private static final String PREF_PREFIX_KEY_PREVIOUS = "appwidget_previous_";
-
- public WidgetUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
- super(context, params);
- }
-
- @NonNull
- @Override
- public Result doWork() {
- Context context = getApplicationContext();
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- ComponentName thisWidget = new ComponentName(context, BitcoinPriceWidget.class);
- int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
- for (int appWidgetId : appWidgetIds) {
- updateAppWidget(context, appWidgetManager, appWidgetId);
- }
- return Result.success();
- }
-
- private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
- SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
- String prevPrice = prefs.getString(PREF_PREFIX_KEY_PREVIOUS + appWidgetId, "N/A");
- String currentPrice = prefs.getString(PREF_PREFIX_KEY_CURRENT + appWidgetId, "N/A");
-
- Log.d(TAG, "Previous price: " + prevPrice);
- Log.d(TAG, "Current price: " + currentPrice);
-
- RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
-
- // Fetch the latest price
- String newPrice = MarketAPI.fetchPrice(context, "USD");
- if (newPrice != null) {
- String currentTime = new SimpleDateFormat("hh:mm a", Locale.getDefault()).format(new Date());
- NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.getDefault());
-
- // Update the widget views
- views.setTextViewText(R.id.price_value, currencyFormat.format(Double.parseDouble(newPrice)));
- views.setTextViewText(R.id.last_updated_time, currentTime);
- views.setViewVisibility(R.id.last_updated_label, View.VISIBLE);
- views.setViewVisibility(R.id.last_updated_time, View.VISIBLE);
-
- if (!prevPrice.equals("N/A") && !prevPrice.equals(newPrice)) {
- views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE);
- views.setTextViewText(R.id.previous_price, currencyFormat.format(Double.parseDouble(prevPrice)));
- views.setViewVisibility(R.id.previous_price_label, View.VISIBLE);
- views.setViewVisibility(R.id.previous_price, View.VISIBLE);
- if (Double.parseDouble(newPrice) > Double.parseDouble(prevPrice)) {
- views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_up_float);
- } else {
- views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_down_float);
- }
- } else {
- views.setViewVisibility(R.id.price_arrow_container, View.GONE);
- views.setViewVisibility(R.id.previous_price_label, View.GONE);
- views.setViewVisibility(R.id.previous_price, View.GONE);
- }
-
- // Save the new price and time
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(PREF_PREFIX_KEY_PREVIOUS + appWidgetId, currentPrice);
- editor.putString(PREF_PREFIX_KEY_CURRENT + appWidgetId, newPrice);
- editor.apply();
-
- Log.d(TAG, "Fetch completed with price: " + newPrice + " at " + currentTime + ". Previous price: " + prevPrice);
-
- appWidgetManager.updateAppWidget(appWidgetId, views);
-
- // Log the next update time
- long nextUpdateTimeMillis = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(4);
- String nextUpdateTime = new SimpleDateFormat("hh:mm a", Locale.getDefault()).format(new Date(nextUpdateTimeMillis));
- Log.d(TAG, "Next fetch scheduled at: " + nextUpdateTime);
- } else {
- Log.e(TAG, "Failed to fetch Bitcoin price");
- views.setTextViewText(R.id.price_value, "Error");
- appWidgetManager.updateAppWidget(appWidgetId, views);
- }
- }
-
- public static OneTimeWorkRequest createWorkRequest() {
- return new OneTimeWorkRequest.Builder(WidgetUpdateWorker.class).build();
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.kt b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.kt
new file mode 100644
index 000000000..30b30f1e4
--- /dev/null
+++ b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.kt
@@ -0,0 +1,107 @@
+package io.bluewallet.bluewallet
+
+import android.appwidget.AppWidgetManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.SharedPreferences
+import android.util.Log
+import android.view.View
+import android.widget.RemoteViews
+import androidx.work.*
+import org.json.JSONObject
+import java.io.InputStreamReader
+import java.net.HttpURLConnection
+import java.net.URL
+import java.text.NumberFormat
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
+
+ companion object {
+ const val TAG = "WidgetUpdateWorker"
+ const val WORK_NAME = "widget_update_work"
+ const val REPEAT_INTERVAL_MINUTES = 15L
+
+ fun scheduleWork(context: Context) {
+ val workRequest = PeriodicWorkRequestBuilder(
+ REPEAT_INTERVAL_MINUTES, TimeUnit.MINUTES
+ ).build()
+ WorkManager.getInstance(context).enqueueUniquePeriodicWork(
+ WORK_NAME,
+ ExistingPeriodicWorkPolicy.REPLACE,
+ workRequest
+ )
+ Log.d(TAG, "Scheduling work for widget updates, will run every $REPEAT_INTERVAL_MINUTES minutes")
+ }
+ }
+
+ override fun doWork(): Result {
+ Log.d(TAG, "Widget update worker running")
+
+ val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
+ val thisWidget = ComponentName(applicationContext, BitcoinPriceWidget::class.java)
+ val appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget)
+ val views = RemoteViews(applicationContext.packageName, R.layout.widget_layout)
+
+ val sharedPref = applicationContext.getSharedPreferences("widget_prefs", Context.MODE_PRIVATE)
+
+ val currentTime = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date())
+ val price = fetchPrice()
+ val previousPrice = sharedPref.getString("previous_price", null)
+
+ // Log fetched data
+ Log.d(TAG, "Fetch completed with price: $price at $currentTime. Previous price: $previousPrice")
+
+ // Update views
+ val currencyFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()).apply {
+ maximumFractionDigits = 0
+ }
+ views.setTextViewText(R.id.price_value, currencyFormat.format(price.toDouble()))
+ views.setTextViewText(R.id.last_updated_time, currentTime)
+ views.setViewVisibility(R.id.last_updated_label, View.VISIBLE)
+ views.setViewVisibility(R.id.last_updated_time, View.VISIBLE)
+
+ if (previousPrice != null) {
+ views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE)
+ views.setTextViewText(R.id.previous_price, currencyFormat.format(previousPrice.toDouble()))
+ if (price.toDouble() > previousPrice.toDouble()) {
+ views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_up_float)
+ } else {
+ views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_down_float)
+ }
+ } else {
+ views.setViewVisibility(R.id.price_arrow_container, View.GONE)
+ }
+
+ appWidgetManager.updateAppWidget(appWidgetIds, views)
+
+ savePrice(sharedPref, price)
+
+ return Result.success()
+ }
+
+ private fun fetchPrice(): String {
+ val urlString = "https://api.kraken.com/0/public/Ticker?pair=XXBTZUSD"
+ val url = URL(urlString)
+ val urlConnection = url.openConnection() as HttpURLConnection
+ return try {
+ val reader = InputStreamReader(urlConnection.inputStream)
+ val jsonResponse = StringBuilder()
+ val buffer = CharArray(1024)
+ var read: Int
+ while (reader.read(buffer).also { read = it } != -1) {
+ jsonResponse.append(buffer, 0, read)
+ }
+ val json = JSONObject(jsonResponse.toString())
+ json.getJSONObject("result").getJSONObject("XXBTZUSD").getJSONArray("c").getString(0)
+ } finally {
+ urlConnection.disconnect()
+ }
+ }
+
+ private fun savePrice(sharedPref: SharedPreferences, price: String) {
+ sharedPref.edit().putString("previous_price", price).apply()
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUtils.java b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUtils.java
deleted file mode 100644
index 3b1a128fa..000000000
--- a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUtils.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package io.bluewallet.bluewallet;
-
-import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.widget.RemoteViews;
-
-public class WidgetUtils {
-
- public static void updateAppWidget(Context context, RemoteViews views) {
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- ComponentName thisWidget = new ComponentName(context, BitcoinPriceWidget.class);
- int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
-
- for (int widgetId : allWidgetIds) {
- appWidgetManager.updateAppWidget(widgetId, views);
- }
- }
-}
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index f0a8fbef3..4b2a094b9 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -1,3 +1,6 @@
BlueWallet
+ Loading...
+ Last Updated
+ From
diff --git a/android/app/src/main/res/xml/bitcoin_price_widget_info.xml b/android/app/src/main/res/xml/bitcoin_price_widget_info.xml
index db053a920..736f12190 100644
--- a/android/app/src/main/res/xml/bitcoin_price_widget_info.xml
+++ b/android/app/src/main/res/xml/bitcoin_price_widget_info.xml
@@ -1,9 +1,10 @@
+ android:resizeMode="none"
+/>
diff --git a/android/build.gradle b/android/build.gradle
index 7b4ae292c..609bfedc5 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -15,7 +15,7 @@ buildscript {
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
kotlin_version = '1.9.23'
- kotlinVersion = '1.9.20'
+ kotlinVersion = '1.9.23'
}
repositories {
google()
diff --git a/components/TransactionListItem.tsx b/components/TransactionListItem.tsx
index d700f6efb..b3a13be03 100644
--- a/components/TransactionListItem.tsx
+++ b/components/TransactionListItem.tsx
@@ -22,6 +22,7 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { DetailViewStackParamList } from '../navigation/DetailViewStackParamList';
import { useStorage } from '../hooks/context/useStorage';
import ToolTipMenu from './TooltipMenu';
+import { CommonToolTipActions } from '../typings/CommonToolTipActions';
interface TransactionListItemProps {
itemPriceUnit: BitcoinUnit;
@@ -255,17 +256,17 @@ export const TransactionListItem: React.FC = React.mem
const onToolTipPress = useCallback(
(id: any) => {
- if (id === actionKeys.CopyAmount) {
+ if (id === CommonToolTipActions.CopyAmount.id) {
handleOnCopyAmountTap();
- } else if (id === actionKeys.CopyNote) {
+ } else if (id === CommonToolTipActions.CopyNote.id) {
handleOnCopyNote();
- } else if (id === actionKeys.OpenInBlockExplorer) {
+ } else if (id === CommonToolTipActions.OpenInBlockExplorer.id) {
handleOnViewOnBlockExplorer();
- } else if (id === actionKeys.ExpandNote) {
+ } else if (id === CommonToolTipActions.ExpandNote.id) {
handleOnExpandNote();
- } else if (id === actionKeys.CopyBlockExplorerLink) {
+ } else if (id === CommonToolTipActions.CopyBlockExplorerLink.id) {
handleCopyOpenInBlockExplorerPress();
- } else if (id === actionKeys.CopyTXID) {
+ } else if (id === CommonToolTipActions.CopyTXID.id) {
handleOnCopyTransactionID();
}
},
@@ -282,51 +283,19 @@ export const TransactionListItem: React.FC = React.mem
const actions: (Action | Action[])[] = [];
if (rowTitle !== loc.lnd.expired) {
- actions.push({
- id: actionKeys.CopyAmount,
- text: loc.transactions.details_copy_amount,
- icon: actionIcons.Clipboard,
- });
+ actions.push(CommonToolTipActions.CopyAmount);
}
if (subtitle) {
- actions.push({
- id: actionKeys.CopyNote,
- text: loc.transactions.details_copy_note,
- icon: actionIcons.Clipboard,
- });
+ actions.push(CommonToolTipActions.CopyNote);
}
if (item.hash) {
- actions.push(
- {
- id: actionKeys.CopyTXID,
- text: loc.transactions.details_copy_txid,
- icon: actionIcons.Clipboard,
- },
- {
- id: actionKeys.CopyBlockExplorerLink,
- text: loc.transactions.details_copy_block_explorer_link,
- icon: actionIcons.Clipboard,
- },
- [
- {
- id: actionKeys.OpenInBlockExplorer,
- text: loc.transactions.details_show_in_block_explorer,
- icon: actionIcons.Link,
- },
- ],
- );
+ actions.push(CommonToolTipActions.CopyTXID, CommonToolTipActions.CopyBlockExplorerLink, [CommonToolTipActions.OpenInBlockExplorer]);
}
if (subtitle && subtitleNumberOfLines === 1) {
- actions.push([
- {
- id: actionKeys.ExpandNote,
- text: loc.transactions.expand_note,
- icon: actionIcons.Note,
- },
- ]);
+ actions.push([CommonToolTipActions.ExpandNote]);
}
return actions as Action[] | Action[][];
@@ -363,30 +332,3 @@ export const TransactionListItem: React.FC = React.mem
);
});
-
-const actionKeys = {
- CopyTXID: 'copyTX_ID',
- CopyBlockExplorerLink: 'copy_blockExplorer',
- ExpandNote: 'expandNote',
- OpenInBlockExplorer: 'open_in_blockExplorer',
- CopyAmount: 'copyAmount',
- CopyNote: 'copyNote',
-};
-
-const actionIcons = {
- Eye: {
- iconValue: 'eye',
- },
- EyeSlash: {
- iconValue: 'eye.slash',
- },
- Clipboard: {
- iconValue: 'doc.on.doc',
- },
- Link: {
- iconValue: 'link',
- },
- Note: {
- iconValue: 'note.text',
- },
-};
diff --git a/ios/BlueWallet.xcodeproj/project.pbxproj b/ios/BlueWallet.xcodeproj/project.pbxproj
index 2f33593d4..b9edaf205 100644
--- a/ios/BlueWallet.xcodeproj/project.pbxproj
+++ b/ios/BlueWallet.xcodeproj/project.pbxproj
@@ -169,8 +169,19 @@
B4D0B2662C1DEB7F006B6B1B /* ReceiveInterfaceMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D0B2652C1DEB7F006B6B1B /* ReceiveInterfaceMode.swift */; };
B4D0B2682C1DED67006B6B1B /* ReceiveMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4D0B2672C1DED67006B6B1B /* ReceiveMethod.swift */; };
B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
+ B4EFF73B2C3F6C5E0095D655 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4EFF73A2C3F6C5E0095D655 /* MockData.swift */; };
+ B4EFF73C2C3F6C5E0095D655 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4EFF73A2C3F6C5E0095D655 /* MockData.swift */; };
+ B4EFF73D2C3F6C6C0095D655 /* MarketData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B44033E82BCC371A00162242 /* MarketData.swift */; };
+ B4EFF7412C3F6C960095D655 /* PriceWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6CA4BC255872E3009312A5 /* PriceWidget.swift */; };
+ B4EFF7422C3F6C990095D655 /* PriceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6CA5272558EC52009312A5 /* PriceView.swift */; };
+ B4EFF7432C3F6F650095D655 /* Currency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B44033C92BCC350A00162242 /* Currency.swift */; };
+ B4EFF7442C3F6F6A0095D655 /* FiatUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D2AA8072568B8F40090B089 /* FiatUnit.swift */; };
+ B4EFF7452C3F6FF30095D655 /* Placeholders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEB4BFA254FBA0E00E9F9AA /* Placeholders.swift */; };
+ B4EFF7462C3F6FF90095D655 /* WalletData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B44033E32BCC36FF00162242 /* WalletData.swift */; };
+ B4EFF7472C3F70010095D655 /* LatestTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B44033DC2BCC36C300162242 /* LatestTransaction.swift */; };
+ B4EFF7482C3F70090095D655 /* BitcoinUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = B44033BE2BCC32F800162242 /* BitcoinUnit.swift */; };
C59F90CE0D04D3E4BB39BC5D /* libPods-BlueWalletUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F02C2F7CA3591E4E0B06EBA /* libPods-BlueWalletUITests.a */; };
- C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
+ C978A716948AB7DEC5B6F677 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -461,6 +472,7 @@
B4D0B2652C1DEB7F006B6B1B /* ReceiveInterfaceMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiveInterfaceMode.swift; sourceTree = ""; };
B4D0B2672C1DED67006B6B1B /* ReceiveMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiveMethod.swift; sourceTree = ""; };
B4D3235A177F4580BA52F2F9 /* libRNCSlider.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCSlider.a; sourceTree = ""; };
+ B4EFF73A2C3F6C5E0095D655 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = ""; };
B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTQRCodeLocalImage.a; sourceTree = ""; };
B68F8552DD4428F64B11DCFB /* Pods-BlueWallet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BlueWallet.debug.xcconfig"; path = "Target Support Files/Pods-BlueWallet/Pods-BlueWallet.debug.xcconfig"; sourceTree = ""; };
B9D9B3A7B2CB4255876B67AF /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
@@ -485,7 +497,7 @@
files = (
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */,
764B49B1420D4AEB8109BF62 /* libsqlite3.0.tbd in Frameworks */,
- C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */,
+ C978A716948AB7DEC5B6F677 /* (null) in Frameworks */,
773E382FE62E836172AAB98B /* libPods-BlueWallet.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -550,6 +562,7 @@
children = (
00E356F01AD99517003FC87E /* Supporting Files */,
B49038D82B8FBAD300A8164A /* BlueWalletUITest.swift */,
+ B4EFF73A2C3F6C5E0095D655 /* MockData.swift */,
);
path = BlueWalletTests;
sourceTree = "";
@@ -1627,6 +1640,7 @@
B44033DB2BCC369B00162242 /* Colors.swift in Sources */,
B40D4E632258425500428FCC /* ReceiveInterfaceController.swift in Sources */,
B43D0378225847C500FBAA95 /* WalletGradient.swift in Sources */,
+ B4EFF73B2C3F6C5E0095D655 /* MockData.swift in Sources */,
B44033C02BCC32F800162242 /* BitcoinUnit.swift in Sources */,
B44033E52BCC36FF00162242 /* WalletData.swift in Sources */,
B44033EF2BCC374500162242 /* Numeric+abbreviated.swift in Sources */,
@@ -1645,6 +1659,16 @@
buildActionMask = 2147483647;
files = (
B49038D92B8FBAD300A8164A /* BlueWalletUITest.swift in Sources */,
+ B4EFF7472C3F70010095D655 /* LatestTransaction.swift in Sources */,
+ B4EFF7422C3F6C990095D655 /* PriceView.swift in Sources */,
+ B4EFF7482C3F70090095D655 /* BitcoinUnit.swift in Sources */,
+ B4EFF73D2C3F6C6C0095D655 /* MarketData.swift in Sources */,
+ B4EFF73C2C3F6C5E0095D655 /* MockData.swift in Sources */,
+ B4EFF7432C3F6F650095D655 /* Currency.swift in Sources */,
+ B4EFF7412C3F6C960095D655 /* PriceWidget.swift in Sources */,
+ B4EFF7462C3F6FF90095D655 /* WalletData.swift in Sources */,
+ B4EFF7452C3F6FF30095D655 /* Placeholders.swift in Sources */,
+ B4EFF7442C3F6F6A0095D655 /* FiatUnit.swift in Sources */,
B47B21EC2B2128B8001F6690 /* BlueWalletUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
diff --git a/ios/BlueWalletTests/MockData.swift b/ios/BlueWalletTests/MockData.swift
new file mode 100644
index 000000000..8460b865a
--- /dev/null
+++ b/ios/BlueWalletTests/MockData.swift
@@ -0,0 +1,15 @@
+//
+// MockData.swift
+// BlueWallet
+//
+// Created by Marcos Rodriguez on 7/10/24.
+// Copyright © 2024 BlueWallet. All rights reserved.
+//
+
+import Foundation
+
+struct MockData {
+ static let currentMarketData = MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2023-01-01T00:00:00+00:00")
+ static let previousMarketData = MarketData(nextBlock: "", sats: "", price: "$9,000", rate: 9000, dateString: "2022-12-31T00:00:00+00:00")
+ static let noChangeMarketData = MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2023-01-01T00:00:00+00:00")
+}
diff --git a/ios/Widgets/Shared/Views/PriceView.swift b/ios/Widgets/Shared/Views/PriceView.swift
index 6f24b1a8b..04502f5e2 100644
--- a/ios/Widgets/Shared/Views/PriceView.swift
+++ b/ios/Widgets/Shared/Views/PriceView.swift
@@ -15,9 +15,13 @@ struct PriceView: View {
var body: some View {
switch entry.family {
case .accessoryInline, .accessoryCircular, .accessoryRectangular:
- wrappedView(for: getView(for: entry.family), family: entry.family)
+ if #available(iOSApplicationExtension 16.0, *) {
+ wrappedView(for: getView(for: entry.family), family: entry.family)
+ } else {
+ getView(for: entry.family)
+ }
default:
- defaultView.background(Color.widgetBackground)
+ defaultView.background(Color(UIColor.systemBackground))
}
}
@@ -40,7 +44,7 @@ struct PriceView: View {
ZStack {
if family == .accessoryRectangular {
AccessoryWidgetBackground()
- .background(Color(.systemBackground))
+ .background(Color(UIColor.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 10))
} else {
AccessoryWidgetBackground()
@@ -118,26 +122,26 @@ struct PriceView: View {
}
.padding(.all, 8)
.frame(maxWidth: .infinity, alignment: .leading)
- .background(Color(.systemBackground))
+ .background(Color(UIColor.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 10))
}
private var defaultView: some View {
VStack(alignment: .trailing, spacing: nil, content: {
- Text("Last Updated").font(Font.system(size: 11, weight: .regular, design: .default)).foregroundColor(.textColorLightGray)
+ Text("Last Updated").font(Font.system(size: 11, weight: .regular)).foregroundColor(Color(UIColor.lightGray))
HStack(alignment: .lastTextBaseline, spacing: nil, content: {
- Text(entry.currentMarketData?.formattedDate ?? "").lineLimit(1).foregroundColor(.textColor).font(Font.system(size:13, weight: .regular, design: .default)).minimumScaleFactor(0.01).transition(.opacity)
+ Text(entry.currentMarketData?.formattedDate ?? "").lineLimit(1).foregroundColor(.primary).font(Font.system(size: 13, weight: .regular)).minimumScaleFactor(0.01).transition(.opacity)
})
Spacer()
VStack(alignment: .trailing, spacing: 16, content: {
HStack(alignment: .lastTextBaseline, spacing: nil, content: {
- Text(entry.currentMarketData?.price ?? "").lineLimit(1).foregroundColor(.textColor).font(Font.system(size:28, weight: .bold, design: .default)).minimumScaleFactor(0.01).transition(.opacity)
+ Text(entry.currentMarketData?.price ?? "").lineLimit(1).foregroundColor(.primary).font(Font.system(size: 28, weight: .bold)).minimumScaleFactor(0.01).transition(.opacity)
})
if let previousMarketDataPrice = entry.previousMarketData?.price, let currentMarketDataRate = entry.currentMarketData?.rate, let previousMarketDataRate = entry.previousMarketData?.rate, previousMarketDataRate > 0, currentMarketDataRate != previousMarketDataRate {
HStack(alignment: .lastTextBaseline, spacing: nil, content: {
- Image(systemName: currentMarketDataRate > previousMarketDataRate ? "arrow.up" : "arrow.down")
- Text("from").lineLimit(1).foregroundColor(.textColor).font(Font.system(size:13, weight: .regular, design: .default)).minimumScaleFactor(0.01)
- Text(previousMarketDataPrice).lineLimit(1).foregroundColor(.textColor).font(Font.system(size:13, weight: .regular, design: .default)).minimumScaleFactor(0.01)
+ Image(systemName: currentMarketDataRate > previousMarketDataRate ? "arrow.up" : "arrow.down")
+ Text("from").lineLimit(1).foregroundColor(.primary).font(Font.system(size: 13, weight: .regular)).minimumScaleFactor(0.01)
+ Text(previousMarketDataPrice).lineLimit(1).foregroundColor(.primary).font(Font.system(size: 13, weight: .regular)).minimumScaleFactor(0.01)
}).transition(.slide)
}
})
@@ -178,7 +182,7 @@ struct PriceView_Previews: PreviewProvider {
.previewContext(WidgetPreviewContext(family: .accessoryInline))
PriceView(entry: PriceWidgetEntry(date: Date(), family: .accessoryRectangular, currentMarketData: MarketData(nextBlock: "", sats: "", price: "$10,000", rate: 10000, dateString: "2019-09-18T17:27:00+00:00"), previousMarketData: emptyMarketData))
.previewContext(WidgetPreviewContext(family: .accessoryRectangular))
- }
- }
- }
- }
+ }
+ }
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index e5deece08..4a9248a61 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -46,7 +46,7 @@
"coinselect": "3.1.13",
"crypto-js": "4.2.0",
"dayjs": "1.11.11",
- "detox": "20.25.0",
+ "detox": "20.25.1",
"ecpair": "2.0.1",
"ecurve": "1.0.6",
"electrum-client": "github:BlueWallet/rn-electrum-client#1bfe3cc",
@@ -9650,9 +9650,9 @@
}
},
"node_modules/detox": {
- "version": "20.25.0",
- "resolved": "https://registry.npmjs.org/detox/-/detox-20.25.0.tgz",
- "integrity": "sha512-PI4JR7XZQM/VPLLscf07WHdjHu/n9zRRKajEFrgIIpaZK2txDJZnv1rqaIC+4yFGEvCVSbxYQFrjjihLERDWrA==",
+ "version": "20.25.1",
+ "resolved": "https://registry.npmjs.org/detox/-/detox-20.25.1.tgz",
+ "integrity": "sha512-l+VhhtwfIR8DR4Htvo3KUIx1VPQkYanwjxsKUlXrOM60e47GB45U87la3VYVpFEvUQ5liD46tWWDkY0fkhoOOw==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -29881,9 +29881,9 @@
"dev": true
},
"detox": {
- "version": "20.25.0",
- "resolved": "https://registry.npmjs.org/detox/-/detox-20.25.0.tgz",
- "integrity": "sha512-PI4JR7XZQM/VPLLscf07WHdjHu/n9zRRKajEFrgIIpaZK2txDJZnv1rqaIC+4yFGEvCVSbxYQFrjjihLERDWrA==",
+ "version": "20.25.1",
+ "resolved": "https://registry.npmjs.org/detox/-/detox-20.25.1.tgz",
+ "integrity": "sha512-l+VhhtwfIR8DR4Htvo3KUIx1VPQkYanwjxsKUlXrOM60e47GB45U87la3VYVpFEvUQ5liD46tWWDkY0fkhoOOw==",
"requires": {
"ajv": "^8.6.3",
"bunyan": "^1.8.12",
diff --git a/package.json b/package.json
index a9ec3153f..21cc5c967 100644
--- a/package.json
+++ b/package.json
@@ -64,7 +64,7 @@
"e2e:release-build": "detox build -c android.release",
"e2e:release-test": "detox test -c android.release",
"tslint": "tsc",
- "lint": " npm run tslint && node scripts/find-unused-loc.js && eslint --ext .js,.ts,.tsx '*.@(js|ts|tsx)' screen 'blue_modules/*.@(js|ts|tsx)' class models loc tests components navigation",
+ "lint": " npm run tslint && node scripts/find-unused-loc.js && eslint --ext .js,.ts,.tsx '*.@(js|ts|tsx)' screen 'blue_modules/*.@(js|ts|tsx)' class models loc tests components navigation typings",
"lint:fix": "npm run lint -- --fix",
"lint:quickfix": "git status --porcelain | grep -v '\\.json' | grep -E '\\.js|\\.ts' --color=never | awk '{print $2}' | xargs eslint --fix; exit 0",
"unit": "jest -b -i tests/unit/*"
@@ -130,7 +130,7 @@
"coinselect": "3.1.13",
"crypto-js": "4.2.0",
"dayjs": "1.11.11",
- "detox": "20.25.0",
+ "detox": "20.25.1",
"ecpair": "2.0.1",
"ecurve": "1.0.6",
"electrum-client": "github:BlueWallet/rn-electrum-client#1bfe3cc",
diff --git a/scripts/find-unused-loc.js b/scripts/find-unused-loc.js
index 1ad5fa54d..6a1de9eae 100644
--- a/scripts/find-unused-loc.js
+++ b/scripts/find-unused-loc.js
@@ -2,7 +2,7 @@ const fs = require('fs');
const path = require('path');
const mainLocFile = './loc/en.json';
-const dirsToInterate = ['components', 'screen', 'blue_modules', 'class', 'hooks', 'helpers', 'navigation'];
+const dirsToInterate = ['components', 'screen', 'blue_modules', 'class', 'hooks', 'helpers', 'navigation', 'typings'];
const addFiles = ['BlueComponents.js', 'App.tsx', 'navigation/index.tsx'];
const allowedLocPrefixes = ['loc.lnurl_auth', 'loc.units'];
diff --git a/typings/CommonToolTipActions.ts b/typings/CommonToolTipActions.ts
new file mode 100644
index 000000000..d1f816ddc
--- /dev/null
+++ b/typings/CommonToolTipActions.ts
@@ -0,0 +1,61 @@
+import loc from '../loc';
+
+const keys = {
+ CopyTXID: 'copyTX_ID',
+ CopyBlockExplorerLink: 'copy_blockExplorer',
+ ExpandNote: 'expandNote',
+ OpenInBlockExplorer: 'open_in_blockExplorer',
+ CopyAmount: 'copyAmount',
+ CopyNote: 'copyNote',
+};
+
+const icons = {
+ Eye: {
+ iconValue: 'eye',
+ },
+ EyeSlash: {
+ iconValue: 'eye.slash',
+ },
+ Clipboard: {
+ iconValue: 'doc.on.doc',
+ },
+ Link: {
+ iconValue: 'link',
+ },
+ Note: {
+ iconValue: 'note.text',
+ },
+};
+
+export const CommonToolTipActions = {
+ CopyTXID: {
+ id: keys.CopyTXID,
+ text: loc.transactions.details_copy_txid,
+ icon: icons.Clipboard,
+ },
+ CopyBlockExplorerLink: {
+ id: keys.CopyBlockExplorerLink,
+ text: loc.transactions.details_copy_block_explorer_link,
+ icon: icons.Clipboard,
+ },
+ OpenInBlockExplorer: {
+ id: keys.OpenInBlockExplorer,
+ text: loc.transactions.details_show_in_block_explorer,
+ icon: icons.Link,
+ },
+ ExpandNote: {
+ id: keys.ExpandNote,
+ text: loc.transactions.expand_note,
+ icon: icons.Note,
+ },
+ CopyAmount: {
+ id: keys.CopyAmount,
+ text: loc.transactions.details_copy_amount,
+ icon: icons.Clipboard,
+ },
+ CopyNote: {
+ id: keys.CopyNote,
+ text: loc.transactions.details_copy_note,
+ icon: icons.Clipboard,
+ },
+};