mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2024-11-19 09:50:15 +01:00
Merge branch 'master' into modal
This commit is contained in:
commit
dc0fa26395
@ -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)
|
||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle")
|
||||
apply plugin: 'org.jetbrains.kotlin.android'; applyNativeModulesAppBuildGradle(project)
|
@ -23,6 +23,7 @@
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.ACTION_SCREEN_OFF"/>
|
||||
<uses-permission android:name="android.permission.ACTION_SCREEN_ON"/>
|
||||
|
||||
<application
|
||||
android:name=".MainApplication"
|
||||
android:label="@string/app_name"
|
||||
@ -62,16 +63,16 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".BitcoinPriceWidget" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||
<action android:name="android.intent.action.SCREEN_ON"/>
|
||||
<action android:name="android.intent.action.SCREEN_OFF"/>
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/bitcoin_price_widget_info"/>
|
||||
</receiver>
|
||||
<receiver android:name=".BitcoinPriceWidget" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
|
||||
<action android:name="android.intent.action.SCREEN_ON"/>
|
||||
<action android:name="android.intent.action.SCREEN_OFF"/>
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/bitcoin_price_widget_info"/>
|
||||
</receiver>
|
||||
|
||||
<service
|
||||
android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
|
||||
|
@ -1,125 +0,0 @@
|
||||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.app.Application;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.PowerManager;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BitcoinPriceWidget extends AppWidgetProvider {
|
||||
|
||||
private static final String TAG = "BitcoinPriceWidget";
|
||||
private static final String ACTION_UPDATE = "io.bluewallet.bluewallet.UPDATE_WIDGET";
|
||||
private static final long UPDATE_INTERVAL_MINUTES = 15; // Update interval in minutes
|
||||
private static final int MAX_RETRIES = 3;
|
||||
|
||||
private static PowerManager.WakeLock wakeLock;
|
||||
private static int retryCount = 0;
|
||||
private static boolean isScreenOn = true;
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
super.onEnabled(context);
|
||||
registerScreenReceiver(context);
|
||||
schedulePeriodicUpdates(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(Context context) {
|
||||
super.onDisabled(context);
|
||||
unregisterScreenReceiver(context);
|
||||
WorkManager.getInstance(context).cancelUniqueWork("UpdateWidgetWork");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds);
|
||||
if (isScreenOn) {
|
||||
scheduleWork(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void schedulePeriodicUpdates(Context context) {
|
||||
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(WidgetUpdateWorker.class,
|
||||
UPDATE_INTERVAL_MINUTES, TimeUnit.MINUTES)
|
||||
.setInitialDelay(UPDATE_INTERVAL_MINUTES, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
|
||||
"UpdateWidgetWork",
|
||||
ExistingPeriodicWorkPolicy.REPLACE,
|
||||
workRequest
|
||||
);
|
||||
}
|
||||
|
||||
private void registerScreenReceiver(Context context) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
context.getApplicationContext().registerReceiver(screenReceiver, filter);
|
||||
}
|
||||
|
||||
private void unregisterScreenReceiver(Context context) {
|
||||
context.getApplicationContext().unregisterReceiver(screenReceiver);
|
||||
}
|
||||
|
||||
private final BroadcastReceiver screenReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction() != null) {
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null) {
|
||||
switch (intent.getAction()) {
|
||||
case Intent.ACTION_SCREEN_ON:
|
||||
isScreenOn = true;
|
||||
Log.d(TAG, "Screen ON");
|
||||
acquireWakeLock(context);
|
||||
scheduleWork(context);
|
||||
break;
|
||||
case Intent.ACTION_SCREEN_OFF:
|
||||
isScreenOn = false;
|
||||
Log.d(TAG, "Screen OFF");
|
||||
releaseWakeLock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void acquireWakeLock(Context context) {
|
||||
if (wakeLock == null) {
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null) {
|
||||
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||
wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseWakeLock() {
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
wakeLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleWork(Context context) {
|
||||
Log.d(TAG, "Scheduling work for widget update");
|
||||
WorkManager.getInstance(context).enqueue(WidgetUpdateWorker.createWorkRequest());
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package io.bluewallet.bluewallet
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.work.WorkManager
|
||||
|
||||
class BitcoinPriceWidget : AppWidgetProvider() {
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
Log.d("BitcoinPriceWidget", "onUpdate called")
|
||||
WidgetUpdateWorker.scheduleWork(context)
|
||||
}
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
super.onEnabled(context)
|
||||
Log.d("BitcoinPriceWidget", "onEnabled called")
|
||||
WidgetUpdateWorker.scheduleWork(context)
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
Log.d("BitcoinPriceWidget", "onDisabled called")
|
||||
WorkManager.getInstance(context).cancelUniqueWork(WidgetUpdateWorker.WORK_NAME)
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
super.onReceive(context, intent)
|
||||
Log.d("BitcoinPriceWidget", "onReceive called with action: ${intent.action}")
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Bundle;
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "BlueWallet";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(null);
|
||||
if (getResources().getBoolean(R.bool.portrait_only)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
||||
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
||||
* (aka React 18) with two boolean flags.
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new DefaultReactActivityDelegate(
|
||||
this,
|
||||
getMainComponentName(),
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
DefaultNewArchitectureEntryPoint.getFabricEnabled());
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package io.bluewallet.bluewallet
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import com.facebook.react.ReactActivity
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
||||
|
||||
class MainActivity : ReactActivity() {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
override fun getMainComponentName(): String {
|
||||
return "BlueWallet"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(null)
|
||||
if (resources.getBoolean(R.bool.portrait_only)) {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the [ReactActivityDelegate]. Here we use a util class [DefaultReactActivityDelegate]
|
||||
* which allows you to easily enable Fabric and Concurrent React (aka React 18) with two boolean flags.
|
||||
*/
|
||||
override fun createReactActivityDelegate(): ReactActivityDelegate {
|
||||
return DefaultReactActivityDelegate(
|
||||
this,
|
||||
mainComponentName,
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
DefaultNewArchitectureEntryPoint.fabricEnabled
|
||||
)
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
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 java.lang.reflect.InvocationTargetException;
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
||||
import java.util.List;
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MainApplication extends Application implements ReactApplication {
|
||||
|
||||
private final ReactNativeHost mReactNativeHost =
|
||||
new DefaultReactNativeHost(this) {
|
||||
@Override
|
||||
public boolean getUseDeveloperSupport() {
|
||||
return BuildConfig.DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> 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);
|
||||
|
||||
}
|
||||
}
|
@ -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<ReactPackage> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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<WidgetUpdateWorker>(
|
||||
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()
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
<resources>
|
||||
<string name="app_name">BlueWallet</string>
|
||||
<string name="loading">Loading...</string>
|
||||
<string name="last_updated">Last Updated</string>
|
||||
<string name="from">From</string>
|
||||
</resources>
|
||||
|
@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/widget_layout"
|
||||
android:minWidth="130dp"
|
||||
android:minHeight="40dp"
|
||||
android:minWidth="160dp"
|
||||
android:minHeight="80dp"
|
||||
android:updatePeriodMillis="0"
|
||||
android:widgetCategory="home_screen"
|
||||
android:previewImage="@drawable/widget_preview"
|
||||
android:resizeMode="none" />
|
||||
android:resizeMode="none"
|
||||
/>
|
||||
|
@ -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()
|
||||
|
@ -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<TransactionListItemProps> = 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<TransactionListItemProps> = 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<TransactionListItemProps> = React.mem
|
||||
</ToolTipMenu>
|
||||
);
|
||||
});
|
||||
|
||||
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',
|
||||
},
|
||||
};
|
||||
|
@ -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 = "<group>"; };
|
||||
B4D0B2672C1DED67006B6B1B /* ReceiveMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiveMethod.swift; sourceTree = "<group>"; };
|
||||
B4D3235A177F4580BA52F2F9 /* libRNCSlider.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCSlider.a; sourceTree = "<group>"; };
|
||||
B4EFF73A2C3F6C5E0095D655 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = "<group>"; };
|
||||
B642AFB13483418CAB6FF25E /* libRCTQRCodeLocalImage.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTQRCodeLocalImage.a; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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 = "<group>";
|
||||
@ -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;
|
||||
|
15
ios/BlueWalletTests/MockData.swift
Normal file
15
ios/BlueWalletTests/MockData.swift
Normal file
@ -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")
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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'];
|
||||
|
||||
|
61
typings/CommonToolTipActions.ts
Normal file
61
typings/CommonToolTipActions.ts
Normal file
@ -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,
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue
Block a user