Compare commits
44
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d53ca8d658 | ||
|
|
ee1f93defb | ||
|
|
3665fe26da | ||
|
|
3ec7209fcc | ||
|
|
195826a7f4 | ||
|
|
5bb4f3632a | ||
|
|
4fe62553ca | ||
|
|
cb586c8676 | ||
|
|
fd50e4281c | ||
|
|
dcbb080217 | ||
|
|
97d1b3faa1 | ||
|
|
9c322397a2 | ||
|
|
ca2b1d5c8e | ||
|
|
9a4bc7e250 | ||
|
|
c6dbfb6244 | ||
|
|
6a7a8ee813 | ||
|
|
826ab6625e | ||
|
|
a9ec2ca7ba | ||
|
|
1e11edc442 | ||
|
|
bf8461f007 | ||
|
|
5926426672 | ||
|
|
e871357b67 | ||
|
|
9f5e53cc63 | ||
|
|
61c0650cec | ||
|
|
7cc570b6d0 | ||
|
|
e609e2ff6a | ||
|
|
f8cdb3bd7b | ||
|
|
40d8cf3121 | ||
|
|
c198017513 | ||
|
|
fe9a4eac59 | ||
|
|
d934dc26ef | ||
|
|
ed3cb42572 | ||
|
|
8a4302c85a | ||
|
|
c888c512a2 | ||
|
|
72bf8a8702 | ||
|
|
49e9b4212b | ||
|
|
19aa78cc81 | ||
|
|
02b8d2140a | ||
|
|
72d8c4446f | ||
|
|
09bf6673cf | ||
|
|
455a739318 | ||
|
|
ac07109e86 | ||
|
|
248996ce6f | ||
|
|
a2bf23cc12 |
@@ -282,11 +282,12 @@ SIMULATOR=iPhone 13
|
||||
# TODO: fix IOS_STATUS_GO_TARGETS to be either amd64 or arm64 when RN is upgraded
|
||||
run-ios: export TARGET := ios
|
||||
run-ios: export IOS_STATUS_GO_TARGETS := ios/arm64;iossimulator/amd64
|
||||
run-ios: export XCBeautify=$(shell which xcbeautify) # for react-native-cli to pick this up and to auto format output
|
||||
run-ios: ##@run Build iOS app and start it in a simulator/device
|
||||
ifneq ("$(SIMULATOR)", "")
|
||||
npx react-native run-ios --simulator="$(SIMULATOR)" | xcbeautify
|
||||
npx react-native run-ios --simulator="$(SIMULATOR)"
|
||||
else
|
||||
npx react-native run-ios | xcbeautify
|
||||
npx react-native run-ios
|
||||
endif
|
||||
|
||||
show-ios-devices: ##@other shows connected ios device and its name
|
||||
@@ -386,9 +387,8 @@ component-test-watch: export COMPONENT_TEST := true
|
||||
component-test-watch: export BABEL_ENV := test
|
||||
component-test-watch: export JEST_USE_SILENT_REPORTER := false
|
||||
component-test-watch: ##@ Watch tests and re-run no changes to cljs files
|
||||
@@scripts/check-metro-shadow-process.sh
|
||||
@scripts/check-metro-shadow-process.sh
|
||||
rm -rf ./component-spec
|
||||
yarn install
|
||||
nodemon --exec 'yarn shadow-cljs compile component-test && jest --config=test/jest/jest.config.js --testEnvironment node ' -e cljs
|
||||
|
||||
component-test: export TARGET := clojure
|
||||
@@ -398,7 +398,6 @@ component-test: export JEST_USE_SILENT_REPORTER := false
|
||||
component-test: ##@test Run component tests once in NodeJS
|
||||
@scripts/check-metro-shadow-process.sh
|
||||
rm -rf ./component-spec
|
||||
yarn install
|
||||
yarn shadow-cljs compile component-test && \
|
||||
jest --clearCache && jest --config=test/jest/jest.config.js --testEnvironment node
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ buildToolsVersion=33.0.0
|
||||
supportLibVersion=28.0.0
|
||||
# This should match version from nix/pkgs/aapt2/default.nix
|
||||
gradlePluginVersion=7.4.2
|
||||
kotlinPluginVersion=1.7.22
|
||||
kotlinPluginVersion=1.9.0
|
||||
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
package im.status.ethereum.pushnotifications;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.app.Service;
|
||||
import android.os.IBinder;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import android.os.Build;
|
||||
import im.status.ethereum.module.R;
|
||||
|
||||
public class ForegroundService extends Service {
|
||||
private static final String CHANNEL_ID = "status-service";
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent i, int flags, int startId) {
|
||||
// NOTE: recent versions of Android require the service to display
|
||||
// a sticky notification to inform the user that the service is running
|
||||
Context context = getApplicationContext();
|
||||
// Create the NotificationChannel, but only on API 26+ because
|
||||
// the NotificationChannel class is new and not in the support library
|
||||
|
||||
Intent intent = null;
|
||||
String notificationContentText = null;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManager notificationManager =
|
||||
context.getSystemService(NotificationManager.class);
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
|
||||
context.getResources().getString(R.string.status_service),
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setShowBadge(false);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
|
||||
// Create intent that takes the user to the notification channel settings so they can hide it
|
||||
|
||||
intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, CHANNEL_ID);
|
||||
|
||||
notificationContentText = context.getResources().getString(R.string.tap_to_hide_notification);
|
||||
|
||||
} else {
|
||||
|
||||
// For older versions of android intent takes the user to the Status app
|
||||
|
||||
Class intentClass;
|
||||
String packageName = context.getPackageName();
|
||||
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
String className = launchIntent.getComponent().getClassName();
|
||||
try {
|
||||
intentClass = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
|
||||
intent = new Intent(context, intentClass);
|
||||
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
||||
notificationContentText = context.getResources().getString(R.string.keep_status_running);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE);
|
||||
Intent stopIntent = new Intent(PushNotificationHelper.ACTION_TAP_STOP);
|
||||
PendingIntent stopPendingIntent = PendingIntent.getBroadcast(context, 0, stopIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_stat_notify_status)
|
||||
.setContentTitle(context.getResources().getString(R.string.background_service_opened))
|
||||
.setContentText(notificationContentText)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setNumber(0)
|
||||
.addAction(R.drawable.ic_stat_notify_status,
|
||||
context.getResources().getString(R.string.stop),
|
||||
stopPendingIntent)
|
||||
.build();
|
||||
// the id of the foreground notification MUST NOT be 0
|
||||
startForeground(1, notification);
|
||||
return START_STICKY;
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
package im.status.ethereum.pushnotifications
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.app.Service
|
||||
import android.os.IBinder
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.provider.Settings
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import android.os.Build
|
||||
import im.status.ethereum.module.R
|
||||
|
||||
class ForegroundService : Service() {
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onStartCommand(i: Intent?, flags: Int, startId: Int): Int {
|
||||
// NOTE: recent versions of Android require the service to display
|
||||
// a sticky notification to inform the user that the service is running
|
||||
val context: Context = getApplicationContext()
|
||||
// Create the NotificationChannel, but only on API 26+ because
|
||||
// the NotificationChannel class is new and not in the support library
|
||||
var intent: Intent? = null
|
||||
var notificationContentText: String? = null
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val notificationManager: NotificationManager = context.getSystemService(NotificationManager::class.java)
|
||||
val channel = NotificationChannel(CHANNEL_ID,
|
||||
context.getResources().getString(R.string.status_service),
|
||||
NotificationManager.IMPORTANCE_HIGH)
|
||||
channel.setShowBadge(false)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
|
||||
// Create intent that takes the user to the notification channel settings so they can hide it
|
||||
intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, CHANNEL_ID)
|
||||
notificationContentText = context.getResources().getString(R.string.tap_to_hide_notification)
|
||||
} else {
|
||||
// For older versions of android intent takes the user to the Status app
|
||||
val intentClass: java.lang.Class<*>
|
||||
val packageName: String = context.getPackageName()
|
||||
val launchIntent: Intent? = context.getPackageManager().getLaunchIntentForPackage(packageName)
|
||||
val className: String? = launchIntent?.getComponent()?.getClassName()
|
||||
if (className == null) {
|
||||
return 0
|
||||
}
|
||||
intentClass = try {
|
||||
java.lang.Class.forName(className)
|
||||
} catch (e: java.lang.ClassNotFoundException) {
|
||||
e.printStackTrace()
|
||||
return 0
|
||||
}
|
||||
intent = Intent(context, intentClass)
|
||||
intent.addCategory(Intent.CATEGORY_BROWSABLE)
|
||||
intent.setAction(Intent.ACTION_VIEW)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
notificationContentText = context.getResources().getString(R.string.keep_status_running)
|
||||
}
|
||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)
|
||||
val stopIntent = Intent(PushNotificationHelper.ACTION_TAP_STOP)
|
||||
val stopPendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0, stopIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE)
|
||||
val notification: Notification = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_stat_notify_status)
|
||||
.setContentTitle(context.getResources().getString(R.string.background_service_opened))
|
||||
.setContentText(notificationContentText)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setNumber(0)
|
||||
.addAction(R.drawable.ic_stat_notify_status,
|
||||
context.getResources().getString(R.string.stop),
|
||||
stopPendingIntent)
|
||||
.build()
|
||||
// the id of the foreground notification MUST NOT be 0
|
||||
startForeground(1, notification)
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val CHANNEL_ID = "status-service"
|
||||
}
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
package im.status.ethereum.pushnotifications;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import com.facebook.react.bridge.ActivityEventListener;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.Callback;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import android.util.Log;
|
||||
|
||||
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery;
|
||||
|
||||
public class PushNotification extends ReactContextBaseJavaModule implements ActivityEventListener {
|
||||
public static final String LOG_TAG = "PushNotification";
|
||||
|
||||
private final SecureRandom mRandomNumberGenerator = new SecureRandom();
|
||||
private PushNotificationHelper pushNotificationHelper;
|
||||
private PushNotificationJsDelivery delivery;
|
||||
private ReactApplicationContext reactContext;
|
||||
private boolean started;
|
||||
|
||||
public PushNotification(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
this.reactContext = reactContext;
|
||||
reactContext.addActivityEventListener(this);
|
||||
Application applicationContext = (Application) reactContext.getApplicationContext();
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
pushNotificationHelper = new PushNotificationHelper(applicationContext, intentFilter);
|
||||
|
||||
delivery = new PushNotificationJsDelivery(reactContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "PushNotification";
|
||||
}
|
||||
|
||||
// removed @Override temporarily just to get it working on different versions of RN
|
||||
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
|
||||
onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
// removed @Override temporarily just to get it working on different versions of RN
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
// Ignored, required to implement ActivityEventListener for RN 0.33
|
||||
}
|
||||
|
||||
private Bundle getBundleFromIntent(Intent intent) {
|
||||
Bundle bundle = null;
|
||||
if (intent.hasExtra("notification")) {
|
||||
bundle = intent.getBundleExtra("notification");
|
||||
} else if (intent.hasExtra("google.message_id")) {
|
||||
bundle = new Bundle();
|
||||
|
||||
bundle.putBundle("data", intent.getExtras());
|
||||
}
|
||||
|
||||
if(null != bundle && !bundle.getBoolean("foreground", false) && !bundle.containsKey("userInteraction")) {
|
||||
bundle.putBoolean("userInteraction", true);
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewIntent(Intent intent) {
|
||||
Bundle bundle = this.getBundleFromIntent(intent);
|
||||
if (bundle != null) {
|
||||
delivery.notifyNotification(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
/**
|
||||
* Creates a channel if it does not already exist. Returns whether the channel was created.
|
||||
*/
|
||||
public void createChannel(ReadableMap channelInfo, Callback callback) {
|
||||
boolean created = pushNotificationHelper.createChannel(channelInfo);
|
||||
|
||||
if(callback != null) {
|
||||
callback.invoke(created);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void presentLocalNotification(ReadableMap details) {
|
||||
if (!this.started) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle bundle = Arguments.toBundle(details);
|
||||
// If notification ID is not provided by the user, generate one at random
|
||||
if (bundle.getString("id") == null) {
|
||||
bundle.putString("id", String.valueOf(mRandomNumberGenerator.nextInt()));
|
||||
}
|
||||
|
||||
pushNotificationHelper.sendToNotificationCentre(bundle);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void clearMessageNotifications(String conversationId) {
|
||||
if (this.started) {
|
||||
pushNotificationHelper.clearMessageNotifications(conversationId);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void clearAllMessageNotifications() {
|
||||
pushNotificationHelper.clearAllMessageNotifications();
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void enableNotifications() {
|
||||
this.started = true;
|
||||
this.pushNotificationHelper.start();
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void disableNotifications() {
|
||||
if (this.started) {
|
||||
this.started = false;
|
||||
this.pushNotificationHelper.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package im.status.ethereum.pushnotifications
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.app.NotificationManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.facebook.react.bridge.ActivityEventListener
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.Callback
|
||||
import com.facebook.react.bridge.Promise
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||
import com.facebook.react.bridge.ReactMethod
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.facebook.react.bridge.WritableArray
|
||||
import com.facebook.react.bridge.ReadableMap
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
import android.util.Log
|
||||
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery
|
||||
|
||||
class PushNotification(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext), ActivityEventListener {
|
||||
companion object {
|
||||
const val LOG_TAG = "PushNotification"
|
||||
}
|
||||
|
||||
private val mRandomNumberGenerator: java.security.SecureRandom = java.security.SecureRandom()
|
||||
private val pushNotificationHelper: PushNotificationHelper
|
||||
private val delivery: PushNotificationJsDelivery
|
||||
private val reactContext: ReactApplicationContext
|
||||
private var started = false
|
||||
|
||||
init {
|
||||
this.reactContext = reactContext
|
||||
reactContext.addActivityEventListener(this)
|
||||
val applicationContext: Application = reactContext.getApplicationContext() as Application
|
||||
val intentFilter = IntentFilter()
|
||||
pushNotificationHelper = PushNotificationHelper(applicationContext, intentFilter)
|
||||
delivery = PushNotificationJsDelivery(reactContext)
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
return "PushNotification"
|
||||
}
|
||||
|
||||
// removed @Override temporarily just to get it working on different versions of RN
|
||||
override fun onActivityResult(activity: Activity?, requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
// removed @Override temporarily just to get it working on different versions of RN
|
||||
fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
// Ignored, required to implement ActivityEventListener for RN 0.33
|
||||
}
|
||||
|
||||
private fun getBundleFromIntent(intent: Intent): Bundle? {
|
||||
var bundle: Bundle? = null
|
||||
if (intent.hasExtra("notification")) {
|
||||
bundle = intent.getBundleExtra("notification")
|
||||
} else if (intent.hasExtra("google.message_id")) {
|
||||
bundle = Bundle()
|
||||
bundle.putBundle("data", intent.getExtras())
|
||||
}
|
||||
if (null != bundle && !bundle.getBoolean("foreground", false) && !bundle.containsKey("userInteraction")) {
|
||||
bundle.putBoolean("userInteraction", true)
|
||||
}
|
||||
return bundle
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
val bundle: Bundle? = getBundleFromIntent(intent)
|
||||
if (bundle != null) {
|
||||
delivery.notifyNotification(bundle)
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a channel if it does not already exist. Returns whether the channel was created.
|
||||
@ReactMethod
|
||||
fun createChannel(channelInfo: ReadableMap?, callback: Callback?) {
|
||||
if (channelInfo == null) {
|
||||
return
|
||||
}
|
||||
val created: Boolean = pushNotificationHelper.createChannel(channelInfo)
|
||||
if (callback != null) {
|
||||
callback.invoke(created)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun presentLocalNotification(details: ReadableMap?) {
|
||||
if (!started) {
|
||||
return
|
||||
}
|
||||
val bundle: Bundle? = Arguments.toBundle(details)
|
||||
if (bundle == null) {
|
||||
return
|
||||
}
|
||||
// If notification ID is not provided by the user, generate one at random
|
||||
if (bundle.getString("id") == null) {
|
||||
bundle.putString("id", mRandomNumberGenerator.nextInt().toString())
|
||||
}
|
||||
pushNotificationHelper.sendToNotificationCentre(bundle)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun clearMessageNotifications(conversationId: String?) {
|
||||
if (started && conversationId != null) {
|
||||
pushNotificationHelper.clearMessageNotifications(conversationId)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun clearAllMessageNotifications() {
|
||||
pushNotificationHelper.clearAllMessageNotifications()
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun enableNotifications() {
|
||||
started = true
|
||||
pushNotificationHelper.start()
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun disableNotifications() {
|
||||
if (started) {
|
||||
started = false
|
||||
pushNotificationHelper.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
package im.status.ethereum.pushnotifications;
|
||||
|
||||
import android.os.Build;
|
||||
import android.app.Application;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
|
||||
|
||||
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery;
|
||||
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
|
||||
|
||||
public class PushNotificationActions extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(final Context context, Intent intent) {
|
||||
String intentActionPrefix = context.getPackageName() + ".ACTION_";
|
||||
|
||||
Log.i(LOG_TAG, "PushNotificationBootEventReceiver loading scheduled notifications");
|
||||
|
||||
if (null == intent.getAction() || !intent.getAction().startsWith(intentActionPrefix)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Bundle bundle = intent.getBundleExtra("notification");
|
||||
|
||||
// Dismiss the notification popup.
|
||||
NotificationManager manager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
|
||||
int notificationID = Integer.parseInt(bundle.getString("id"));
|
||||
|
||||
boolean autoCancel = bundle.getBoolean("autoCancel", true);
|
||||
|
||||
if(autoCancel) {
|
||||
if (bundle.containsKey("tag")) {
|
||||
String tag = bundle.getString("tag");
|
||||
manager.cancel(tag, notificationID);
|
||||
} else {
|
||||
manager.cancel(notificationID);
|
||||
}
|
||||
}
|
||||
|
||||
boolean invokeApp = bundle.getBoolean("invokeApp", true);
|
||||
|
||||
// Notify the action.
|
||||
if(invokeApp) {
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
PushNotificationHelper helper = new PushNotificationHelper((Application) context.getApplicationContext(), intentFilter);
|
||||
|
||||
helper.invokeApp(bundle);
|
||||
} else {
|
||||
|
||||
// We need to run this on the main thread, as the React code assumes that is true.
|
||||
// Namely, DevServerHelper constructs a Handler() without a Looper, which triggers:
|
||||
// "Can't create handler inside thread that has not called Looper.prepare()"
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.post(new Runnable() {
|
||||
public void run() {
|
||||
// Construct and load our normal React JS code bundle
|
||||
final ReactInstanceManager mReactInstanceManager = ((ReactApplication) context.getApplicationContext()).getReactNativeHost().getReactInstanceManager();
|
||||
ReactContext context = mReactInstanceManager.getCurrentReactContext();
|
||||
PushNotificationJsDelivery delivery = new PushNotificationJsDelivery(context);
|
||||
// If it's constructed, send a notification
|
||||
if (context != null) {
|
||||
delivery.notifyNotificationAction(bundle);
|
||||
} else {
|
||||
// Otherwise wait for construction, then send the notification
|
||||
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
|
||||
public void onReactContextInitialized(ReactContext context) {
|
||||
PushNotificationJsDelivery delivery = new PushNotificationJsDelivery(context);
|
||||
delivery.notifyNotificationAction(bundle);
|
||||
mReactInstanceManager.removeReactInstanceEventListener(this);
|
||||
}
|
||||
});
|
||||
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
|
||||
// Construct it in the background
|
||||
mReactInstanceManager.createReactContextInBackground();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
package im.status.ethereum.pushnotifications
|
||||
|
||||
import android.os.Build
|
||||
import android.app.Application
|
||||
import android.app.NotificationManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactInstanceManager
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import im.status.ethereum.pushnotifications.PushNotificationJsDelivery
|
||||
|
||||
class PushNotificationActions : BroadcastReceiver() {
|
||||
companion object {
|
||||
const val LOG_TAG = "PushNotification"
|
||||
}
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val intentActionPrefix: String = context.getPackageName() + ".ACTION_"
|
||||
Log.i(LOG_TAG, "PushNotificationBootEventReceiver loading scheduled notifications")
|
||||
var intentAction: String? = intent.getAction()
|
||||
if (intentAction == null || !intentAction.startsWith(intentActionPrefix)) {
|
||||
return
|
||||
}
|
||||
val bundle: Bundle? = intent.getBundleExtra("notification")
|
||||
if (bundle == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Dismiss the notification popup.
|
||||
val manager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
val notificationID: Int? = bundle.getString("id")?.toInt()
|
||||
val autoCancel: Boolean = bundle.getBoolean("autoCancel", true)
|
||||
if (notificationID != null && autoCancel) {
|
||||
if (bundle.containsKey("tag")) {
|
||||
val tag: String? = bundle.getString("tag")
|
||||
if (tag != null) {
|
||||
manager.cancel(tag, notificationID)
|
||||
}
|
||||
} else {
|
||||
manager.cancel(notificationID)
|
||||
}
|
||||
}
|
||||
val invokeApp: Boolean = bundle.getBoolean("invokeApp", true)
|
||||
|
||||
// Notify the action.
|
||||
if (invokeApp) {
|
||||
val intentFilter = IntentFilter()
|
||||
val helper = PushNotificationHelper(context.getApplicationContext() as Application, intentFilter)
|
||||
helper.invokeApp(bundle)
|
||||
} else {
|
||||
// We need to run this on the main thread, as the React code assumes that is true.
|
||||
// Namely, DevServerHelper constructs a Handler() without a Looper, which triggers:
|
||||
// "Can't create handler inside thread that has not called Looper.prepare()"
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
handler.post(object : java.lang.Runnable {
|
||||
override fun run() {
|
||||
// Construct and load our normal React JS code bundle
|
||||
val mReactInstanceManager: ReactInstanceManager = (context.getApplicationContext() as ReactApplication).getReactNativeHost().getReactInstanceManager()
|
||||
val context: ReactContext? = mReactInstanceManager.getCurrentReactContext()
|
||||
// If it's constructed, send a notification
|
||||
if (context != null) {
|
||||
val delivery = PushNotificationJsDelivery(context)
|
||||
delivery.notifyNotificationAction(bundle)
|
||||
} else {
|
||||
// Otherwise wait for construction, then send the notification
|
||||
mReactInstanceManager.addReactInstanceEventListener(object : ReactInstanceManager.ReactInstanceEventListener {
|
||||
override fun onReactContextInitialized(context: ReactContext) {
|
||||
val delivery = PushNotificationJsDelivery(context)
|
||||
delivery.notifyNotificationAction(bundle)
|
||||
mReactInstanceManager.removeReactInstanceEventListener(this)
|
||||
}
|
||||
})
|
||||
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
|
||||
// Construct it in the background
|
||||
mReactInstanceManager.createReactContextInBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
-886
@@ -1,886 +0,0 @@
|
||||
// https://github.com/zo0r/react-native-push-notification/blob/bedc8f646aab67d594f291449fbfa24e07b64fe8/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java Copy-Paste with removed firebase
|
||||
package im.status.ethereum.pushnotifications;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningAppProcessInfo;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.Application;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.util.Log;
|
||||
import android.util.Base64;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.Person;
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import im.status.ethereum.module.R;
|
||||
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
|
||||
|
||||
public class PushNotificationHelper {
|
||||
|
||||
private Context context;
|
||||
|
||||
private static final long DEFAULT_VIBRATION = 300L;
|
||||
private static final String CHANNEL_ID = "status-im-notifications";
|
||||
public static final String ACTION_DELETE_NOTIFICATION = "im.status.ethereum.module.DELETE_NOTIFICATION";
|
||||
public static final String ACTION_TAP_STOP = "im.status.ethereum.module.TAP_STOP";
|
||||
final int flag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE : PendingIntent.FLAG_CANCEL_CURRENT;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
|
||||
|
||||
private HashMap<String, Person> persons;
|
||||
private HashMap<String, StatusMessageGroup> messageGroups;
|
||||
|
||||
private IntentFilter intentFilter;
|
||||
|
||||
public PushNotificationHelper(Application context, IntentFilter intentFilter) {
|
||||
this.context = context;
|
||||
this.intentFilter = intentFilter;
|
||||
this.persons = new HashMap<String, Person>();
|
||||
this.messageGroups = new HashMap<String, StatusMessageGroup>();
|
||||
this.notificationManager = context.getSystemService(NotificationManager.class);
|
||||
this.registerBroadcastReceiver();
|
||||
}
|
||||
|
||||
public Intent getOpenAppIntent(String deepLink) {
|
||||
Class intentClass;
|
||||
String packageName = context.getPackageName();
|
||||
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
String className = launchIntent.getComponent().getClassName();
|
||||
try {
|
||||
intentClass = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
Intent intent = new Intent(context, intentClass);
|
||||
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
//NOTE: you might wonder, why the heck did he decide to set these flags in particular. Well,
|
||||
//the answer is a simple as it can get in the Android native development world. I noticed
|
||||
//that my initial setup was opening the app but wasn't triggering any events on the js side, like
|
||||
//the links do from the browser. So I compared both intents and noticed that the link from
|
||||
//the browser produces an intent with the flag 0x14000000. I found out that it was the following
|
||||
//flags in this link:
|
||||
//https://stackoverflow.com/questions/52390129/android-intent-setflags-issue
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
if (deepLink != null) {
|
||||
intent.setData(Uri.parse(deepLink));
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
//NOTE: we use a dynamically created BroadcastReceiver here so that we can capture
|
||||
//intents from notifications and act on them. For instance when tapping/dismissing
|
||||
//a chat notification we want to clear the chat so that next messages don't show
|
||||
//the messages that we have seen again
|
||||
private final BroadcastReceiver notificationActionReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction() == ACTION_DELETE_NOTIFICATION) {
|
||||
String groupId = intent.getExtras().getString("im.status.ethereum.groupId");
|
||||
if (groupId != null) {
|
||||
cleanGroup(groupId);
|
||||
}
|
||||
}
|
||||
if (intent.getAction() == ACTION_TAP_STOP) {
|
||||
stop();
|
||||
System.exit(0);
|
||||
}
|
||||
Log.e(LOG_TAG, "intent received: " + intent.getAction());
|
||||
}
|
||||
};
|
||||
|
||||
public void registerBroadcastReceiver() {
|
||||
this.intentFilter.addAction(ACTION_DELETE_NOTIFICATION);
|
||||
this.intentFilter.addAction(ACTION_TAP_STOP);
|
||||
context.registerReceiver(notificationActionReceiver, this.intentFilter);
|
||||
Log.e(LOG_TAG, "Broadcast Receiver registered");
|
||||
}
|
||||
|
||||
|
||||
|
||||
private NotificationManager notificationManager() {
|
||||
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
}
|
||||
|
||||
public void invokeApp(Bundle bundle) {
|
||||
String packageName = context.getPackageName();
|
||||
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
String className = launchIntent.getComponent().getClassName();
|
||||
|
||||
try {
|
||||
Class<?> activityClass = Class.forName(className);
|
||||
Intent activityIntent = new Intent(context, activityClass);
|
||||
|
||||
if(bundle != null) {
|
||||
activityIntent.putExtra("notification", bundle);
|
||||
}
|
||||
|
||||
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
context.startActivity(activityIntent);
|
||||
} catch(Exception e) {
|
||||
Log.e(LOG_TAG, "Class not found", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendToNotificationCentre(final Bundle bundle) {
|
||||
PushNotificationPicturesAggregator aggregator = new PushNotificationPicturesAggregator(new PushNotificationPicturesAggregator.Callback() {
|
||||
public void call(Bitmap largeIconImage, Bitmap bigPictureImage) {
|
||||
sendToNotificationCentreWithPicture(bundle, largeIconImage, bigPictureImage);
|
||||
}
|
||||
});
|
||||
|
||||
aggregator.setLargeIconUrl(context, bundle.getString("largeIconUrl"));
|
||||
aggregator.setBigPictureUrl(context, bundle.getString("bigPictureUrl"));
|
||||
}
|
||||
|
||||
public void handleConversation(final Bundle bundle) {
|
||||
if (bundle.getBoolean("deleted")){
|
||||
this.removeStatusMessage(bundle);
|
||||
} else {
|
||||
this.addStatusMessage(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearMessageNotifications(String conversationId) {
|
||||
notificationManager.cancel(conversationId.hashCode());
|
||||
cleanGroup(conversationId);
|
||||
}
|
||||
|
||||
public void clearAllMessageNotifications() {
|
||||
notificationManager.cancelAll();
|
||||
}
|
||||
|
||||
public void sendToNotificationCentreWithPicture(final Bundle bundle, Bitmap largeIconBitmap, Bitmap bigPictureBitmap) {
|
||||
|
||||
try {
|
||||
Class intentClass = getMainActivityClass();
|
||||
if (intentClass == null) {
|
||||
Log.e(LOG_TAG, "No activity class found for the notification");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bundle.getBoolean("isConversation")) {
|
||||
this.handleConversation(bundle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bundle.getString("message") == null) {
|
||||
// this happens when a 'data' notification is received - we do not synthesize a local notification in this case
|
||||
Log.d(LOG_TAG, "Ignore this message if you sent data-only notification. Cannot send to notification centre because there is no 'message' field in: " + bundle);
|
||||
return;
|
||||
}
|
||||
|
||||
String notificationIdString = bundle.getString("id");
|
||||
if (notificationIdString == null) {
|
||||
Log.e(LOG_TAG, "No notification ID specified for the notification");
|
||||
return;
|
||||
}
|
||||
|
||||
Resources res = context.getResources();
|
||||
String packageName = context.getPackageName();
|
||||
|
||||
String title = bundle.getString("title");
|
||||
if (title == null) {
|
||||
ApplicationInfo appInfo = context.getApplicationInfo();
|
||||
title = context.getPackageManager().getApplicationLabel(appInfo).toString();
|
||||
}
|
||||
|
||||
int priority = NotificationCompat.PRIORITY_HIGH;
|
||||
final String priorityString = bundle.getString("priority");
|
||||
|
||||
if (priorityString != null) {
|
||||
switch (priorityString.toLowerCase()) {
|
||||
case "max":
|
||||
priority = NotificationCompat.PRIORITY_MAX;
|
||||
break;
|
||||
case "high":
|
||||
priority = NotificationCompat.PRIORITY_HIGH;
|
||||
break;
|
||||
case "low":
|
||||
priority = NotificationCompat.PRIORITY_LOW;
|
||||
break;
|
||||
case "min":
|
||||
priority = NotificationCompat.PRIORITY_MIN;
|
||||
break;
|
||||
case "default":
|
||||
priority = NotificationCompat.PRIORITY_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
priority = NotificationCompat.PRIORITY_HIGH;
|
||||
}
|
||||
}
|
||||
|
||||
int visibility = NotificationCompat.VISIBILITY_PRIVATE;
|
||||
final String visibilityString = bundle.getString("visibility");
|
||||
|
||||
if (visibilityString != null) {
|
||||
switch (visibilityString.toLowerCase()) {
|
||||
case "private":
|
||||
visibility = NotificationCompat.VISIBILITY_PRIVATE;
|
||||
break;
|
||||
case "public":
|
||||
visibility = NotificationCompat.VISIBILITY_PUBLIC;
|
||||
break;
|
||||
case "secret":
|
||||
visibility = NotificationCompat.VISIBILITY_SECRET;
|
||||
break;
|
||||
default:
|
||||
visibility = NotificationCompat.VISIBILITY_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
String channel_id = bundle.getString("channelId");
|
||||
|
||||
if(channel_id == null) {
|
||||
channel_id = this.getNotificationDefaultChannelId();
|
||||
}
|
||||
|
||||
NotificationCompat.Builder notification = new NotificationCompat.Builder(context, channel_id)
|
||||
.setContentTitle(title)
|
||||
.setTicker(bundle.getString("ticker"))
|
||||
.setVisibility(visibility)
|
||||
.setPriority(priority)
|
||||
.setAutoCancel(bundle.getBoolean("autoCancel", true))
|
||||
.setOnlyAlertOnce(bundle.getBoolean("onlyAlertOnce", false));
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // API 24 and higher
|
||||
// Restore showing timestamp on Android 7+
|
||||
// Source: https://developer.android.com/reference/android/app/Notification.Builder.html#setShowWhen(boolean)
|
||||
boolean showWhen = bundle.getBoolean("showWhen", true);
|
||||
|
||||
notification.setShowWhen(showWhen);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // API 26 and higher
|
||||
// Changing Default mode of notification
|
||||
notification.setDefaults(Notification.DEFAULT_LIGHTS);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { // API 20 and higher
|
||||
String group = bundle.getString("group");
|
||||
|
||||
if (group != null) {
|
||||
notification.setGroup(group);
|
||||
}
|
||||
|
||||
if (bundle.containsKey("groupSummary") || bundle.getBoolean("groupSummary")) {
|
||||
notification.setGroupSummary(bundle.getBoolean("groupSummary"));
|
||||
}
|
||||
}
|
||||
|
||||
String numberString = bundle.getString("number");
|
||||
|
||||
if (numberString != null) {
|
||||
notification.setNumber(Integer.parseInt(numberString));
|
||||
}
|
||||
|
||||
// Small icon
|
||||
int smallIconResId = 0;
|
||||
|
||||
String smallIcon = bundle.getString("smallIcon");
|
||||
|
||||
if (smallIcon != null && !smallIcon.isEmpty()) {
|
||||
smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName);
|
||||
} else if(smallIcon == null) {
|
||||
smallIconResId = res.getIdentifier("ic_stat_notify_status", "drawable", packageName);
|
||||
}
|
||||
|
||||
if (smallIconResId == 0) {
|
||||
smallIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
|
||||
|
||||
if (smallIconResId == 0) {
|
||||
smallIconResId = android.R.drawable.ic_dialog_info;
|
||||
}
|
||||
}
|
||||
|
||||
notification.setSmallIcon(smallIconResId);
|
||||
|
||||
// Large icon
|
||||
if(largeIconBitmap == null) {
|
||||
int largeIconResId = 0;
|
||||
|
||||
String largeIcon = bundle.getString("largeIcon");
|
||||
|
||||
if (largeIcon != null && !largeIcon.isEmpty()) {
|
||||
largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName);
|
||||
} else if(largeIcon == null) {
|
||||
largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
|
||||
}
|
||||
|
||||
// Before Lolipop there was no large icon for notifications.
|
||||
if (largeIconResId != 0 && (largeIcon != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) {
|
||||
largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
|
||||
}
|
||||
}
|
||||
|
||||
Bundle author = bundle.getBundle("notificationAuthor");
|
||||
|
||||
if (largeIconBitmap == null && author != null) {
|
||||
String base64Image = author.getString("icon").split(",")[1];
|
||||
byte[] decodedString = Base64.decode(base64Image, Base64.DEFAULT);
|
||||
Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
|
||||
notification.setLargeIcon(getCircleBitmap(decodedByte));
|
||||
} else if (largeIconBitmap != null){
|
||||
notification.setLargeIcon(largeIconBitmap);
|
||||
}
|
||||
|
||||
String message = bundle.getString("message");
|
||||
|
||||
notification.setContentText(message);
|
||||
|
||||
String subText = bundle.getString("subText");
|
||||
|
||||
if (subText != null) {
|
||||
notification.setSubText(subText);
|
||||
}
|
||||
|
||||
String bigText = bundle.getString("bigText");
|
||||
|
||||
if (bigText == null) {
|
||||
bigText = message;
|
||||
}
|
||||
|
||||
NotificationCompat.Style style;
|
||||
|
||||
if(bigPictureBitmap != null) {
|
||||
style = new NotificationCompat.BigPictureStyle()
|
||||
.bigPicture(bigPictureBitmap)
|
||||
.setBigContentTitle(title)
|
||||
.setSummaryText(message);
|
||||
} else {
|
||||
style = new NotificationCompat.BigTextStyle().bigText(bigText);
|
||||
}
|
||||
|
||||
notification.setStyle(style);
|
||||
|
||||
Intent intent = new Intent(context, intentClass);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
bundle.putBoolean("foreground", this.isApplicationInForeground());
|
||||
bundle.putBoolean("userInteraction", true);
|
||||
intent.putExtra("notification", bundle);
|
||||
|
||||
Uri soundUri = null;
|
||||
|
||||
if (!bundle.containsKey("playSound") || bundle.getBoolean("playSound")) {
|
||||
String soundName = bundle.getString("soundName");
|
||||
|
||||
if (soundName == null) {
|
||||
soundName = "default";
|
||||
}
|
||||
|
||||
soundUri = getSoundUri(soundName);
|
||||
|
||||
notification.setSound(soundUri);
|
||||
}
|
||||
|
||||
if (soundUri == null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notification.setSound(null);
|
||||
}
|
||||
|
||||
if (bundle.containsKey("ongoing") || bundle.getBoolean("ongoing")) {
|
||||
notification.setOngoing(bundle.getBoolean("ongoing"));
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
notification.setCategory(NotificationCompat.CATEGORY_CALL);
|
||||
|
||||
String color = bundle.getString("color");
|
||||
int defaultColor = -1;
|
||||
if (color != null) {
|
||||
notification.setColor(Color.parseColor(color));
|
||||
} else if (defaultColor != -1) {
|
||||
notification.setColor(defaultColor);
|
||||
}
|
||||
}
|
||||
|
||||
int notificationID = notificationIdString.hashCode();
|
||||
|
||||
notification.setContentIntent(createOnTapIntent(context, notificationID, bundle.getString("deepLink")))
|
||||
.setDeleteIntent(createOnDismissedIntent(context, notificationID, bundle.getString("deepLink")));
|
||||
|
||||
NotificationManager notificationManager = notificationManager();
|
||||
|
||||
long[] vibratePattern = new long[]{0};
|
||||
|
||||
if (!bundle.containsKey("vibrate") || bundle.getBoolean("vibrate")) {
|
||||
long vibration = bundle.containsKey("vibration") ? (long) bundle.getDouble("vibration") : DEFAULT_VIBRATION;
|
||||
if (vibration == 0)
|
||||
vibration = DEFAULT_VIBRATION;
|
||||
|
||||
vibratePattern = new long[]{0, vibration};
|
||||
|
||||
notification.setVibrate(vibratePattern);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// Define the shortcutId
|
||||
String shortcutId = bundle.getString("shortcutId");
|
||||
|
||||
if (shortcutId != null) {
|
||||
notification.setShortcutId(shortcutId);
|
||||
}
|
||||
|
||||
Long timeoutAfter = (long) bundle.getDouble("timeoutAfter");
|
||||
|
||||
if (timeoutAfter != null && timeoutAfter >= 0) {
|
||||
notification.setTimeoutAfter(timeoutAfter);
|
||||
}
|
||||
}
|
||||
|
||||
Long when = (long) bundle.getDouble("when");
|
||||
|
||||
if (when != null && when >= 0) {
|
||||
notification.setWhen(when);
|
||||
}
|
||||
|
||||
notification.setUsesChronometer(bundle.getBoolean("usesChronometer", false));
|
||||
notification.setChannelId(channel_id);
|
||||
|
||||
JSONArray actionsArray = null;
|
||||
try {
|
||||
actionsArray = bundle.getString("actions") != null ? new JSONArray(bundle.getString("actions")) : null;
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, "Exception while converting actions to JSON object.", e);
|
||||
}
|
||||
|
||||
if (actionsArray != null) {
|
||||
// No icon for now. The icon value of 0 shows no icon.
|
||||
int icon = 0;
|
||||
|
||||
// Add button for each actions.
|
||||
for (int i = 0; i < actionsArray.length(); i++) {
|
||||
String action;
|
||||
try {
|
||||
action = actionsArray.getString(i);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, "Exception while getting action from actionsArray.", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Intent actionIntent = new Intent(context, PushNotificationActions.class);
|
||||
actionIntent.setAction(packageName + ".ACTION_" + i);
|
||||
|
||||
actionIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
|
||||
// Add "action" for later identifying which button gets pressed.
|
||||
bundle.putString("action", action);
|
||||
actionIntent.putExtra("notification", bundle);
|
||||
actionIntent.setPackage(packageName);
|
||||
|
||||
PendingIntent pendingActionIntent = PendingIntent.getBroadcast(context, notificationID, actionIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
notification.addAction(new NotificationCompat.Action.Builder(icon, action, pendingActionIntent).build());
|
||||
} else {
|
||||
notification.addAction(icon, action, pendingActionIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!(this.isApplicationInForeground() && bundle.getBoolean("ignoreInForeground"))) {
|
||||
Notification info = notification.build();
|
||||
info.defaults |= Notification.DEFAULT_LIGHTS;
|
||||
|
||||
if (bundle.containsKey("tag")) {
|
||||
String tag = bundle.getString("tag");
|
||||
notificationManager.notify(tag, notificationID, info);
|
||||
} else {
|
||||
notificationManager.notify(notificationID, info);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "failed to send push notification", e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkOrCreateChannel(NotificationManager manager, String channel_id, String channel_name, String channel_description, Uri soundUri, int importance, long[] vibratePattern, boolean showBadge) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
return false;
|
||||
if (manager == null)
|
||||
return false;
|
||||
|
||||
NotificationChannel channel = manager.getNotificationChannel(channel_id);
|
||||
|
||||
if (
|
||||
channel == null && channel_name != null && channel_description != null ||
|
||||
channel != null &&
|
||||
(
|
||||
channel_name != null && !channel.getName().equals(channel_name) ||
|
||||
channel_description != null && !channel.getDescription().equals(channel_description)
|
||||
)
|
||||
) {
|
||||
// If channel doesn't exist create a new one.
|
||||
// If channel name or description is updated then update the existing channel.
|
||||
channel = new NotificationChannel(channel_id, channel_name, importance);
|
||||
|
||||
channel.setDescription(channel_description);
|
||||
channel.enableLights(true);
|
||||
channel.enableVibration(vibratePattern != null);
|
||||
channel.setVibrationPattern(vibratePattern);
|
||||
channel.setShowBadge(showBadge);
|
||||
|
||||
if (soundUri != null) {
|
||||
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
||||
.build();
|
||||
|
||||
channel.setSound(soundUri, audioAttributes);
|
||||
} else {
|
||||
channel.setSound(null, null);
|
||||
}
|
||||
|
||||
manager.createNotificationChannel(channel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean createChannel(ReadableMap channelInfo) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||||
return false;
|
||||
|
||||
String channelId = channelInfo.getString("channelId");
|
||||
String channelName = channelInfo.getString("channelName");
|
||||
String channelDescription = channelInfo.hasKey("channelDescription") ? channelInfo.getString("channelDescription") : "";
|
||||
String soundName = channelInfo.hasKey("soundName") ? channelInfo.getString("soundName") : "default";
|
||||
int importance = channelInfo.hasKey("importance") ? channelInfo.getInt("importance") : 4;
|
||||
boolean vibrate = channelInfo.hasKey("vibrate") && channelInfo.getBoolean("vibrate");
|
||||
long[] vibratePattern = vibrate ? new long[] { DEFAULT_VIBRATION } : null;
|
||||
boolean showBadge = channelInfo.hasKey("showBadge") && channelInfo.getBoolean("showBadge");
|
||||
|
||||
NotificationManager manager = notificationManager();
|
||||
|
||||
Uri soundUri = getSoundUri(soundName);
|
||||
|
||||
return checkOrCreateChannel(manager, channelId, channelName, channelDescription, soundUri, importance, vibratePattern, showBadge);
|
||||
}
|
||||
|
||||
public String getNotificationDefaultChannelId() {
|
||||
return this.CHANNEL_ID;
|
||||
}
|
||||
|
||||
private Uri getSoundUri(String soundName) {
|
||||
if (soundName == null || "default".equalsIgnoreCase(soundName)) {
|
||||
return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
} else {
|
||||
|
||||
// sound name can be full filename, or just the resource name.
|
||||
// So the strings 'my_sound.mp3' AND 'my_sound' are accepted
|
||||
// The reason is to make the iOS and android javascript interfaces compatible
|
||||
|
||||
int resId;
|
||||
if (context.getResources().getIdentifier(soundName, "raw", context.getPackageName()) != 0) {
|
||||
resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
|
||||
} else {
|
||||
soundName = soundName.substring(0, soundName.lastIndexOf('.'));
|
||||
resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
|
||||
}
|
||||
|
||||
return Uri.parse("android.resource://" + context.getPackageName() + "/" + resId);
|
||||
}
|
||||
}
|
||||
|
||||
public Class getMainActivityClass() {
|
||||
String packageName = context.getPackageName();
|
||||
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
|
||||
String className = launchIntent.getComponent().getClassName();
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isApplicationInForeground() {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<RunningAppProcessInfo> processInfos = activityManager.getRunningAppProcesses();
|
||||
if (processInfos != null) {
|
||||
for (RunningAppProcessInfo processInfo : processInfos) {
|
||||
if (processInfo.processName.equals(context.getPackageName())
|
||||
&& processInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||
&& processInfo.pkgList.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Bitmap getCircleBitmap(Bitmap bitmap) {
|
||||
final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
|
||||
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
final Canvas canvas = new Canvas(output);
|
||||
|
||||
final int color = Color.RED;
|
||||
final Paint paint = new Paint();
|
||||
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
final RectF rectF = new RectF(rect);
|
||||
|
||||
paint.setAntiAlias(true);
|
||||
canvas.drawARGB(0, 0, 0, 0);
|
||||
paint.setColor(color);
|
||||
canvas.drawOval(rectF, paint);
|
||||
|
||||
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
||||
canvas.drawBitmap(bitmap, rect, rect, paint);
|
||||
|
||||
bitmap.recycle();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private Person getPerson(Bundle bundle) {
|
||||
String name = bundle.getString("name");
|
||||
|
||||
return new Person.Builder().setName(name).build();
|
||||
}
|
||||
|
||||
private StatusMessage createMessage(Bundle data) {
|
||||
Person author = getPerson(data.getBundle("notificationAuthor"));
|
||||
long timeStampLongValue = (long) data.getDouble("timestamp");
|
||||
return new StatusMessage(data.getString("id"), author, timeStampLongValue, data.getString("message"));
|
||||
}
|
||||
|
||||
private PendingIntent createGroupOnDismissedIntent(Context context, int notificationId, String groupId, String deepLink) {
|
||||
Intent intent = new Intent(ACTION_DELETE_NOTIFICATION);
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink);
|
||||
intent.putExtra("im.status.ethereum.groupId", groupId);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
private PendingIntent createGroupOnTapIntent(Context context, int notificationId, String groupId, String deepLink) {
|
||||
Intent intent = getOpenAppIntent(deepLink);
|
||||
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
private PendingIntent createOnTapIntent(Context context, int notificationId, String deepLink) {
|
||||
Intent intent = getOpenAppIntent(deepLink);
|
||||
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
private PendingIntent createOnDismissedIntent(Context context, int notificationId, String deepLink) {
|
||||
Intent intent = new Intent(ACTION_DELETE_NOTIFICATION);
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
public void removeStatusMessage(Bundle bundle) {
|
||||
String conversationId = bundle.getString("conversationId");
|
||||
StatusMessageGroup group = this.messageGroups.get(conversationId);
|
||||
NotificationManager notificationManager = notificationManager();
|
||||
|
||||
if (group == null) {
|
||||
group = new StatusMessageGroup(conversationId);
|
||||
}
|
||||
|
||||
this.messageGroups.put(conversationId, group);
|
||||
|
||||
String id = bundle.getString("id");
|
||||
group.removeMessage(id);
|
||||
|
||||
this.showMessages(bundle);
|
||||
}
|
||||
|
||||
public StatusMessageGroup getMessageGroup(String conversationId) {
|
||||
return this.messageGroups.get(conversationId);
|
||||
}
|
||||
|
||||
public void addStatusMessage(Bundle bundle) {
|
||||
String conversationId = bundle.getString("conversationId");
|
||||
StatusMessageGroup group = this.messageGroups.get(conversationId);
|
||||
NotificationManager notificationManager = notificationManager();
|
||||
|
||||
if (group == null) {
|
||||
group = new StatusMessageGroup(conversationId);
|
||||
}
|
||||
|
||||
this.messageGroups.put(conversationId, group);
|
||||
|
||||
group.addMessage(createMessage(bundle));
|
||||
|
||||
this.showMessages(bundle);
|
||||
}
|
||||
|
||||
public void showMessages(Bundle bundle) {
|
||||
String conversationId = bundle.getString("conversationId");
|
||||
StatusMessageGroup group = this.messageGroups.get(conversationId);
|
||||
NotificationManager notificationManager = notificationManager();
|
||||
|
||||
NotificationCompat.MessagingStyle messagingStyle = new NotificationCompat.MessagingStyle("Me");
|
||||
ArrayList<StatusMessage> messages = group.getMessages();
|
||||
|
||||
if (messages.size() == 0) {
|
||||
notificationManager.cancel(conversationId.hashCode());
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
StatusMessage message = messages.get(i);
|
||||
messagingStyle.addMessage(message.getText(),
|
||||
message.getTimestamp(),
|
||||
message.getAuthor());
|
||||
}
|
||||
|
||||
if (bundle.getString("title") != null) {
|
||||
messagingStyle.setConversationTitle(bundle.getString("title"));
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_stat_notify_status)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setStyle(messagingStyle)
|
||||
.setGroup(conversationId)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(createGroupOnTapIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
|
||||
.setDeleteIntent(createGroupOnDismissedIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
|
||||
.setNumber(messages.size() + 1)
|
||||
.setAutoCancel(true);
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
builder.setVibrate(new long[0]);
|
||||
}
|
||||
notificationManager.notify(conversationId.hashCode(), builder.build());
|
||||
}
|
||||
|
||||
class StatusMessageGroup {
|
||||
private ArrayList<StatusMessage> messages;
|
||||
private String id;
|
||||
|
||||
StatusMessageGroup(String id) {
|
||||
this.id = id;
|
||||
this.messages = new ArrayList<StatusMessage>();
|
||||
}
|
||||
public ArrayList<StatusMessage> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void addMessage(StatusMessage message) {
|
||||
this.messages.add(message);
|
||||
}
|
||||
|
||||
public void removeMessage(String id) {
|
||||
ArrayList<StatusMessage> newMessages = new ArrayList<StatusMessage>();
|
||||
for(StatusMessage message: this.messages) {
|
||||
if(!message.id.equals(id)) {
|
||||
newMessages.add(message);
|
||||
}
|
||||
}
|
||||
this.messages = newMessages;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
|
||||
class StatusMessage {
|
||||
public Person getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
private String id;
|
||||
private Person author;
|
||||
private long timestamp;
|
||||
private String text;
|
||||
|
||||
StatusMessage(String id, Person author, long timestamp, String text) {
|
||||
this.id = id;
|
||||
this.author = author;
|
||||
this.timestamp = timestamp;
|
||||
this.text = text;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeGroup(String groupId) {
|
||||
this.messageGroups.remove(groupId);
|
||||
}
|
||||
|
||||
private void cleanGroup(String groupId) {
|
||||
removeGroup(groupId);
|
||||
if (messageGroups.size() == 0) {
|
||||
notificationManager.cancelAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
Log.e(LOG_TAG, "Starting Foreground Service");
|
||||
Intent serviceIntent = new Intent(context, ForegroundService.class);
|
||||
context.startService(serviceIntent);
|
||||
this.registerBroadcastReceiver();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
Log.e(LOG_TAG, "Stopping Foreground Service");
|
||||
//NOTE: we cancel all the current notifications, because the intents can't be used anymore
|
||||
//since the broadcast receiver will be killed as well and won't be able to handle any intent
|
||||
notificationManager.cancelAll();
|
||||
Intent serviceIntent = new Intent(context, ForegroundService.class);
|
||||
context.stopService(serviceIntent);
|
||||
context.unregisterReceiver(notificationActionReceiver);
|
||||
}
|
||||
}
|
||||
+771
@@ -0,0 +1,771 @@
|
||||
// https://github.com/zo0r/react-native-push-notification/blob/bedc8f646aab67d594f291449fbfa24e07b64fe8/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java Copy-Paste with removed firebase
|
||||
package im.status.ethereum.pushnotifications
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.ActivityManager.RunningAppProcessInfo
|
||||
import android.app.AlarmManager
|
||||
import android.app.Application
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffXfermode
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import android.media.AudioAttributes
|
||||
import android.media.RingtoneManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.service.notification.StatusBarNotification
|
||||
import android.util.Log
|
||||
import android.util.Base64
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.Person
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.ReadableArray
|
||||
import com.facebook.react.bridge.ReadableMap
|
||||
import com.facebook.react.bridge.WritableArray
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import im.status.ethereum.module.R
|
||||
|
||||
class PushNotificationHelper(context: Application, intentFilter: IntentFilter) {
|
||||
companion object {
|
||||
const val LOG_TAG = "PushNotification"
|
||||
private const val DEFAULT_VIBRATION: Long = 300L
|
||||
private const val CHANNEL_ID = "status-im-notifications"
|
||||
const val ACTION_DELETE_NOTIFICATION = "im.status.ethereum.module.DELETE_NOTIFICATION"
|
||||
const val ACTION_TAP_STOP = "im.status.ethereum.module.TAP_STOP"
|
||||
}
|
||||
|
||||
private val context: Context
|
||||
val flag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_MUTABLE else PendingIntent.FLAG_CANCEL_CURRENT
|
||||
private val notificationManager: NotificationManager
|
||||
private val persons: HashMap<String, Person>
|
||||
private val messageGroups: HashMap<String, StatusMessageGroup>
|
||||
private val intentFilter: IntentFilter
|
||||
fun getOpenAppIntent(deepLink: String?): Intent? {
|
||||
val intentClass: java.lang.Class<*>
|
||||
val packageName: String = context.getPackageName()
|
||||
val launchIntent: Intent? = context.getPackageManager().getLaunchIntentForPackage(packageName)
|
||||
if (launchIntent == null) {
|
||||
return null
|
||||
}
|
||||
val className: String? = launchIntent.getComponent()?.getClassName()
|
||||
if (className == null) {
|
||||
return null
|
||||
}
|
||||
intentClass = try {
|
||||
java.lang.Class.forName(className)
|
||||
} catch (e: java.lang.ClassNotFoundException) {
|
||||
e.printStackTrace()
|
||||
return null
|
||||
}
|
||||
val intent = Intent(context, intentClass)
|
||||
intent.addCategory(Intent.CATEGORY_BROWSABLE)
|
||||
intent.setAction(Intent.ACTION_VIEW)
|
||||
//NOTE: you might wonder, why the heck did he decide to set these flags in particular. Well,
|
||||
//the answer is a simple as it can get in the Android native development world. I noticed
|
||||
//that my initial setup was opening the app but wasn't triggering any events on the js side, like
|
||||
//the links do from the browser. So I compared both intents and noticed that the link from
|
||||
//the browser produces an intent with the flag 0x14000000. I found out that it was the following
|
||||
//flags in this link:
|
||||
//https://stackoverflow.com/questions/52390129/android-intent-setflags-issue
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
if (deepLink != null) {
|
||||
intent.setData(Uri.parse(deepLink))
|
||||
}
|
||||
return intent
|
||||
}
|
||||
|
||||
//NOTE: we use a dynamically created BroadcastReceiver here so that we can capture
|
||||
//intents from notifications and act on them. For instance when tapping/dismissing
|
||||
//a chat notification we want to clear the chat so that next messages don't show
|
||||
//the messages that we have seen again
|
||||
private val notificationActionReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent) {
|
||||
if (intent.getAction() === ACTION_DELETE_NOTIFICATION) {
|
||||
val groupId: String? = intent.getExtras()?.getString("im.status.ethereum.groupId")
|
||||
if (groupId != null) {
|
||||
cleanGroup(groupId)
|
||||
}
|
||||
}
|
||||
if (intent.getAction() === ACTION_TAP_STOP) {
|
||||
stop()
|
||||
java.lang.System.exit(0)
|
||||
}
|
||||
Log.e(LOG_TAG, "intent received: " + intent.getAction())
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
this.context = context
|
||||
this.intentFilter = intentFilter
|
||||
persons = HashMap<String, Person>()
|
||||
messageGroups = HashMap<String, StatusMessageGroup>()
|
||||
notificationManager = context.getSystemService(NotificationManager::class.java)
|
||||
registerBroadcastReceiver()
|
||||
}
|
||||
|
||||
fun registerBroadcastReceiver() {
|
||||
intentFilter.addAction(ACTION_DELETE_NOTIFICATION)
|
||||
intentFilter.addAction(ACTION_TAP_STOP)
|
||||
context.registerReceiver(notificationActionReceiver, intentFilter)
|
||||
Log.e(LOG_TAG, "Broadcast Receiver registered")
|
||||
}
|
||||
|
||||
private fun notificationManager(): NotificationManager {
|
||||
return context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
}
|
||||
|
||||
fun invokeApp(bundle: Bundle?) {
|
||||
val packageName: String = context.getPackageName()
|
||||
val launchIntent: Intent? = context.getPackageManager().getLaunchIntentForPackage(packageName)
|
||||
if (launchIntent == null) {
|
||||
return
|
||||
}
|
||||
val className: String? = launchIntent.getComponent()?.getClassName()
|
||||
if (className == null) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
val activityClass: java.lang.Class<*> = java.lang.Class.forName(className)
|
||||
val activityIntent = Intent(context, activityClass)
|
||||
if (bundle != null) {
|
||||
activityIntent.putExtra("notification", bundle)
|
||||
}
|
||||
activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
context.startActivity(activityIntent)
|
||||
} catch (e: java.lang.Exception) {
|
||||
Log.e(LOG_TAG, "Class not found", e)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fun sendToNotificationCentre(bundle: Bundle) {
|
||||
val aggregator = PushNotificationPicturesAggregator(object : PushNotificationPicturesAggregator.Callback {
|
||||
override fun call(largeIconImage: Bitmap?, bigPictureImage: Bitmap?) {
|
||||
sendToNotificationCentreWithPicture(bundle, largeIconImage, bigPictureImage)
|
||||
}
|
||||
})
|
||||
aggregator.setLargeIconUrl(context, bundle.getString("largeIconUrl"))
|
||||
aggregator.setBigPictureUrl(context, bundle.getString("bigPictureUrl"))
|
||||
}
|
||||
|
||||
fun handleConversation(bundle: Bundle) {
|
||||
if (bundle.getBoolean("deleted")) {
|
||||
removeStatusMessage(bundle)
|
||||
} else {
|
||||
addStatusMessage(bundle)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearMessageNotifications(conversationId: String) {
|
||||
notificationManager.cancel(conversationId.hashCode())
|
||||
cleanGroup(conversationId)
|
||||
}
|
||||
|
||||
fun clearAllMessageNotifications() {
|
||||
notificationManager.cancelAll()
|
||||
}
|
||||
|
||||
fun sendToNotificationCentreWithPicture(bundle: Bundle, largeIconBitmap: Bitmap?, bigPictureBitmap: Bitmap?) {
|
||||
var largeIconBitmap: Bitmap? = largeIconBitmap
|
||||
try {
|
||||
val intentClass: java.lang.Class<*>? = mainActivityClass
|
||||
if (intentClass == null) {
|
||||
Log.e(LOG_TAG, "No activity class found for the notification")
|
||||
return
|
||||
}
|
||||
if (bundle.getBoolean("isConversation")) {
|
||||
handleConversation(bundle)
|
||||
return
|
||||
}
|
||||
if (bundle.getString("message") == null) {
|
||||
// this happens when a 'data' notification is received - we do not synthesize a local notification in this case
|
||||
Log.d(LOG_TAG, "Ignore this message if you sent data-only notification. Cannot send to notification centre because there is no 'message' field in: $bundle")
|
||||
return
|
||||
}
|
||||
val notificationIdString: String? = bundle.getString("id")
|
||||
if (notificationIdString == null) {
|
||||
Log.e(LOG_TAG, "No notification ID specified for the notification")
|
||||
return
|
||||
}
|
||||
val res: Resources = context.getResources()
|
||||
val packageName: String = context.getPackageName()
|
||||
var title: String? = bundle.getString("title")
|
||||
if (title == null) {
|
||||
val appInfo: ApplicationInfo = context.getApplicationInfo()
|
||||
title = context.getPackageManager().getApplicationLabel(appInfo).toString()
|
||||
}
|
||||
var priority: Int = NotificationCompat.PRIORITY_HIGH
|
||||
val priorityString: String? = bundle.getString("priority")
|
||||
if (priorityString != null) {
|
||||
priority = when (priorityString.lowercase()) {
|
||||
"max" -> NotificationCompat.PRIORITY_MAX
|
||||
"high" -> NotificationCompat.PRIORITY_HIGH
|
||||
"low" -> NotificationCompat.PRIORITY_LOW
|
||||
"min" -> NotificationCompat.PRIORITY_MIN
|
||||
"default" -> NotificationCompat.PRIORITY_DEFAULT
|
||||
else -> NotificationCompat.PRIORITY_HIGH
|
||||
}
|
||||
}
|
||||
var visibility: Int = NotificationCompat.VISIBILITY_PRIVATE
|
||||
val visibilityString: String? = bundle.getString("visibility")
|
||||
if (visibilityString != null) {
|
||||
visibility = when (visibilityString.lowercase()) {
|
||||
"private" -> NotificationCompat.VISIBILITY_PRIVATE
|
||||
"public" -> NotificationCompat.VISIBILITY_PUBLIC
|
||||
"secret" -> NotificationCompat.VISIBILITY_SECRET
|
||||
else -> NotificationCompat.VISIBILITY_PRIVATE
|
||||
}
|
||||
}
|
||||
var channel_id: String? = bundle.getString("channelId")
|
||||
if (channel_id == null) {
|
||||
channel_id = notificationDefaultChannelId
|
||||
}
|
||||
val notification: NotificationCompat.Builder = NotificationCompat.Builder(context, channel_id)
|
||||
.setContentTitle(title)
|
||||
.setTicker(bundle.getString("ticker"))
|
||||
.setVisibility(visibility)
|
||||
.setPriority(priority)
|
||||
.setAutoCancel(bundle.getBoolean("autoCancel", true))
|
||||
.setOnlyAlertOnce(bundle.getBoolean("onlyAlertOnce", false))
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // API 24 and higher
|
||||
// Restore showing timestamp on Android 7+
|
||||
// Source: https://developer.android.com/reference/android/app/Notification.Builder.html#setShowWhen(boolean)
|
||||
val showWhen: Boolean = bundle.getBoolean("showWhen", true)
|
||||
notification.setShowWhen(showWhen)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // API 26 and higher
|
||||
// Changing Default mode of notification
|
||||
notification.setDefaults(Notification.DEFAULT_LIGHTS)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { // API 20 and higher
|
||||
val group: String? = bundle.getString("group")
|
||||
if (group != null) {
|
||||
notification.setGroup(group)
|
||||
}
|
||||
if (bundle.containsKey("groupSummary") || bundle.getBoolean("groupSummary")) {
|
||||
notification.setGroupSummary(bundle.getBoolean("groupSummary"))
|
||||
}
|
||||
}
|
||||
val numberString: String? = bundle.getString("number")
|
||||
if (numberString != null) {
|
||||
notification.setNumber(numberString.toInt())
|
||||
}
|
||||
|
||||
// Small icon
|
||||
var smallIconResId = 0
|
||||
val smallIcon: String? = bundle.getString("smallIcon")
|
||||
if (smallIcon != null && !smallIcon.isEmpty()) {
|
||||
smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName)
|
||||
} else if (smallIcon == null) {
|
||||
smallIconResId = res.getIdentifier("ic_stat_notify_status", "drawable", packageName)
|
||||
}
|
||||
if (smallIconResId == 0) {
|
||||
smallIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName)
|
||||
if (smallIconResId == 0) {
|
||||
smallIconResId = android.R.drawable.ic_dialog_info
|
||||
}
|
||||
}
|
||||
notification.setSmallIcon(smallIconResId)
|
||||
|
||||
// Large icon
|
||||
if (largeIconBitmap == null) {
|
||||
var largeIconResId = 0
|
||||
val largeIcon: String? = bundle.getString("largeIcon")
|
||||
if (largeIcon != null && !largeIcon.isEmpty()) {
|
||||
largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName)
|
||||
} else if (largeIcon == null) {
|
||||
largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName)
|
||||
}
|
||||
|
||||
// Before Lolipop there was no large icon for notifications.
|
||||
if (largeIconResId != 0 && (largeIcon != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) {
|
||||
largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId)
|
||||
}
|
||||
}
|
||||
val author: Bundle? = bundle.getBundle("notificationAuthor")
|
||||
if (largeIconBitmap == null && author != null) {
|
||||
val base64Image: String? = author.getString("icon")?.split(",")?.get(1)
|
||||
if (base64Image != null) {
|
||||
val decodedString: ByteArray = Base64.decode(base64Image, Base64.DEFAULT)
|
||||
val decodedByte: Bitmap = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.size)
|
||||
notification.setLargeIcon(getCircleBitmap(decodedByte))
|
||||
}
|
||||
} else if (largeIconBitmap != null) {
|
||||
notification.setLargeIcon(largeIconBitmap)
|
||||
}
|
||||
val message: String? = bundle.getString("message")
|
||||
if (message != null) {
|
||||
notification.setContentText(message)
|
||||
}
|
||||
val subText: String? = bundle.getString("subText")
|
||||
if (subText != null) {
|
||||
notification.setSubText(subText)
|
||||
}
|
||||
var bigText: String? = bundle.getString("bigText")
|
||||
if (bigText == null) {
|
||||
bigText = message
|
||||
}
|
||||
val style: NotificationCompat.Style
|
||||
style = if (bigPictureBitmap != null) {
|
||||
NotificationCompat.BigPictureStyle()
|
||||
.bigPicture(bigPictureBitmap)
|
||||
.setBigContentTitle(title)
|
||||
.setSummaryText(message)
|
||||
} else {
|
||||
NotificationCompat.BigTextStyle().bigText(bigText)
|
||||
}
|
||||
notification.setStyle(style)
|
||||
val intent = Intent(context, intentClass)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
bundle.putBoolean("foreground", isApplicationInForeground)
|
||||
bundle.putBoolean("userInteraction", true)
|
||||
intent.putExtra("notification", bundle)
|
||||
var soundUri: Uri? = null
|
||||
if (!bundle.containsKey("playSound") || bundle.getBoolean("playSound")) {
|
||||
var soundName: String? = bundle.getString("soundName")
|
||||
if (soundName == null) {
|
||||
soundName = "default"
|
||||
}
|
||||
soundUri = getSoundUri(soundName)
|
||||
notification.setSound(soundUri)
|
||||
}
|
||||
if (soundUri == null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
notification.setSound(null)
|
||||
}
|
||||
if (bundle.containsKey("ongoing") || bundle.getBoolean("ongoing")) {
|
||||
notification.setOngoing(bundle.getBoolean("ongoing"))
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
notification.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
val color: String? = bundle.getString("color")
|
||||
val defaultColor = -1
|
||||
if (color != null) {
|
||||
notification.setColor(Color.parseColor(color))
|
||||
} else if (defaultColor != -1) {
|
||||
notification.setColor(defaultColor)
|
||||
}
|
||||
}
|
||||
val notificationID = notificationIdString.hashCode()
|
||||
var deepLink: String? = bundle.getString("deepLink")
|
||||
if (deepLink != null) {
|
||||
notification
|
||||
.setContentIntent(createOnTapIntent(context, notificationID, deepLink))
|
||||
.setDeleteIntent(createOnDismissedIntent(context, notificationID, deepLink))
|
||||
}
|
||||
val notificationManager: NotificationManager = notificationManager()
|
||||
if (!bundle.containsKey("vibrate") || bundle.getBoolean("vibrate")) {
|
||||
var vibration = if (bundle.containsKey("vibration")) bundle.getDouble("vibration") as Long else DEFAULT_VIBRATION
|
||||
if (vibration == 0L) {
|
||||
vibration = DEFAULT_VIBRATION
|
||||
}
|
||||
val vibrationPattern: LongArray = longArrayOf(0L, vibration)
|
||||
notification.setVibrate(vibrationPattern)
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// Define the shortcutId
|
||||
val shortcutId: String? = bundle.getString("shortcutId")
|
||||
if (shortcutId != null) {
|
||||
notification.setShortcutId(shortcutId)
|
||||
}
|
||||
val timeoutAfter = bundle.getDouble("timeoutAfter") as Long
|
||||
if (timeoutAfter != null && timeoutAfter >= 0) {
|
||||
notification.setTimeoutAfter(timeoutAfter)
|
||||
}
|
||||
}
|
||||
val `when` = bundle.getDouble("when") as Long
|
||||
if (`when` != null && `when` >= 0) {
|
||||
notification.setWhen(`when`)
|
||||
}
|
||||
notification.setUsesChronometer(bundle.getBoolean("usesChronometer", false))
|
||||
notification.setChannelId(channel_id)
|
||||
var actionsArray: JSONArray? = null
|
||||
try {
|
||||
actionsArray = if (bundle.getString("actions") != null) JSONArray(bundle.getString("actions")) else null
|
||||
} catch (e: JSONException) {
|
||||
Log.e(LOG_TAG, "Exception while converting actions to JSON object.", e)
|
||||
}
|
||||
if (actionsArray != null) {
|
||||
// No icon for now. The icon value of 0 shows no icon.
|
||||
val icon = 0
|
||||
|
||||
// Add button for each actions.
|
||||
for (i in 0 until actionsArray.length()) {
|
||||
var action: String
|
||||
action = try {
|
||||
actionsArray.getString(i)
|
||||
} catch (e: JSONException) {
|
||||
Log.e(LOG_TAG, "Exception while getting action from actionsArray.", e)
|
||||
continue
|
||||
}
|
||||
val actionIntent = Intent(context, PushNotificationActions::class.java)
|
||||
actionIntent.setAction("$packageName.ACTION_$i")
|
||||
actionIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
|
||||
// Add "action" for later identifying which button gets pressed.
|
||||
bundle.putString("action", action)
|
||||
actionIntent.putExtra("notification", bundle)
|
||||
actionIntent.setPackage(packageName)
|
||||
val pendingActionIntent: PendingIntent = PendingIntent.getBroadcast(context, notificationID, actionIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
notification.addAction(NotificationCompat.Action.Builder(icon, action, pendingActionIntent).build())
|
||||
} else {
|
||||
notification.addAction(icon, action, pendingActionIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(isApplicationInForeground && bundle.getBoolean("ignoreInForeground"))) {
|
||||
val info: Notification = notification.build()
|
||||
info.defaults = info.defaults or Notification.DEFAULT_LIGHTS
|
||||
if (bundle.containsKey("tag")) {
|
||||
val tag: String? = bundle.getString("tag")
|
||||
if (tag != null) {
|
||||
notificationManager.notify(tag, notificationID, info)
|
||||
}
|
||||
} else {
|
||||
notificationManager.notify(notificationID, info)
|
||||
}
|
||||
}
|
||||
} catch (e: java.lang.Exception) {
|
||||
Log.e(LOG_TAG, "failed to send push notification", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOrCreateChannel(manager: NotificationManager?, channel_id: String, channel_name: String?, channel_description: String?, soundUri: Uri?, importance: Int, vibratePattern: LongArray?, showBadge: Boolean): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return false
|
||||
if (manager == null) return false
|
||||
var channel: NotificationChannel? = manager.getNotificationChannel(channel_id)
|
||||
if (channel == null && channel_name != null && channel_description != null ||
|
||||
channel != null &&
|
||||
(channel_name != null && !channel.getName().equals(channel_name) ||
|
||||
channel_description != null && !channel.getDescription().equals(channel_description))) {
|
||||
// If channel doesn't exist create a new one.
|
||||
// If channel name or description is updated then update the existing channel.
|
||||
channel = NotificationChannel(channel_id, channel_name, importance)
|
||||
channel.setDescription(channel_description)
|
||||
channel.enableLights(true)
|
||||
channel.enableVibration(vibratePattern != null)
|
||||
channel.setVibrationPattern(vibratePattern)
|
||||
channel.setShowBadge(showBadge)
|
||||
if (soundUri != null) {
|
||||
val audioAttributes: AudioAttributes = AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
||||
.build()
|
||||
channel.setSound(soundUri, audioAttributes)
|
||||
} else {
|
||||
channel.setSound(null, null)
|
||||
}
|
||||
manager.createNotificationChannel(channel)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun createChannel(channelInfo: ReadableMap): Boolean {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return false
|
||||
}
|
||||
val channelId: String? = channelInfo.getString("channelId")
|
||||
val channelName: String? = channelInfo.getString("channelName")
|
||||
val channelDescription = if (channelInfo.hasKey("channelDescription")) channelInfo.getString("channelDescription") else ""
|
||||
val soundName = if (channelInfo.hasKey("soundName")) channelInfo.getString("soundName") else "default"
|
||||
val importance = if (channelInfo.hasKey("importance")) channelInfo.getInt("importance") else 4
|
||||
val vibrate = channelInfo.hasKey("vibrate") && channelInfo.getBoolean("vibrate")
|
||||
val vibratePattern = if (vibrate) longArrayOf(DEFAULT_VIBRATION) else null
|
||||
val showBadge = channelInfo.hasKey("showBadge") && channelInfo.getBoolean("showBadge")
|
||||
val manager: NotificationManager = notificationManager()
|
||||
val soundUri: Uri? = if (soundName != null) getSoundUri(soundName) else null
|
||||
if (channelId != null) {
|
||||
return checkOrCreateChannel(manager, channelId, channelName, channelDescription, soundUri, importance, vibratePattern, showBadge)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
val notificationDefaultChannelId: String
|
||||
get() = CHANNEL_ID
|
||||
|
||||
private fun getSoundUri(soundName: String): Uri {
|
||||
var soundName: String? = soundName
|
||||
return if (soundName == null || "default".equals(soundName, ignoreCase = true)) {
|
||||
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
|
||||
} else {
|
||||
|
||||
// sound name can be full filename, or just the resource name.
|
||||
// So the strings 'my_sound.mp3' AND 'my_sound' are accepted
|
||||
// The reason is to make the iOS and android javascript interfaces compatible
|
||||
val resId: Int
|
||||
if (context.getResources().getIdentifier(soundName, "raw", context.getPackageName()) !== 0) {
|
||||
resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName())
|
||||
} else {
|
||||
soundName = soundName.substring(0, soundName.lastIndexOf('.'))
|
||||
resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName())
|
||||
}
|
||||
Uri.parse("android.resource://" + context.getPackageName() + "/" + resId)
|
||||
}
|
||||
}
|
||||
|
||||
val mainActivityClass: java.lang.Class<*>?
|
||||
get() {
|
||||
val packageName: String = context.getPackageName()
|
||||
val launchIntent: Intent? = context.getPackageManager().getLaunchIntentForPackage(packageName)
|
||||
if (launchIntent == null) {
|
||||
return null
|
||||
}
|
||||
val className: String? = launchIntent.getComponent()?.getClassName()
|
||||
if (className == null) {
|
||||
return null
|
||||
}
|
||||
return try {
|
||||
java.lang.Class.forName(className)
|
||||
} catch (e: java.lang.ClassNotFoundException) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
val isApplicationInForeground: Boolean
|
||||
get() {
|
||||
val activityManager: ActivityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
||||
val processInfos: List<RunningAppProcessInfo> = activityManager.getRunningAppProcesses()
|
||||
if (processInfos != null) {
|
||||
for (processInfo in processInfos) {
|
||||
if (processInfo.processName.equals(context.getPackageName()) && processInfo.importance === RunningAppProcessInfo.IMPORTANCE_FOREGROUND && processInfo.pkgList.size > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getCircleBitmap(bitmap: Bitmap): Bitmap {
|
||||
val output: Bitmap = Bitmap.createBitmap(bitmap.getWidth(),
|
||||
bitmap.getHeight(), Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(output)
|
||||
val color: Int = Color.RED
|
||||
val paint = Paint()
|
||||
val rect = Rect(0, 0, bitmap.getWidth(), bitmap.getHeight())
|
||||
val rectF = RectF(rect)
|
||||
paint.setAntiAlias(true)
|
||||
canvas.drawARGB(0, 0, 0, 0)
|
||||
paint.setColor(color)
|
||||
canvas.drawOval(rectF, paint)
|
||||
paint.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_IN))
|
||||
canvas.drawBitmap(bitmap, rect, rect, paint)
|
||||
bitmap.recycle()
|
||||
return output
|
||||
}
|
||||
|
||||
private fun getPerson(bundle: Bundle?): Person {
|
||||
val builder = Person.Builder()
|
||||
val name: String? = bundle?.getString("name")
|
||||
if (name != null) {
|
||||
builder.setName(name)
|
||||
}
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
private fun createMessage(data: Bundle): StatusMessage {
|
||||
val notificationAuthor: Bundle? = data.getBundle("notificationAuthor")
|
||||
val author: Person = getPerson(notificationAuthor)
|
||||
val timeStampLongValue = data.getDouble("timestamp") as Long
|
||||
val id: String? = data.getString("id")
|
||||
val message: String? = data.getString("message")
|
||||
return StatusMessage(id ?: "", author, timeStampLongValue, message ?: "")
|
||||
}
|
||||
|
||||
private fun createGroupOnDismissedIntent(context: Context, notificationId: Int, groupId: String, deepLink: String?): PendingIntent {
|
||||
val intent = Intent(ACTION_DELETE_NOTIFICATION)
|
||||
if (deepLink != null) {
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink)
|
||||
}
|
||||
intent.putExtra("im.status.ethereum.groupId", groupId)
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag)
|
||||
}
|
||||
|
||||
private fun createGroupOnTapIntent(context: Context, notificationId: Int, groupId: String, deepLink: String?): PendingIntent {
|
||||
val intent: Intent? = getOpenAppIntent(deepLink)
|
||||
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag)
|
||||
}
|
||||
|
||||
private fun createOnTapIntent(context: Context, notificationId: Int, deepLink: String?): PendingIntent {
|
||||
val intent: Intent? = getOpenAppIntent(deepLink)
|
||||
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag)
|
||||
}
|
||||
|
||||
private fun createOnDismissedIntent(context: Context, notificationId: Int, deepLink: String): PendingIntent {
|
||||
val intent = Intent(ACTION_DELETE_NOTIFICATION)
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink)
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag)
|
||||
}
|
||||
|
||||
fun removeStatusMessage(bundle: Bundle) {
|
||||
val conversationId: String? = bundle.getString("conversationId")
|
||||
if (conversationId == null) {
|
||||
return
|
||||
}
|
||||
var group: StatusMessageGroup? = if (conversationId != null) messageGroups.get(conversationId) else null
|
||||
val notificationManager: NotificationManager = notificationManager()
|
||||
|
||||
if (group == null) {
|
||||
group = StatusMessageGroup(conversationId)
|
||||
}
|
||||
messageGroups.put(conversationId, group)
|
||||
|
||||
val id: String? = bundle.getString("id")
|
||||
if (id != null) {
|
||||
group.removeMessage(id)
|
||||
}
|
||||
showMessages(bundle)
|
||||
}
|
||||
|
||||
fun getMessageGroup(conversationId: String?): StatusMessageGroup? {
|
||||
return messageGroups.get(conversationId)
|
||||
}
|
||||
|
||||
fun addStatusMessage(bundle: Bundle) {
|
||||
val conversationId: String? = bundle.getString("conversationId")
|
||||
if (conversationId == null) {
|
||||
return
|
||||
}
|
||||
var group: StatusMessageGroup? = if (conversationId != null) messageGroups.get(conversationId) else null
|
||||
val notificationManager: NotificationManager = notificationManager()
|
||||
|
||||
if (group == null) {
|
||||
group = StatusMessageGroup(conversationId)
|
||||
}
|
||||
messageGroups.put(conversationId, group)
|
||||
|
||||
group.addMessage(createMessage(bundle))
|
||||
showMessages(bundle)
|
||||
}
|
||||
|
||||
fun showMessages(bundle: Bundle) {
|
||||
val conversationId: String? = bundle.getString("conversationId")
|
||||
if (conversationId == null) {
|
||||
return
|
||||
}
|
||||
val group: StatusMessageGroup? = if (conversationId != null) messageGroups.get(conversationId) else null
|
||||
val notificationManager: NotificationManager = notificationManager()
|
||||
val messagingStyle: NotificationCompat.MessagingStyle = NotificationCompat.MessagingStyle("Me")
|
||||
var messages = if (group != null) group.getMessages() else null
|
||||
if (messages == null || messages.size == 0) {
|
||||
notificationManager.cancel(conversationId.hashCode())
|
||||
return
|
||||
}
|
||||
for (i in messages.indices) {
|
||||
val message: StatusMessage = messages.get(i)
|
||||
messagingStyle.addMessage(message.text, message.timestamp, message.getAuthor())
|
||||
}
|
||||
val title: String? = bundle.getString("title")
|
||||
if (title != null) {
|
||||
messagingStyle.setConversationTitle(title)
|
||||
}
|
||||
val builder: NotificationCompat.Builder = NotificationCompat.Builder(context, CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_stat_notify_status)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
|
||||
.setStyle(messagingStyle)
|
||||
.setGroup(conversationId)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setGroupSummary(true)
|
||||
.setContentIntent(createGroupOnTapIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
|
||||
.setDeleteIntent(createGroupOnDismissedIntent(context, conversationId.hashCode(), conversationId, bundle.getString("deepLink")))
|
||||
.setNumber(messages.size + 1)
|
||||
.setAutoCancel(true)
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
builder.setVibrate(LongArray(0))
|
||||
}
|
||||
notificationManager.notify(conversationId.hashCode(), builder.build())
|
||||
}
|
||||
|
||||
inner class StatusMessageGroup(val id: String) {
|
||||
private var messages: java.util.ArrayList<StatusMessage>
|
||||
|
||||
init {
|
||||
messages = java.util.ArrayList<StatusMessage>()
|
||||
}
|
||||
|
||||
fun getMessages(): java.util.ArrayList<StatusMessage> {
|
||||
return messages
|
||||
}
|
||||
|
||||
fun addMessage(message: StatusMessage) {
|
||||
messages.add(message)
|
||||
}
|
||||
|
||||
fun removeMessage(id: String) {
|
||||
val newMessages: java.util.ArrayList<StatusMessage> = java.util.ArrayList<StatusMessage>()
|
||||
for (message in messages) {
|
||||
if (message.id != id) {
|
||||
newMessages.add(message)
|
||||
}
|
||||
}
|
||||
messages = newMessages
|
||||
}
|
||||
}
|
||||
|
||||
inner class StatusMessage(val id: String, author: Person, timestamp: Long, text: String) {
|
||||
fun getAuthor(): Person {
|
||||
return author
|
||||
}
|
||||
|
||||
private val author: Person
|
||||
val timestamp: Long
|
||||
val text: String
|
||||
|
||||
init {
|
||||
this.author = author
|
||||
this.timestamp = timestamp
|
||||
this.text = text
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeGroup(groupId: String) {
|
||||
messageGroups.remove(groupId)
|
||||
}
|
||||
|
||||
private fun cleanGroup(groupId: String) {
|
||||
removeGroup(groupId)
|
||||
if (messageGroups.size == 0) {
|
||||
notificationManager.cancelAll()
|
||||
}
|
||||
}
|
||||
|
||||
fun start() {
|
||||
Log.e(LOG_TAG, "Starting Foreground Service")
|
||||
val serviceIntent = Intent(context, ForegroundService::class.java)
|
||||
context.startService(serviceIntent)
|
||||
registerBroadcastReceiver()
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
Log.e(LOG_TAG, "Stopping Foreground Service")
|
||||
//NOTE: we cancel all the current notifications, because the intents can't be used anymore
|
||||
//since the broadcast receiver will be killed as well and won't be able to handle any intent
|
||||
notificationManager.cancelAll()
|
||||
val serviceIntent = Intent(context, ForegroundService::class.java)
|
||||
context.stopService(serviceIntent)
|
||||
context.unregisterReceiver(notificationActionReceiver)
|
||||
}
|
||||
}
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
package im.status.ethereum.pushnotifications;
|
||||
|
||||
import android.os.Build;
|
||||
import android.app.Application;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
|
||||
|
||||
class PushNotificationJsDelivery {
|
||||
|
||||
private ReactContext reactContext;
|
||||
|
||||
PushNotificationJsDelivery(ReactContext context){
|
||||
reactContext = context;
|
||||
}
|
||||
|
||||
String convertJSON(Bundle bundle) {
|
||||
try {
|
||||
JSONObject json = convertJSONObject(bundle);
|
||||
return json.toString();
|
||||
} catch (JSONException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// a Bundle is not a map, so we have to convert it explicitly
|
||||
private JSONObject convertJSONObject(Bundle bundle) throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
Set<String> keys = bundle.keySet();
|
||||
for (String key : keys) {
|
||||
Object value = bundle.get(key);
|
||||
if (value instanceof Bundle) {
|
||||
json.put(key, convertJSONObject((Bundle)value));
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
json.put(key, JSONObject.wrap(value));
|
||||
} else {
|
||||
json.put(key, value);
|
||||
}
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
void notifyNotification(Bundle bundle) {
|
||||
String bundleString = convertJSON(bundle);
|
||||
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putString("dataJSON", bundleString);
|
||||
|
||||
sendEvent("remoteNotificationReceived", params);
|
||||
}
|
||||
|
||||
void notifyNotificationAction(Bundle bundle) {
|
||||
String bundleString = convertJSON(bundle);
|
||||
|
||||
WritableMap params = Arguments.createMap();
|
||||
params.putString("dataJSON", bundleString);
|
||||
|
||||
sendEvent("notificationActionReceived", params);
|
||||
}
|
||||
|
||||
void sendEvent(String eventName, Object params) {
|
||||
if (reactContext.hasActiveCatalystInstance()) {
|
||||
reactContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
||||
.emit(eventName, params);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package im.status.ethereum.pushnotifications
|
||||
|
||||
import android.os.Build
|
||||
import android.app.Application
|
||||
import android.app.NotificationManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import com.facebook.react.bridge.ReactContext
|
||||
import com.facebook.react.bridge.Arguments
|
||||
import com.facebook.react.bridge.WritableMap
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
internal class PushNotificationJsDelivery(context: ReactContext) {
|
||||
companion object {
|
||||
const val LOG_TAG = "PushNotification"
|
||||
}
|
||||
|
||||
private val reactContext: ReactContext
|
||||
|
||||
init {
|
||||
reactContext = context
|
||||
}
|
||||
|
||||
fun convertJSON(bundle: Bundle): String? {
|
||||
return try {
|
||||
val json: JSONObject = convertJSONObject(bundle)
|
||||
json.toString()
|
||||
} catch (e: JSONException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// a Bundle is not a map, so we have to convert it explicitly
|
||||
@kotlin.Throws(JSONException::class)
|
||||
private fun convertJSONObject(bundle: Bundle): JSONObject {
|
||||
val json = JSONObject()
|
||||
val keys: Set<String> = bundle.keySet()
|
||||
for (key in keys) {
|
||||
val value: Any? = bundle.get(key)
|
||||
if (value == null) {
|
||||
continue
|
||||
} else if (value is Bundle) {
|
||||
json.put(key, convertJSONObject(value as Bundle))
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
json.put(key, JSONObject.wrap(value))
|
||||
} else {
|
||||
json.put(key, value)
|
||||
}
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
fun notifyNotification(bundle: Bundle) {
|
||||
val bundleString = convertJSON(bundle)
|
||||
val params: WritableMap = Arguments.createMap()
|
||||
params.putString("dataJSON", bundleString)
|
||||
sendEvent("remoteNotificationReceived", params)
|
||||
}
|
||||
|
||||
fun notifyNotificationAction(bundle: Bundle) {
|
||||
val bundleString = convertJSON(bundle)
|
||||
val params: WritableMap = Arguments.createMap()
|
||||
params.putString("dataJSON", bundleString)
|
||||
sendEvent("notificationActionReceived", params)
|
||||
}
|
||||
|
||||
fun sendEvent(eventName: String, params: Any?) {
|
||||
if (reactContext.hasActiveCatalystInstance()) {
|
||||
reactContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit(eventName, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package im.status.ethereum.pushnotifications;
|
||||
|
||||
import im.status.ethereum.pushnotifications.PushNotification;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.uimanager.ViewManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PushNotificationPackage implements ReactPackage {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
return Collections.<NativeModule>singletonList(new PushNotification(reactContext));
|
||||
}
|
||||
|
||||
public List<Class<? extends JavaScriptModule>> createJSModules() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package im.status.ethereum.pushnotifications
|
||||
|
||||
import im.status.ethereum.pushnotifications.PushNotification
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.bridge.JavaScriptModule
|
||||
import com.facebook.react.bridge.NativeModule
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.uimanager.ViewManager
|
||||
|
||||
class PushNotificationPackage : ReactPackage {
|
||||
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
||||
return listOf<NativeModule>(PushNotification(reactContext))
|
||||
}
|
||||
|
||||
fun createJSModules(): List<java.lang.Class<out JavaScriptModule?>> {
|
||||
return emptyList<java.lang.Class<out JavaScriptModule?>>()
|
||||
}
|
||||
|
||||
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
package im.status.ethereum.pushnotifications;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.common.executors.CallerThreadExecutor;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.imagepipeline.common.Priority;
|
||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||
import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||
|
||||
import android.util.Log;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static im.status.ethereum.pushnotifications.PushNotification.LOG_TAG;
|
||||
|
||||
public class PushNotificationPicturesAggregator {
|
||||
interface Callback {
|
||||
public void call(Bitmap largeIconImage, Bitmap bigPictureImage);
|
||||
}
|
||||
|
||||
private AtomicInteger count = new AtomicInteger(0);
|
||||
|
||||
private Bitmap largeIconImage;
|
||||
private Bitmap bigPictureImage;
|
||||
|
||||
private Callback callback;
|
||||
|
||||
public PushNotificationPicturesAggregator(Callback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public void setBigPicture(Bitmap bitmap) {
|
||||
this.bigPictureImage = bitmap;
|
||||
this.finished();
|
||||
}
|
||||
|
||||
public void setBigPictureUrl(Context context, String url) {
|
||||
if(null == url) {
|
||||
this.setBigPicture(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = null;
|
||||
|
||||
try {
|
||||
uri = Uri.parse(url);
|
||||
} catch(Exception ex) {
|
||||
Log.e(LOG_TAG, "Failed to parse bigPictureUrl", ex);
|
||||
this.setBigPicture(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final PushNotificationPicturesAggregator aggregator = this;
|
||||
|
||||
this.downloadRequest(context, uri, new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
public void onNewResultImpl(@Nullable Bitmap bitmap) {
|
||||
aggregator.setBigPicture(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailureImpl(DataSource dataSource) {
|
||||
aggregator.setBigPicture(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setLargeIcon(Bitmap bitmap) {
|
||||
this.largeIconImage = bitmap;
|
||||
this.finished();
|
||||
}
|
||||
|
||||
public void setLargeIconUrl(Context context, String url) {
|
||||
if(null == url) {
|
||||
this.setLargeIcon(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Uri uri = null;
|
||||
|
||||
try {
|
||||
uri = Uri.parse(url);
|
||||
} catch(Exception ex) {
|
||||
Log.e(LOG_TAG, "Failed to parse largeIconUrl", ex);
|
||||
this.setLargeIcon(null);
|
||||
return;
|
||||
}
|
||||
|
||||
final PushNotificationPicturesAggregator aggregator = this;
|
||||
|
||||
this.downloadRequest(context, uri, new BaseBitmapDataSubscriber() {
|
||||
@Override
|
||||
public void onNewResultImpl(@Nullable Bitmap bitmap) {
|
||||
aggregator.setLargeIcon(bitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailureImpl(DataSource dataSource) {
|
||||
aggregator.setLargeIcon(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void downloadRequest(Context context, Uri uri, BaseBitmapDataSubscriber subscriber) {
|
||||
ImageRequest imageRequest = ImageRequestBuilder
|
||||
.newBuilderWithSource(uri)
|
||||
.setRequestPriority(Priority.HIGH)
|
||||
.setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
|
||||
.build();
|
||||
|
||||
if(!Fresco.hasBeenInitialized()) {
|
||||
Fresco.initialize(context);
|
||||
}
|
||||
|
||||
DataSource<CloseableReference<CloseableImage>> dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, context);
|
||||
|
||||
dataSource.subscribe(subscriber, CallerThreadExecutor.getInstance());
|
||||
}
|
||||
|
||||
private void finished() {
|
||||
synchronized(this.count) {
|
||||
int val = this.count.incrementAndGet();
|
||||
|
||||
if(val >= 2 && this.callback != null) {
|
||||
this.callback.call(this.largeIconImage, this.bigPictureImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package im.status.ethereum.pushnotifications
|
||||
|
||||
import com.facebook.common.executors.CallerThreadExecutor
|
||||
import com.facebook.common.references.CloseableReference
|
||||
import com.facebook.datasource.DataSource
|
||||
import com.facebook.drawee.backends.pipeline.Fresco
|
||||
import com.facebook.imagepipeline.common.Priority
|
||||
import com.facebook.imagepipeline.core.ImagePipeline
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
|
||||
import com.facebook.imagepipeline.image.CloseableImage
|
||||
import com.facebook.imagepipeline.request.ImageRequest
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder
|
||||
import android.util.Log
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class PushNotificationPicturesAggregator(private val callback: Callback?) {
|
||||
companion object {
|
||||
const val LOG_TAG = "PushNotification"
|
||||
}
|
||||
|
||||
interface Callback {
|
||||
fun call(largeIconImage: Bitmap?, bigPictureImage: Bitmap?)
|
||||
}
|
||||
|
||||
private val count: AtomicInteger = AtomicInteger(0)
|
||||
private var largeIconImage: Bitmap? = null
|
||||
private var bigPictureImage: Bitmap? = null
|
||||
fun setBigPicture(bitmap: Bitmap?) {
|
||||
bigPictureImage = bitmap
|
||||
finished()
|
||||
}
|
||||
|
||||
fun setBigPictureUrl(context: Context, url: String?) {
|
||||
if (null == url) {
|
||||
setBigPicture(null)
|
||||
return
|
||||
}
|
||||
var uri: Uri? = null
|
||||
uri = try {
|
||||
Uri.parse(url)
|
||||
} catch (ex: java.lang.Exception) {
|
||||
Log.e(LOG_TAG, "Failed to parse bigPictureUrl", ex)
|
||||
setBigPicture(null)
|
||||
return
|
||||
}
|
||||
val aggregator = this
|
||||
downloadRequest(context, uri, object : BaseBitmapDataSubscriber() {
|
||||
override fun onNewResultImpl(bitmap: Bitmap?) {
|
||||
aggregator.setBigPicture(bitmap)
|
||||
}
|
||||
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
|
||||
aggregator.setBigPicture(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun setLargeIcon(bitmap: Bitmap?) {
|
||||
largeIconImage = bitmap
|
||||
finished()
|
||||
}
|
||||
|
||||
fun setLargeIconUrl(context: Context, url: String?) {
|
||||
if (null == url) {
|
||||
setLargeIcon(null)
|
||||
return
|
||||
}
|
||||
var uri: Uri? = null
|
||||
uri = try {
|
||||
Uri.parse(url)
|
||||
} catch (ex: java.lang.Exception) {
|
||||
Log.e(LOG_TAG, "Failed to parse largeIconUrl", ex)
|
||||
setLargeIcon(null)
|
||||
return
|
||||
}
|
||||
val aggregator = this
|
||||
downloadRequest(context, uri, object : BaseBitmapDataSubscriber() {
|
||||
override fun onNewResultImpl(bitmap: Bitmap?) {
|
||||
aggregator.setLargeIcon(bitmap)
|
||||
}
|
||||
|
||||
override fun onFailureImpl(dataSource: DataSource<CloseableReference<CloseableImage>>) {
|
||||
aggregator.setLargeIcon(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun downloadRequest(context: Context, uri: Uri?, subscriber: BaseBitmapDataSubscriber) {
|
||||
val imageRequest: ImageRequest = ImageRequestBuilder
|
||||
.newBuilderWithSource(uri)
|
||||
.setRequestPriority(Priority.HIGH)
|
||||
.setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
|
||||
.build()
|
||||
if (!Fresco.hasBeenInitialized()) {
|
||||
Fresco.initialize(context)
|
||||
}
|
||||
val dataSource: DataSource<CloseableReference<CloseableImage>> = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, context)
|
||||
dataSource.subscribe(subscriber, CallerThreadExecutor.getInstance())
|
||||
}
|
||||
|
||||
private fun finished() {
|
||||
synchronized(count) {
|
||||
val `val`: Int = count.incrementAndGet()
|
||||
if (`val` >= 2 && callback != null) {
|
||||
callback.call(largeIconImage, bigPictureImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+613
-33
@@ -11129,16 +11129,16 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "com/google/errorprone/error_prone_annotations/2.24.0",
|
||||
"path": "com/google/errorprone/error_prone_annotations/2.24.1",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"error_prone_annotations-2.24.0.pom": {
|
||||
"sha1": "a267fc6adc4f7e83c84ecd3bfa3d757f0faa4c4e",
|
||||
"sha256": "sha256-MDJ5+CcjbFXBkhu9hjL941NUxcNcAma8CF+apovLcV0="
|
||||
"error_prone_annotations-2.24.1.pom": {
|
||||
"sha1": "e36d508134193b5b782a79cd834fef22f96850ea",
|
||||
"sha256": "sha256-dW0LPDkGWZ2XMUDuziZuDej08ZKvFLGQBq8Un+kO+NU="
|
||||
},
|
||||
"error_prone_annotations-2.24.0.jar": {
|
||||
"sha1": "29dca9118b577ad24533e563438feb8fad51427e",
|
||||
"sha256": "sha256-qIvYgSTI1MmFGrgjrcOo8EsKE68VydgLXs9J9aejIdI="
|
||||
"error_prone_annotations-2.24.1.jar": {
|
||||
"sha1": "32b299e45105aa9b0df8279c74dc1edfcf313ff0",
|
||||
"sha256": "sha256-Gf4vcVXSDqCTFoUnmZ2pgQgQPuVG0ei3JrxLJ8MaPDA="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -11232,12 +11232,12 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "com/google/errorprone/error_prone_parent/2.24.0",
|
||||
"path": "com/google/errorprone/error_prone_parent/2.24.1",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"error_prone_parent-2.24.0.pom": {
|
||||
"sha1": "2aca1bd2b01e085422633daa951905ea36ccd009",
|
||||
"sha256": "sha256-XfQPVmlQqy86ZF0z2LcXGB7v9OplOO+2/ZuRL+obie4="
|
||||
"error_prone_parent-2.24.1.pom": {
|
||||
"sha1": "20bcb755b5f180723ef1b055f34a1834b7410f54",
|
||||
"sha256": "sha256-y589WAUC7OXhJCnX96x1/kerhdat91zd+G+lokrvpI8="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -16913,6 +16913,17 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"org.jetbrains.kotlin.jvm.gradle.plugin-1.9.0.pom": {
|
||||
"sha1": "896c88badef90b092f519041d1ee201eb5d1b1a0",
|
||||
"sha256": "sha256-Bb7sz992ArEqfazluEynL3ao8E3ookIUo/WSxzYmtrc="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-android-extensions/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -16943,6 +16954,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-android-extensions/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-android-extensions-1.9.0.pom": {
|
||||
"sha1": "c08cc427bc37faf86bd41de0b64e8ac67899a06b",
|
||||
"sha256": "sha256-WOHIg3JukUaVIDV95u0Xw/mSnI4IsRdKJ6OtbsDrzVI="
|
||||
},
|
||||
"kotlin-android-extensions-1.9.0.jar": {
|
||||
"sha1": "ca23de2a64833d204f9af248e265274f3b8c0333",
|
||||
"sha256": "sha256-IGdbuyp7aYKnFEUYpRi5qJZfkK6JuMmHiJgBIkSh6jM="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-annotations-jvm/1.3.72",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17018,6 +17044,51 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-build-common/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-build-common-1.9.0.pom": {
|
||||
"sha1": "ab861d4e7cbca289fa3d215daad0cbfb2a2cfb82",
|
||||
"sha256": "sha256-6itlfmsETYgfDT/f/BPOKIwlQYxipbBL/64XeJDs6EU="
|
||||
},
|
||||
"kotlin-build-common-1.9.0.jar": {
|
||||
"sha1": "68e075bff7dca0d2d444c19279afe6882f311a19",
|
||||
"sha256": "sha256-txTWfNj1bTe4Pqj2XdysZAXFdix9n2EV81J0o5GxW2w="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-build-tools-api/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-build-tools-api-1.9.0.pom": {
|
||||
"sha1": "ab0f33a948936b4ef2b51a8b82720fe5982184e5",
|
||||
"sha256": "sha256-iIFxmCZbm8BrPzSTWyv2qpj0gA7OvBzOQREN70m3pCE="
|
||||
},
|
||||
"kotlin-build-tools-api-1.9.0.jar": {
|
||||
"sha1": "5d33265af7283ca4d7b1f3115b83b54545f4cfab",
|
||||
"sha256": "sha256-0ZyavegKnPQtJezJLLEUjtOhXu2yZy2qHkZubaWQ7sY="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-build-tools-impl/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-build-tools-impl-1.9.0.pom": {
|
||||
"sha1": "3d7403fd4d2f8914b46b250f6746da17c5a1ba47",
|
||||
"sha256": "sha256-3Qrb3SO5XBP8N2JAqCoL/F1k37eBON7GJASX6QVtQIw="
|
||||
},
|
||||
"kotlin-build-tools-impl-1.9.0.jar": {
|
||||
"sha1": "1ce2bdaf6dd80d4e7748058231059766e298a6de",
|
||||
"sha256": "sha256-1yw3nxx1eZrAxm5sMVj2iGW9/tGlw1xepBwnihuAUBo="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-compiler-embeddable/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17048,6 +17119,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-compiler-embeddable/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-compiler-embeddable-1.9.0.pom": {
|
||||
"sha1": "079ce81c461cfa0c3ad6b05b4a40b23bf821dbd2",
|
||||
"sha256": "sha256-7jyHHO39bM9A0ESUQJbihlXeL5NF2ET+7GPMqcXkt8w="
|
||||
},
|
||||
"kotlin-compiler-embeddable-1.9.0.jar": {
|
||||
"sha1": "2c8c3a7402becca891f12739b3e9fd2dc2adbd7a",
|
||||
"sha256": "sha256-lTtQaR103SGf8/ZkRnDD5UnYfN/AphVPFgKmoKXf43M="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-compiler-runner/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17078,6 +17164,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-compiler-runner/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-compiler-runner-1.9.0.pom": {
|
||||
"sha1": "f2f333b81d78150bca741b75646a042f8a69be2c",
|
||||
"sha256": "sha256-GIN9BlVntn5bzy4wTYWm4rbSRDz4En9AdnNKTo3yo2E="
|
||||
},
|
||||
"kotlin-compiler-runner-1.9.0.jar": {
|
||||
"sha1": "4cf7c2cb6bfd82876e592697d3ad9bc835b21010",
|
||||
"sha256": "sha256-BbWlXUlJFhpxT838ZKXwTYejZMm73IZBs7t6VVfkPqo="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-daemon-client/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17108,6 +17209,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-daemon-client/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-daemon-client-1.9.0.pom": {
|
||||
"sha1": "af448a4a8fdd5c0bff3d93fa78a2e495779e18a5",
|
||||
"sha256": "sha256-sv50dBgjzM6JMXCWkg3LnOXBa460Z5jTn9jmdwPFidc="
|
||||
},
|
||||
"kotlin-daemon-client-1.9.0.jar": {
|
||||
"sha1": "a7aff9bf77b5c882789cc495262def8948d93c5d",
|
||||
"sha256": "sha256-WoyXfn3H8N4tka5Yva3CBBXe9M9dST5aGD+WxZeZAyA="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-daemon-embeddable/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17138,6 +17254,51 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-daemon-embeddable/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-daemon-embeddable-1.9.0.pom": {
|
||||
"sha1": "216bc4d3f72fb679b1f79e73a730cac6e5cd3fa5",
|
||||
"sha256": "sha256-OOMsu5GFenIGy/3cON1O4L5mX0CgEV/21NF348f4kq8="
|
||||
},
|
||||
"kotlin-daemon-embeddable-1.9.0.jar": {
|
||||
"sha1": "186543f5e28cf8d9a0290fecc2cf34301c40a65c",
|
||||
"sha256": "sha256-lDGCoTfs1A5ikFjaUtszbO6bZm+AmWtlWELyCmNz35o="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugins-bom/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-gradle-plugins-bom-1.9.0.pom": {
|
||||
"sha1": "511e9094170ffec75cbb76b58cfe7f1a058dc046",
|
||||
"sha256": "sha256-XTmtkDDf0zsLrCn3UAAJzmKHYulP5h8BF3rhNL24D6E="
|
||||
},
|
||||
"kotlin-gradle-plugins-bom-1.9.0.module": {
|
||||
"sha1": "b6bc353c189d28e9e0419f0635df8121ae8c8cbf",
|
||||
"sha256": "sha256-/SufabguQ8tQnwhBsYvzxd99q18eC+oDT8CypZnW4pY="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-annotations/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-gradle-plugin-annotations-1.9.0.pom": {
|
||||
"sha1": "b1b0737be6ae375ea12ea9e7cbc6c6f1a4832ec1",
|
||||
"sha256": "sha256-qjXuWp0dpZMV92nH/tsshwBWD1PFaAupKwsniBpKQnE="
|
||||
},
|
||||
"kotlin-gradle-plugin-annotations-1.9.0.jar": {
|
||||
"sha1": "42d1ea51a701505256d5bd54d9152819937835dc",
|
||||
"sha256": "sha256-KiZbNbx8VYIUJukbajzU9LkWOKC8fIffz+TgX36IRzg="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17180,6 +17341,53 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-gradle-plugin-api-1.9.0.pom": {
|
||||
"sha1": "7c80dda386f89cf10d0c3ba399176f5ad26cf057",
|
||||
"sha256": "sha256-VPHdp1Zn7esbrIpY9KNsh4wPBJGGA2vJOnAyjNnNfaw="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0-gradle70.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0-gradle71.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0-gradle74.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0-gradle75.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0-gradle76.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0-gradle80.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0-gradle81.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0.jar": {
|
||||
"sha1": "82a9928a60c442822fccaf08aa212528a85fbf25",
|
||||
"sha256": "sha256-AhbssMeA+V47EBoOlc/DZ/LvVrBvLeqqqZJaDxF8INk="
|
||||
},
|
||||
"kotlin-gradle-plugin-api-1.9.0.module": {
|
||||
"sha1": "cede46e2a2c9f2abeaa726fef80f85e451a6b5b3",
|
||||
"sha256": "sha256-Gu1WWijZE0KsWkuhRsKFal67xjkoXFKVUhefr8dhTds="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.7.22",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17195,6 +17403,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-gradle-plugin-idea-proto-1.9.0.pom": {
|
||||
"sha1": "19b5a92d55669744290e83b5a7d643d1fbd83f65",
|
||||
"sha256": "sha256-QeElaFOKOjQHBMlww6T2D/WAH/3OQlymjKLAtHIhclA="
|
||||
},
|
||||
"kotlin-gradle-plugin-idea-proto-1.9.0.jar": {
|
||||
"sha1": "d7e3ab4f0b7f7966e446f516fddf331475319f53",
|
||||
"sha256": "sha256-54mY6iK81fVtE5gszBJMnHM3vRUHVMvcivKhSNdslwA="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.7.22",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17218,6 +17441,29 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-gradle-plugin-idea-1.9.0.pom": {
|
||||
"sha1": "e3c307cf7ee2c63e9535edb403466dc49aa5d5d9",
|
||||
"sha256": "sha256-W/gGnkUbY0wdxLRpLpKO9pEanrLtk3TeIPRC3AloBo0="
|
||||
},
|
||||
"kotlin-gradle-plugin-idea-1.9.0-test-fixtures.jar": {
|
||||
"sha1": "af6020bc0aa902bb438e69aa0eca58438111136b",
|
||||
"sha256": "sha256-MAj3geetkPywHc2d6pbg9ky48bMYT5T4p9jNHeGlsfI="
|
||||
},
|
||||
"kotlin-gradle-plugin-idea-1.9.0.jar": {
|
||||
"sha1": "5538265e4961666c769825f4543ab445cbc45bdc",
|
||||
"sha256": "sha256-C7JszuNyr7m1hPKFfofMLz9TK2Kk9/pFdVjzr8Or6ZU="
|
||||
},
|
||||
"kotlin-gradle-plugin-idea-1.9.0.module": {
|
||||
"sha1": "186d2bb5113962059551805961d9c80a5b06a80c",
|
||||
"sha256": "sha256-pVg82bC0AS6VWtlhn7kBe1saosJRsFltr1M0kExcpls="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17260,6 +17506,53 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-gradle-plugin-model-1.9.0.pom": {
|
||||
"sha1": "b9b395f72a747e294548584cb93b9e48d2d8dad7",
|
||||
"sha256": "sha256-QcXjVzTqEZA+IQr5i3eHB08mQab0C0X5vpwRAAga5Y0="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0-gradle70.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0-gradle71.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0-gradle74.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0-gradle75.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0-gradle76.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0-gradle80.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0-gradle81.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0.jar": {
|
||||
"sha1": "d64789139fb89d78f2128d6b5be8a164d8669a72",
|
||||
"sha256": "sha256-/wWqtoizSnmSp+NfncTewlhD8Tqelwrv5W3T4SKghBU="
|
||||
},
|
||||
"kotlin-gradle-plugin-model-1.9.0.module": {
|
||||
"sha1": "cc984614ad443557e13d3a07c6dbcd8ce214eec4",
|
||||
"sha256": "sha256-ZvS5FEcM80bgXQP4otWenHRvpu3PZMcS8XagiBqsPkk="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17302,6 +17595,53 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-gradle-plugin/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-gradle-plugin-1.9.0.pom": {
|
||||
"sha1": "75f5520fa218e0833a1d4332bc10cf325925f63f",
|
||||
"sha256": "sha256-RSdrCf+7uANPvQRwj5D34xtw1orgI7LPs8EdiG532DY="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0-gradle70.jar": {
|
||||
"sha1": "70f96081d51820be6e998ae76591ca2485298880",
|
||||
"sha256": "sha256-tpVPc4pbBeMHcvq9tFnIHVjfGz2oBqng7qcQ9Oj1Afw="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0-gradle71.jar": {
|
||||
"sha1": "a237533df09406429a90e4cf6eddbd665fb879ef",
|
||||
"sha256": "sha256-eb2kXiIyG1eXss3mhJ9KwFUgfVcAUxsEI+aMURdlers="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0-gradle74.jar": {
|
||||
"sha1": "8ca3abcf0134fac694a3d78e78cc879ac37efc40",
|
||||
"sha256": "sha256-p0Pj9keSxLrCRuE8GYSIIGskZnHvzXB7NQw+IsEWW50="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0-gradle75.jar": {
|
||||
"sha1": "fa282ffe215f034c063a361d338f58b14479b336",
|
||||
"sha256": "sha256-y9fMKOqRe48m9sVeYWVSYOGtQ3ZNM1edrj91pJMa/vI="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0-gradle76.jar": {
|
||||
"sha1": "5e15ae8365f8750303ad66e169cb8fb25e20f408",
|
||||
"sha256": "sha256-k6DXXAMnUB4h2z8pprUoju8uIjFyan/esXYMwNmi6/I="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0-gradle80.jar": {
|
||||
"sha1": "3e9dd06e2aabd397923535a42118bc861bcff9eb",
|
||||
"sha256": "sha256-JnLbEUAqZqYyLpzT9nV2rbtyq+jkdNkStqrzP5QGuPY="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0-gradle81.jar": {
|
||||
"sha1": "8cfc2ba810d67ffb9a931d3785163acb5ff859fd",
|
||||
"sha256": "sha256-2UOg4ntHRuAfg0BaSfUmoi9HGx2x1GOdb84m1xHv+/4="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0.jar": {
|
||||
"sha1": "a031b8e97a70eb71b3d8a9298a82d153fc40c938",
|
||||
"sha256": "sha256-yxGBud8e8LclfOPYc6mWy79G3K6qnD+OxIBWE3V5BxU="
|
||||
},
|
||||
"kotlin-gradle-plugin-1.9.0.module": {
|
||||
"sha1": "f15d8f1e4dff619035904e448de88cbbde259d28",
|
||||
"sha256": "sha256-/mOWRe2PvcW8Et588lRopNsx52O6ea26ogahTeYP0xQ="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17333,16 +17673,31 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.7.22",
|
||||
"path": "org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-klib-commonizer-embeddable-1.7.22.pom": {
|
||||
"sha1": "5b3e5c9d161d6de851b75af4b73a3cd2bf438140",
|
||||
"sha256": "sha256-Ft3srgIpxS0bNHWY5je65P8si2Nj1+AIbNa2bfzIltw="
|
||||
"kotlin-klib-commonizer-api-1.9.0.pom": {
|
||||
"sha1": "dc9b1e38fb03d23112825bc6ab3ab8144ef8d42a",
|
||||
"sha256": "sha256-hE+SCI/0j4v64lYh4yStw1d5CM9lD0/bJWlhC4+vaz4="
|
||||
},
|
||||
"kotlin-klib-commonizer-embeddable-1.7.22.jar": {
|
||||
"sha1": "f84170d46b52ac44408be1055fb12849bfff4629",
|
||||
"sha256": "sha256-3owxqAkvto74DzSjy9+x3NQCB7Z8/4fUC1xHnsamjs4="
|
||||
"kotlin-klib-commonizer-api-1.9.0.jar": {
|
||||
"sha1": "35782a8198131f9ee6eec26d7bf60b1e0a508bc5",
|
||||
"sha256": "sha256-hd4ulysLtvRSjB90j0PEhmMUX8hTnsqWwT0bwNo/k10="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-klib-commonizer-embeddable-1.9.0.pom": {
|
||||
"sha1": "210d03d9e5afa2938c9a6d20ce164c59277dc437",
|
||||
"sha256": "sha256-L9STJpNwf9ujj80diJL7TdPPAIC5LxJSX8cF42G2eTs="
|
||||
},
|
||||
"kotlin-klib-commonizer-embeddable-1.9.0.jar": {
|
||||
"sha1": "bae135d2d5ed2ae9c2c9b7f8300dbcb345ef8bc7",
|
||||
"sha256": "sha256-zdYmTXJPcRKakwi+JEAsqMccKrICywO2hEmdqXrdS4Q="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -17377,6 +17732,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-native-utils/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-native-utils-1.9.0.pom": {
|
||||
"sha1": "80b8f19f18a627c23ed18cf2a44ef7eabc84da96",
|
||||
"sha256": "sha256-Kazy3vkmve3Z6yBOx3Tnhq3Ek0EGqSlJAK81FVbn52o="
|
||||
},
|
||||
"kotlin-native-utils-1.9.0.jar": {
|
||||
"sha1": "13264bd5949f9e68c5d53a6d394b800b725dc577",
|
||||
"sha256": "sha256-D0gF93S/tcG20UXf5g09ILGu4ACJbOjs8+7/hM5EAjs="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-project-model/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17407,6 +17777,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-project-model/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-project-model-1.9.0.pom": {
|
||||
"sha1": "fe67d9afc6940fe0019e18fca37a37c58976536b",
|
||||
"sha256": "sha256-6sHGTGAMYAJ7jmjfrTogPEFbn6IkWB1oz6Xnc+hWa3g="
|
||||
},
|
||||
"kotlin-project-model-1.9.0.jar": {
|
||||
"sha1": "7c6006a523220e0a82f4954652c3140f7353e85a",
|
||||
"sha256": "sha256-rgk26bTGkh8D73eTYcrLu3XtKyIQR5+S9l8cushuNAw="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-reflect/1.1.3-2",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17512,6 +17897,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-reflect/1.6.10",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-reflect-1.6.10.pom": {
|
||||
"sha1": "535560dfd5b3dba98f57b12f3e921c7a83d0aafe",
|
||||
"sha256": "sha256-V5BVJCdKAK4CiqzMJyg/a8WSWpNKBGwcxdBsjuTW1ak="
|
||||
},
|
||||
"kotlin-reflect-1.6.10.jar": {
|
||||
"sha1": "1cbe9c92c12a94eea200d23c2bbaedaf3daf5132",
|
||||
"sha256": "sha256-MnesECrheq0QpVq+x1/1aWyNEJeQOWQ0tJbnUIeFQgM="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-reflect/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17602,6 +18002,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-scripting-common/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-scripting-common-1.9.0.pom": {
|
||||
"sha1": "326fa3ca7bb1705794879cbae1a9467b7d7aa09e",
|
||||
"sha256": "sha256-C/MG5PRe6xrDAk4pnUybcL4J7OmsAOcLL/mLPtQA8wc="
|
||||
},
|
||||
"kotlin-scripting-common-1.9.0.jar": {
|
||||
"sha1": "ea019fa9e3885795a81df270775e5609f7115307",
|
||||
"sha256": "sha256-KFxBwO7tWaDn3LGIuJ+BVBR/vLvVNUcc9piWzNonP4Q="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17632,6 +18047,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-scripting-compiler-embeddable-1.9.0.pom": {
|
||||
"sha1": "cba5705bf5fb615d2b72195faa25159e83e3a264",
|
||||
"sha256": "sha256-UJ5ikUwwvT43GcDgLk5Df8vxkRa4ouZYSPbUbFTsXJ4="
|
||||
},
|
||||
"kotlin-scripting-compiler-embeddable-1.9.0.jar": {
|
||||
"sha1": "1708703c0683b8d2a0a76048544480b94d3eb458",
|
||||
"sha256": "sha256-Rp3RIGiVipevzhGIoBRFVM3tZ95aGqC/XKUwOfKdizY="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17662,6 +18092,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-scripting-compiler-impl-embeddable-1.9.0.pom": {
|
||||
"sha1": "5459191b6fda205abb23fb4a767bb0f86e8cc12e",
|
||||
"sha256": "sha256-zaYp8KgbqcAKM95QJ/D4/7+fC45E5BlwNHdmu4WLTmM="
|
||||
},
|
||||
"kotlin-scripting-compiler-impl-embeddable-1.9.0.jar": {
|
||||
"sha1": "c825693be78dfcbc351a53856d75f4d11bb0e274",
|
||||
"sha256": "sha256-ZcUlbiyPvdpCbAMobmomgmfXEu+V7C05yVKSlXN4sE0="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-scripting-jvm/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17692,6 +18137,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-scripting-jvm/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-scripting-jvm-1.9.0.pom": {
|
||||
"sha1": "178d3c678207d53a68cab7ce61dd89867bacdc6f",
|
||||
"sha256": "sha256-Ymj4BVjTX9qSRruneTKWkrlNWJWWVQfL6KhIkSxQ5yo="
|
||||
},
|
||||
"kotlin-scripting-jvm-1.9.0.jar": {
|
||||
"sha1": "e2e6a0bd37a72cded2fec7882199e1bdc4e8bcb4",
|
||||
"sha256": "sha256-Zp3SvGBBjgdRU/2K2NllvlaetlwvMIOIyxEDud8TUVk="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-script-runtime/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -17722,6 +18182,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-script-runtime/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-script-runtime-1.9.0.pom": {
|
||||
"sha1": "a52322c274112401ded9622eb8d7eb2570fae853",
|
||||
"sha256": "sha256-FNwHhdGUFP7sEgI91z64DIPQeIC3PlS48IqHj0I9seY="
|
||||
},
|
||||
"kotlin-script-runtime-1.9.0.jar": {
|
||||
"sha1": "4b3102cbbb7e1b58d9d1adf89563f00069ffa7d1",
|
||||
"sha256": "sha256-2E0MykEFD6wqlS0RBEc8Mpr/BMULNxMuV1SiCYCa3Z8="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib-common/1.2.71",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -18022,6 +18497,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-stdlib-common-1.9.0.pom": {
|
||||
"sha1": "0a865a0c5a64618fd1dcca13d05dea4cba44c832",
|
||||
"sha256": "sha256-NmDTanD+s6vknxG5BjPkHTYnNXbwcbDhCdqbOg3wgqU="
|
||||
},
|
||||
"kotlin-stdlib-common-1.9.0.jar": {
|
||||
"sha1": "cd65c21cfd1eec4d44ef09f9f52b6d9f8a720636",
|
||||
"sha256": "sha256-KDJ0IEvXwCB4nsRvj45yr0JE1/VQszkqV+XKAGrXqiw="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.2.71",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -18202,6 +18692,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-stdlib-jdk7-1.9.0.pom": {
|
||||
"sha1": "5ce6c5278ef6e342507a96372ee534ed0b830e54",
|
||||
"sha256": "sha256-wRB08MiYqYuGPGFEcdQ409+Soewzgqbjf5NdfXGVS1o="
|
||||
},
|
||||
"kotlin-stdlib-jdk7-1.9.0.jar": {
|
||||
"sha1": "f320478990d05e0cfaadd74f9619fd6027adbf37",
|
||||
"sha256": "sha256-t5eaeqyUBV8Nnx/TtHzl/+HLYDKoQrqfvnGG8IUokXg="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.2.71",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -18367,6 +18872,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-stdlib-jdk8-1.9.0.pom": {
|
||||
"sha1": "4f04e57e4a12d4a59f620c63520681c4ff1d03e6",
|
||||
"sha256": "sha256-ZNWY3YjiUEZnMeIDBKtvBsu7urfuMitHA7a1n4gcT5I="
|
||||
},
|
||||
"kotlin-stdlib-jdk8-1.9.0.jar": {
|
||||
"sha1": "e000bd084353d84c9e888f6fb341dc1f5b79d948",
|
||||
"sha256": "sha256-pZ+iT98f+1lLrs2/D9EAEPl3zqECNtSH/jRkl3pzd/o="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib/1.1.3-2",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -18712,6 +19232,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-stdlib-1.9.0.pom": {
|
||||
"sha1": "4aea0493dfd3ee18df73bcd97db791c2ea5bb383",
|
||||
"sha256": "sha256-N3UiY/Ysw+MlCFbiiO5Kc9QQLXJqd2JwNPlIBsjBCso="
|
||||
},
|
||||
"kotlin-stdlib-1.9.0.jar": {
|
||||
"sha1": "8ee15ef0c67dc83d874f412d84378d7f0eb50b63",
|
||||
"sha256": "sha256-Na7/vi21qkRgcs7lD87ki3+p4vxRyjfAzH19C8OdlS4="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-stdlib/1.9.21",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -18781,6 +19316,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-tooling-core/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-tooling-core-1.9.0.pom": {
|
||||
"sha1": "0c1335f31eaaf304a866a1ca780bb75c05df79d1",
|
||||
"sha256": "sha256-KSbD/CFJWXAcjxYNjdyGn3s/pFKv3o3RdcjnwrvHBSI="
|
||||
},
|
||||
"kotlin-tooling-core-1.9.0.jar": {
|
||||
"sha1": "7b3e809d24d27d00072fd69eb130f8a6cbd365e1",
|
||||
"sha256": "sha256-5TmiJCi5ysZ/dbLWHytuKnGDUNe7MYI8fcqJB/kJma4="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-tooling-metadata/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -18826,6 +19376,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-util-io/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-util-io-1.9.0.pom": {
|
||||
"sha1": "9481aa1ef0a147a6ca09cbf4a5f72915303185e5",
|
||||
"sha256": "sha256-BUBa+xn3tgbFKr6haJqi1ew9qQL+4teoV2lDOeSgQJQ="
|
||||
},
|
||||
"kotlin-util-io-1.9.0.jar": {
|
||||
"sha1": "e9b6d257f0c94bdf8a0f4ab67d6ffc199a0deba7",
|
||||
"sha256": "sha256-gdNtxt4/C+rwTDl6BrVoITujc6vO9Tlyt7gvYX1a9ws="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-util-klib/1.6.20",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -18856,6 +19421,21 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/kotlin/kotlin-util-klib/1.9.0",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"kotlin-util-klib-1.9.0.pom": {
|
||||
"sha1": "45a02a0e039fcf3ef011e38917da856894dad326",
|
||||
"sha256": "sha256-acZHGaDgRjb2whMZ/lGKC1gieEu66P7YXmUfv6b4pFs="
|
||||
},
|
||||
"kotlin-util-klib-1.9.0.jar": {
|
||||
"sha1": "41df7b8939660817a283d9255d5f2e4b555eebc6",
|
||||
"sha256": "sha256-wtxr820zabdzdVq4k3uX5Tmk1F04Fb7xm+36FUsS0xM="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/jetbrains/markdown-jvm/0.2.1",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
@@ -20103,27 +20683,27 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/slf4j/slf4j-api/2.0.9",
|
||||
"path": "org/slf4j/slf4j-api/2.1.0-alpha1",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"slf4j-api-2.0.9.pom": {
|
||||
"sha1": "96a47b716e8241bca75562533e3a9fbb7ffbefb9",
|
||||
"sha256": "sha256-nDplT50KoaNLMXjr5TqJx2eS4dgfwelznL6bFhBSM4U="
|
||||
"slf4j-api-2.1.0-alpha1.pom": {
|
||||
"sha1": "c5362a45b3be4d9d110f5450822ea81b27adb1a0",
|
||||
"sha256": "sha256-QirqW+u6gwWzxhT6Zo7SKePJYQkw7PQvhzOO4F4minU="
|
||||
},
|
||||
"slf4j-api-2.0.9.jar": {
|
||||
"sha1": "7cf2726fdcfbc8610f9a71fb3ed639871f315340",
|
||||
"sha256": "sha256-CBiTDcjX3rtAMgRhFpHaWOSdQsULb/z9zgLa23w8K2w="
|
||||
"slf4j-api-2.1.0-alpha1.jar": {
|
||||
"sha1": "4e2525fc4327cc553c52e3937427c7cf2114735f",
|
||||
"sha256": "sha256-mrf/pkYgK0mdBZlaPsgvMbzLelA0XBUU2MtC7IzOo1M="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/slf4j/slf4j-bom/2.0.9",
|
||||
"path": "org/slf4j/slf4j-bom/2.1.0-alpha1",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"slf4j-bom-2.0.9.pom": {
|
||||
"sha1": "54c55d4b55c9f82d34434fe6a36e8c6b5671588a",
|
||||
"sha256": "sha256-6u9FhIB9gSxqC2z4OdXkf1DHVDJ3GbnOCB4nHRXaYkM="
|
||||
"slf4j-bom-2.1.0-alpha1.pom": {
|
||||
"sha1": "f8784ee61914057bec3db57b5efea4e6b0333c6f",
|
||||
"sha256": "sha256-qOgTiZePRAcJJBuYPTHvp4cRO+EbgYwsa82e0wlv1IU="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -20151,12 +20731,12 @@
|
||||
},
|
||||
|
||||
{
|
||||
"path": "org/slf4j/slf4j-parent/2.0.9",
|
||||
"path": "org/slf4j/slf4j-parent/2.1.0-alpha1",
|
||||
"repo": "https://repo.maven.apache.org/maven2",
|
||||
"files": {
|
||||
"slf4j-parent-2.0.9.pom": {
|
||||
"sha1": "fa1b5ef4b7f48daf822d408eebd3319fa6254c0f",
|
||||
"sha256": "sha256-wwfwQkFB8cUArlzw04aOSGbLIZ7V45m2bFoHxh6iH9U="
|
||||
"slf4j-parent-2.1.0-alpha1.pom": {
|
||||
"sha1": "ef0d60c3407004bb7e71643a13644f4bc2f3ec00",
|
||||
"sha256": "sha256-zkc1sfnIId4Lkrjb5AsHHG6jIHMuWTVZxupt+WX4c48="
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
+39
-26
@@ -2,6 +2,7 @@ androidx.activity:activity:1.0.0
|
||||
androidx.activity:activity:1.0.0-rc01
|
||||
androidx.activity:activity:1.2.4
|
||||
androidx.annotation:annotation-experimental:1.1.0
|
||||
androidx.annotation:annotation:1.0.0
|
||||
androidx.annotation:annotation:1.1.0
|
||||
androidx.annotation:annotation:1.2.0
|
||||
androidx.annotation:annotation:1.3.0
|
||||
@@ -10,6 +11,7 @@ androidx.appcompat:appcompat-resources:1.2.0
|
||||
androidx.appcompat:appcompat-resources:1.3.1
|
||||
androidx.appcompat:appcompat-resources:1.4.1
|
||||
androidx.appcompat:appcompat-resources:1.4.2
|
||||
androidx.appcompat:appcompat:1.0.0
|
||||
androidx.appcompat:appcompat:1.1.0-rc01
|
||||
androidx.appcompat:appcompat:1.2.0
|
||||
androidx.appcompat:appcompat:1.3.1
|
||||
@@ -38,6 +40,7 @@ androidx.coordinatorlayout:coordinatorlayout:1.0.0
|
||||
androidx.coordinatorlayout:coordinatorlayout:1.1.0
|
||||
androidx.core:core-ktx:1.6.0
|
||||
androidx.core:core-splashscreen:1.0.0
|
||||
androidx.core:core:1.0.0
|
||||
androidx.core:core:1.1.0
|
||||
androidx.core:core:1.1.0-rc01
|
||||
androidx.core:core:1.2.0
|
||||
@@ -125,10 +128,13 @@ androidx.tracing:tracing:1.1.0
|
||||
androidx.transition:transition:1.1.0
|
||||
androidx.transition:transition:1.2.0
|
||||
androidx.transition:transition:1.2.0-rc01
|
||||
androidx.vectordrawable:vectordrawable-animated:1.0.0
|
||||
androidx.vectordrawable:vectordrawable-animated:1.1.0
|
||||
androidx.vectordrawable:vectordrawable-animated:1.1.0-rc01
|
||||
androidx.vectordrawable:vectordrawable:1.0.0
|
||||
androidx.vectordrawable:vectordrawable:1.1.0
|
||||
androidx.vectordrawable:vectordrawable:1.1.0-rc01
|
||||
androidx.versionedparcelable:versionedparcelable:1.0.0
|
||||
androidx.versionedparcelable:versionedparcelable:1.1.0
|
||||
androidx.versionedparcelable:versionedparcelable:1.1.0-rc01
|
||||
androidx.versionedparcelable:versionedparcelable:1.1.1
|
||||
@@ -778,35 +784,37 @@ org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2
|
||||
org.jetbrains.kotlin:kotlin-android-extensions:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-android-extensions:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-android-extensions:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-annotations-jvm:1.3.72
|
||||
org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-build-common:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-build-common:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-build-tools-api:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-build-tools-impl:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-compiler-embeddable:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-compiler-runner:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-compiler-runner:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-compiler-runner:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-daemon-client:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-daemon-client:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-daemon-client:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-daemon-embeddable:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugins-bom:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-annotations:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-idea:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-api:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-klib-commonizer-embeddable:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-native-utils:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-native-utils:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-native-utils:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-project-model:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-project-model:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-project-model:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.1.3-2
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.2.0
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.3.20
|
||||
@@ -814,17 +822,17 @@ org.jetbrains.kotlin:kotlin-reflect:1.3.50
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.3.72
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.4.32
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.5.31
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.6.10
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.7.10
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-scripting-common:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-scripting-common:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-scripting-common:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-scripting-compiler-impl-embeddable:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-scripting-jvm:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-scripting-jvm:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-script-runtime:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-scripting-jvm:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-script-runtime:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.20
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50
|
||||
@@ -833,6 +841,7 @@ org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.5.31
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.20
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50
|
||||
@@ -841,6 +850,7 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.20
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50
|
||||
@@ -848,6 +858,7 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.1.3-2
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.2.71
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.3.20
|
||||
@@ -857,12 +868,13 @@ org.jetbrains.kotlin:kotlin-stdlib:1.4.32
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.5.31
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-tooling-core:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-tooling-core:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-tooling-metadata:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-util-io:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-util-io:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-util-io:1.9.0
|
||||
org.jetbrains.kotlin:kotlin-util-klib:1.6.20
|
||||
org.jetbrains.kotlin:kotlin-util-klib:1.7.22
|
||||
org.jetbrains.kotlin:kotlin-util-klib:1.9.0
|
||||
org.jetbrains.trove4j:trove4j:20160824
|
||||
org.jetbrains:annotations:13.0
|
||||
org.jetbrains:markdown-jvm:0.2.1
|
||||
@@ -930,4 +942,5 @@ org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.7.22
|
||||
com.android.tools.lint:lint-gradle:30.4.2
|
||||
com.android.tools.build:gradle:7.4.2
|
||||
com.android.tools.build:aapt2:7.4.2-8841542
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2
|
||||
org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.9.0
|
||||
@@ -697,7 +697,7 @@ https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotatio
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.4.0/error_prone_annotations-2.4.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.9.0/error_prone_annotations-2.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.24.0/error_prone_annotations-2.24.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.24.1/error_prone_annotations-2.24.1.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.0.18/error_prone_parent-2.0.18.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.2.0/error_prone_parent-2.2.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.3.1/error_prone_parent-2.3.1.pom
|
||||
@@ -706,7 +706,7 @@ https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.4.0/error_prone_parent-2.4.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.7.1/error_prone_parent-2.7.1.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.9.0/error_prone_parent-2.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.24.0/error_prone_parent-2.24.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_parent/2.24.1/error_prone_parent-2.24.1.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/flatbuffers/flatbuffers-java/1.12.0/flatbuffers-java-1.12.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/google/1/google-1.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.pom
|
||||
@@ -1116,36 +1116,55 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-co
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-coroutines-core/1.5.2/kotlinx-coroutines-core-1.5.2.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.1.0/kotlinx-metadata-jvm-0.1.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.7.22/org.jetbrains.kotlin.jvm.gradle.plugin-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/jvm/org.jetbrains.kotlin.jvm.gradle.plugin/1.9.0/org.jetbrains.kotlin.jvm.gradle.plugin-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-android-extensions/1.6.20/kotlin-android-extensions-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-android-extensions/1.7.22/kotlin-android-extensions-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-android-extensions/1.9.0/kotlin-android-extensions-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-annotations-jvm/1.3.72/kotlin-annotations-jvm-1.3.72.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.6.20/kotlin-annotation-processing-gradle-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.7.22/kotlin-annotation-processing-gradle-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-build-common/1.6.20/kotlin-build-common-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-build-common/1.7.22/kotlin-build-common-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-build-common/1.9.0/kotlin-build-common-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-build-tools-api/1.9.0/kotlin-build-tools-api-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-build-tools-impl/1.9.0/kotlin-build-tools-impl-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.6.20/kotlin-compiler-embeddable-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.7.22/kotlin-compiler-embeddable-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-embeddable/1.9.0/kotlin-compiler-embeddable-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-runner/1.6.20/kotlin-compiler-runner-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-runner/1.7.22/kotlin-compiler-runner-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-compiler-runner/1.9.0/kotlin-compiler-runner-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-client/1.6.20/kotlin-daemon-client-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-client/1.7.22/kotlin-daemon-client-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-client/1.9.0/kotlin-daemon-client-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.6.20/kotlin-daemon-embeddable-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.7.22/kotlin-daemon-embeddable-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-daemon-embeddable/1.9.0/kotlin-daemon-embeddable-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugins-bom/1.9.0/kotlin-gradle-plugins-bom-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-annotations/1.9.0/kotlin-gradle-plugin-annotations-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.6.20/kotlin-gradle-plugin-api-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.7.22/kotlin-gradle-plugin-api-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-api/1.9.0/kotlin-gradle-plugin-api-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.7.22/kotlin-gradle-plugin-idea-proto-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea-proto/1.9.0/kotlin-gradle-plugin-idea-proto-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.7.22/kotlin-gradle-plugin-idea-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-idea/1.9.0/kotlin-gradle-plugin-idea-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.6.20/kotlin-gradle-plugin-model-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.7.22/kotlin-gradle-plugin-model-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin-model/1.9.0/kotlin-gradle-plugin-model-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.6.20/kotlin-gradle-plugin-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.7.22/kotlin-gradle-plugin-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.9.0/kotlin-gradle-plugin-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.6.20/kotlin-klib-commonizer-api-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.7.22/kotlin-klib-commonizer-api-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.7.22/kotlin-klib-commonizer-embeddable-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-api/1.9.0/kotlin-klib-commonizer-api-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-klib-commonizer-embeddable/1.9.0/kotlin-klib-commonizer-embeddable-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-native-utils/1.6.20/kotlin-native-utils-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-native-utils/1.7.22/kotlin-native-utils-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-native-utils/1.9.0/kotlin-native-utils-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-project-model/1.6.20/kotlin-project-model-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-project-model/1.7.22/kotlin-project-model-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-project-model/1.9.0/kotlin-project-model-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.1.3-2/kotlin-reflect-1.1.3-2.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.2.0/kotlin-reflect-1.2.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.3.20/kotlin-reflect-1.3.20.pom
|
||||
@@ -1153,20 +1172,26 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.3.50/
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.3.72/kotlin-reflect-1.3.72.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.4.32/kotlin-reflect-1.4.32.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.5.31/kotlin-reflect-1.5.31.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.6.20/kotlin-reflect-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.7.10/kotlin-reflect-1.7.10.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/1.7.22/kotlin-reflect-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-reflect/2.0.0-Beta2/kotlin-reflect-2.0.0-Beta2.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.6.20/kotlin-scripting-common-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.7.22/kotlin-scripting-common-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-common/1.9.0/kotlin-scripting-common-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.6.20/kotlin-scripting-compiler-embeddable-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.7.22/kotlin-scripting-compiler-embeddable-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-embeddable/1.9.0/kotlin-scripting-compiler-embeddable-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.6.20/kotlin-scripting-compiler-impl-embeddable-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.7.22/kotlin-scripting-compiler-impl-embeddable-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-compiler-impl-embeddable/1.9.0/kotlin-scripting-compiler-impl-embeddable-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.6.20/kotlin-scripting-jvm-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.7.22/kotlin-scripting-jvm-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-scripting-jvm/1.9.0/kotlin-scripting-jvm-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.6.20/kotlin-script-runtime-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.7.22/kotlin-script-runtime-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-script-runtime/1.9.0/kotlin-script-runtime-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.2.71/kotlin-stdlib-common-1.2.71.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.0/kotlin-stdlib-common-1.3.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.3.20/kotlin-stdlib-common-1.3.20.pom
|
||||
@@ -1187,6 +1212,7 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.10/kotlin-stdlib-common-1.7.10.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.7.22/kotlin-stdlib-common-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-common/1.9.0/kotlin-stdlib-common-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.2.71/kotlin-stdlib-jdk7-1.2.71.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.3.20/kotlin-stdlib-jdk7-1.3.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.3.50/kotlin-stdlib-jdk7-1.3.50.pom
|
||||
@@ -1199,6 +1225,7 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.5
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.10/kotlin-stdlib-jdk7-1.7.10.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.22/kotlin-stdlib-jdk7-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.9.0/kotlin-stdlib-jdk7-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.2.71/kotlin-stdlib-jdk8-1.2.71.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.3.20/kotlin-stdlib-jdk8-1.3.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.3.50/kotlin-stdlib-jdk8-1.3.50.pom
|
||||
@@ -1210,6 +1237,7 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.5.31/kotlin-stdlib-jdk8-1.5.31.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.10/kotlin-stdlib-jdk8-1.7.10.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.7.22/kotlin-stdlib-jdk8-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.9.0/kotlin-stdlib-jdk8-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.1.3-2/kotlin-stdlib-1.1.3-2.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.0/kotlin-stdlib-1.2.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.2.20/kotlin-stdlib-1.2.20.pom
|
||||
@@ -1233,14 +1261,18 @@ https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/k
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.6.21/kotlin-stdlib-1.6.21.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.10/kotlin-stdlib-1.7.10.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.7.22/kotlin-stdlib-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.9.0/kotlin-stdlib-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.9.21/kotlin-stdlib-1.9.21.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/2.0.0-Beta2/kotlin-stdlib-2.0.0-Beta2.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-tooling-core/1.7.22/kotlin-tooling-core-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-tooling-core/1.9.0/kotlin-tooling-core-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-tooling-metadata/1.6.20/kotlin-tooling-metadata-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-util-io/1.6.20/kotlin-util-io-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-util-io/1.7.22/kotlin-util-io-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-util-io/1.9.0/kotlin-util-io-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-util-klib/1.6.20/kotlin-util-klib-1.6.20.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-util-klib/1.7.22/kotlin-util-klib-1.7.22.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-util-klib/1.9.0/kotlin-util-klib-1.9.0.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/markdown-jvm/0.2.1/markdown-jvm-0.2.1.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/markdown/0.2.1/markdown-0.2.1.pom
|
||||
https://repo.maven.apache.org/maven2/org/jetbrains/trove4j/trove4j/20160824/trove4j-20160824.pom
|
||||
@@ -1323,11 +1355,11 @@ https://repo.maven.apache.org/maven2/org/robolectric/utils-reflector/4.7.3/utils
|
||||
https://repo.maven.apache.org/maven2/org/robolectric/utils/4.7.3/utils-4.7.3.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/2.0.9/slf4j-api-2.0.9.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-bom/2.0.9/slf4j-bom-2.0.9.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-api/2.1.0-alpha1/slf4j-api-2.1.0-alpha1.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-bom/2.1.0-alpha1/slf4j-bom-2.1.0-alpha1.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-parent/1.7.21/slf4j-parent-1.7.21.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-parent/1.7.30/slf4j-parent-1.7.30.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-parent/2.0.9/slf4j-parent-2.0.9.pom
|
||||
https://repo.maven.apache.org/maven2/org/slf4j/slf4j-parent/2.1.0-alpha1/slf4j-parent-2.1.0-alpha1.pom
|
||||
https://repo.maven.apache.org/maven2/org/sonatype/oss/oss-parent/4/oss-parent-4.pom
|
||||
https://repo.maven.apache.org/maven2/org/sonatype/oss/oss-parent/5/oss-parent-5.pom
|
||||
https://repo.maven.apache.org/maven2/org/sonatype/oss/oss-parent/7/oss-parent-7.pom
|
||||
|
||||
@@ -50,7 +50,8 @@ org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.7.22
|
||||
com.android.tools.lint:lint-gradle:30.4.2
|
||||
com.android.tools.build:gradle:7.4.2
|
||||
com.android.tools.build:aapt2:7.4.2-8841542
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' \
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2
|
||||
org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:1.9.0' \
|
||||
>> "${DEPS_LIST}"
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -1,92 +0,0 @@
|
||||
import { useDerivedValue, withTiming, interpolate, useAnimatedScrollHandler, runOnJS } from 'react-native-reanimated';
|
||||
|
||||
export function navigationHeaderOpacity(distanceFromListTop, isAllLoaded, isCalculationsComplete, startPosition) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
const isCalculationsCompleteValue = isCalculationsComplete.value;
|
||||
if (distanceFromListTop.value < startPosition && isAllLoaded.value) {
|
||||
return isCalculationsCompleteValue ? withTiming(0) : 0;
|
||||
} else {
|
||||
return isCalculationsCompleteValue ? withTiming(1) : 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function navigationHeaderPosition(distanceFromListTop, isAllLoaded, topBarHeight, startPosition) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return distanceFromListTop.value < startPosition && isAllLoaded.value ? withTiming(topBarHeight) : withTiming(0);
|
||||
});
|
||||
}
|
||||
|
||||
export function interpolateNavigationViewOpacity(props) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
const {
|
||||
'all-loaded?': isAllLoaded,
|
||||
'end-position': endPosition,
|
||||
'start-position': startPosition,
|
||||
'distance-from-list-top': distanceFromListTop,
|
||||
} = props;
|
||||
if (isAllLoaded.value) {
|
||||
return interpolate(distanceFromListTop.value, [startPosition, endPosition], [0, 1], {
|
||||
extrapolateLeft: 'clamp',
|
||||
extrapolateRight: 'clamp',
|
||||
});
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function messagesListOnScroll(distanceFromListTop, chatListScrollY, callback) {
|
||||
return function (event) {
|
||||
'worklet';
|
||||
const currentY = event.contentOffset.y;
|
||||
const layoutHeight = event.layoutMeasurement.height;
|
||||
const contentSizeY = event.contentSize.height - layoutHeight;
|
||||
const newDistance = contentSizeY - currentY;
|
||||
distanceFromListTop.value = newDistance;
|
||||
chatListScrollY.value = currentY;
|
||||
runOnJS(callback)(layoutHeight, newDistance);
|
||||
};
|
||||
}
|
||||
|
||||
export function placeholderOpacity(isCalculationsComplete) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return isCalculationsComplete.value ? 0 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
export function placeholderZIndex(isCalculationsComplete) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return isCalculationsComplete.value ? 0 : 2;
|
||||
});
|
||||
}
|
||||
|
||||
export function scrollDownButtonOpacity(chatListScrollY, isComposerFocused, windowHeight) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
if (isComposerFocused.value) {
|
||||
return 0;
|
||||
} else {
|
||||
return chatListScrollY.value > windowHeight * 0.75 ? 1 : 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonOpacity(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 0 : 1);
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonPosition(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 35 : 0);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { useDerivedValue, withTiming } from 'react-native-reanimated';
|
||||
|
||||
export function scrollDownButtonOpacity(chatListScrollY, isComposerFocused, windowHeight) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
if (isComposerFocused.value) {
|
||||
return 0;
|
||||
} else {
|
||||
return chatListScrollY.value > windowHeight * 0.75 ? 1 : 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonOpacity(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 0 : 1);
|
||||
});
|
||||
}
|
||||
|
||||
export function jumpToButtonPosition(scrollDownButtonOpacity, isComposerFocused) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return withTiming(scrollDownButtonOpacity.value == 1 || isComposerFocused.value ? 35 : 0);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { withTiming, runOnJS } from 'react-native-reanimated';
|
||||
|
||||
export function messagesListOnScroll(distanceFromListTop, chatListScrollY, callback) {
|
||||
return function (event) {
|
||||
'worklet';
|
||||
const currentY = event.contentOffset.y;
|
||||
const layoutHeight = event.layoutMeasurement.height;
|
||||
const contentSizeY = event.contentSize.height - layoutHeight;
|
||||
const newDistance = contentSizeY - currentY;
|
||||
distanceFromListTop.value = newDistance;
|
||||
chatListScrollY.value = currentY;
|
||||
runOnJS(callback)(layoutHeight, newDistance);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { useDerivedValue, withTiming, interpolate } from 'react-native-reanimated';
|
||||
|
||||
export function navigationHeaderOpacity(distanceFromListTop, isAllLoaded, isCalculationsComplete, startPosition) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
const isCalculationsCompleteValue = isCalculationsComplete.value;
|
||||
if (distanceFromListTop.value < startPosition && isAllLoaded.value) {
|
||||
return isCalculationsCompleteValue ? withTiming(0) : 0;
|
||||
} else {
|
||||
return isCalculationsCompleteValue ? withTiming(1) : 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function navigationHeaderPosition(distanceFromListTop, isAllLoaded, topBarHeight, startPosition) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return distanceFromListTop.value < startPosition && isAllLoaded.value ? withTiming(topBarHeight) : withTiming(0);
|
||||
});
|
||||
}
|
||||
|
||||
export function interpolateNavigationViewOpacity(props) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
const {
|
||||
'all-loaded?': isAllLoaded,
|
||||
'end-position': endPosition,
|
||||
'start-position': startPosition,
|
||||
'distance-from-list-top': distanceFromListTop,
|
||||
} = props;
|
||||
if (isAllLoaded.value) {
|
||||
return interpolate(distanceFromListTop.value, [startPosition, endPosition], [0, 1], {
|
||||
extrapolateLeft: 'clamp',
|
||||
extrapolateRight: 'clamp',
|
||||
});
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useDerivedValue } from 'react-native-reanimated';
|
||||
|
||||
export function placeholderOpacity(isCalculationsComplete) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return isCalculationsComplete.value ? 0 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
export function placeholderZIndex(isCalculationsComplete) {
|
||||
return useDerivedValue(function () {
|
||||
'worklet';
|
||||
return isCalculationsComplete.value ? 0 : 2;
|
||||
});
|
||||
}
|
||||
@@ -51,3 +51,9 @@
|
||||
(visibility-status-updates/<-rpc-settings)
|
||||
(set/rename-keys {:compressedKey :compressed-key
|
||||
:emojiHash :emoji-hash})))
|
||||
|
||||
(defn rpc->setting-value
|
||||
[{:keys [name] :as setting}]
|
||||
(condp = name
|
||||
:currency (update setting :value keyword)
|
||||
setting))
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
[db]
|
||||
(into #{}
|
||||
(remove #(= (:type %) :watch)
|
||||
(map #(eip55/address->checksum (:address %)) (get db :profile/wallet-accounts)))))
|
||||
(map #(eip55/address->checksum (:address %)) (vals (get-in db [:wallet :accounts]))))))
|
||||
|
||||
;;NOTE we want to handle only last resolve
|
||||
(def resolve-last-id (atom nil))
|
||||
|
||||
@@ -79,10 +79,15 @@
|
||||
"recent-history-ready" (recent-history-fetching-ended cofx event)
|
||||
"fetching-history-error" (fetching-error cofx event)
|
||||
"non-archival-node-detected" (non-archival-node-detected cofx event)
|
||||
"pending-transaction-status-changed" {:fx
|
||||
[[:dispatch
|
||||
[:wallet/pending-transaction-status-changed-received
|
||||
event]]]}
|
||||
"wallet-owned-collectibles-filtering-done" {:fx [[:dispatch
|
||||
[:wallet/owned-collectibles-filtering-done
|
||||
event]]]}
|
||||
"wallet-get-collectibles-details-done" {:fx [[:dispatch
|
||||
[:wallet/get-collectible-details-done
|
||||
event]]]}
|
||||
"wallet-tick-reload" {:fx [[:dispatch [:wallet/reload]]]}
|
||||
(log/debug ::unknown-wallet-event :type type :event event)))
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
[react-native.permissions :as permissions]
|
||||
[react-native.platform :as platform]
|
||||
[status-im.common.biometric.events :as biometric]
|
||||
status-im.common.serialization
|
||||
status-im.common.standard-authentication.events
|
||||
[status-im.common.theme.core :as theme]
|
||||
[status-im.common.universal-links :as universal-links]
|
||||
|
||||
@@ -246,7 +246,7 @@
|
||||
[rn/touchable-opacity
|
||||
{:on-press #(do
|
||||
(rf/dispatch [:pop-to-root :shell-stack])
|
||||
(rf/dispatch [:navigate-to :community-overview (:id community)])
|
||||
(rf/dispatch [:communities/navigate-to-community-overview (:id community)])
|
||||
(rf/dispatch [:chat/close]))}
|
||||
[rn/text
|
||||
{:style {:text-align :center
|
||||
|
||||
@@ -377,81 +377,88 @@
|
||||
(defn mock
|
||||
[module]
|
||||
(case module
|
||||
"react-native-languages" react-native-languages
|
||||
"react-native-background-timer" background-timer
|
||||
"react-native-image-crop-picker" image-crop-picker
|
||||
"react-native-gesture-handler" react-native-gesture-handler
|
||||
"react-native-static-safe-area-insets" react-native-static-safe-area-insets
|
||||
"react-native-config" config
|
||||
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
|
||||
:getBottomSpace (fn [])})
|
||||
"react-native-screens" (clj->js {})
|
||||
"react-native-reanimated" react-native-reanimated
|
||||
"react-native-redash" react-native-redash
|
||||
"react-native-fetch-polyfill" fetch
|
||||
"react-native-status-keycard" status-keycard
|
||||
"react-native-keychain" keychain
|
||||
"react-native-touch-id" touchid
|
||||
"@react-native-community/netinfo" net-info
|
||||
"react-native-dialogs" dialogs
|
||||
"react-native" react-native
|
||||
"react-native-fs" fs
|
||||
"react-native-mail" react-native-mail
|
||||
"react-native-image-resizer" image-resizer
|
||||
"react-native-haptic-feedback" react-native-haptic-feedback
|
||||
"react-native-device-info" react-native-device-info
|
||||
"react-native-push-notification" react-native-push-notification
|
||||
"react-native-linear-gradient" react-native-gradien
|
||||
"@react-native-community/masked-view" masked-view
|
||||
"react-native-blob-util" react-native-blob-util
|
||||
"react-native-navigation" react-native-navigation
|
||||
"@react-native-community/push-notification-ios" push-notification-ios
|
||||
"@react-native-community/blur" react-native-blur
|
||||
"@react-native-camera-roll/camera-roll" react-native-camera-roll
|
||||
"react-native-camera-kit" react-native-camera-kit
|
||||
"react-native-permissions" react-native-permissions
|
||||
"rn-emoji-keyboard" rn-emoji-keyboard
|
||||
"react-native-hole-view" react-native-hole-view
|
||||
"react-native-draggable-flatlist" react-native-draggable-flatlist
|
||||
"react-native-webview" react-native-webview
|
||||
"@react-native-community/audio-toolkit" react-native-audio-toolkit
|
||||
"react-native-image-viewing" react-native-image-viewing
|
||||
"react-native-share" react-native-share
|
||||
"@react-native-async-storage/async-storage" async-storage
|
||||
"react-native-svg" react-native-svg
|
||||
"react-native-transparent-video" react-native-transparent-video
|
||||
"react-native-orientation-locker" react-native-orientation-locker
|
||||
"react-native-gifted-charts" react-native-gifted-charts
|
||||
"../resources/data/emojis/en.json" (js/JSON.parse (slurp
|
||||
"./resources/data/emojis/en.json"))
|
||||
"../src/js/worklets/core.js" worklet-factory
|
||||
"../src/js/worklets/shell/bottom_tabs.js" #js {}
|
||||
"../src/js/worklets/shell/home_stack.js" #js {}
|
||||
"../src/js/worklets/shell/floating_screen.js" #js {}
|
||||
"../src/js/worklets/bottom_sheet.js" #js {}
|
||||
"../src/js/worklets/record_audio.js" #js {}
|
||||
"../src/js/worklets/scroll_view.js" #js {}
|
||||
"../src/js/worklets/onboarding_carousel.js" #js {}
|
||||
"../src/js/worklets/chat/lightbox.js" #js {}
|
||||
"../src/js/worklets/chat/messages.js" #js {}
|
||||
"../src/js/worklets/parallax.js" #js {}
|
||||
"../src/js/worklets/identifiers_highlighting.js" #js {}
|
||||
"./fleets.js" default-fleets
|
||||
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
|
||||
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
|
||||
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
|
||||
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
|
||||
"../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json"))
|
||||
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
|
||||
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
|
||||
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
|
||||
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
|
||||
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
|
||||
"../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json"))
|
||||
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
|
||||
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
|
||||
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
|
||||
"../translations/zh_hant.json" (js/JSON.parse (slurp
|
||||
"./translations/zh_hant.json"))
|
||||
"../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json"))
|
||||
"react-native-languages" react-native-languages
|
||||
"react-native-background-timer" background-timer
|
||||
"react-native-image-crop-picker" image-crop-picker
|
||||
"react-native-gesture-handler" react-native-gesture-handler
|
||||
"react-native-static-safe-area-insets" react-native-static-safe-area-insets
|
||||
"react-native-config" config
|
||||
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
|
||||
:getBottomSpace (fn [])})
|
||||
"react-native-screens" (clj->js {})
|
||||
"react-native-reanimated" react-native-reanimated
|
||||
"react-native-redash" react-native-redash
|
||||
"react-native-fetch-polyfill" fetch
|
||||
"react-native-status-keycard" status-keycard
|
||||
"react-native-keychain" keychain
|
||||
"react-native-touch-id" touchid
|
||||
"@react-native-community/netinfo" net-info
|
||||
"react-native-dialogs" dialogs
|
||||
"react-native" react-native
|
||||
"react-native-fs" fs
|
||||
"react-native-mail" react-native-mail
|
||||
"react-native-image-resizer" image-resizer
|
||||
"react-native-haptic-feedback" react-native-haptic-feedback
|
||||
"react-native-device-info" react-native-device-info
|
||||
"react-native-push-notification" react-native-push-notification
|
||||
"react-native-linear-gradient" react-native-gradien
|
||||
"@react-native-community/masked-view" masked-view
|
||||
"react-native-blob-util" react-native-blob-util
|
||||
"react-native-navigation" react-native-navigation
|
||||
"@react-native-community/push-notification-ios" push-notification-ios
|
||||
"@react-native-community/blur" react-native-blur
|
||||
"@react-native-camera-roll/camera-roll" react-native-camera-roll
|
||||
"react-native-camera-kit" react-native-camera-kit
|
||||
"react-native-permissions" react-native-permissions
|
||||
"rn-emoji-keyboard" rn-emoji-keyboard
|
||||
"react-native-hole-view" react-native-hole-view
|
||||
"react-native-draggable-flatlist" react-native-draggable-flatlist
|
||||
"react-native-webview" react-native-webview
|
||||
"@react-native-community/audio-toolkit" react-native-audio-toolkit
|
||||
"react-native-image-viewing" react-native-image-viewing
|
||||
"react-native-share" react-native-share
|
||||
"@react-native-async-storage/async-storage" async-storage
|
||||
"react-native-svg" react-native-svg
|
||||
"react-native-transparent-video" react-native-transparent-video
|
||||
"react-native-orientation-locker" react-native-orientation-locker
|
||||
"react-native-gifted-charts" react-native-gifted-charts
|
||||
"../resources/data/emojis/en.json" (js/JSON.parse
|
||||
(slurp
|
||||
"./resources/data/emojis/en.json"))
|
||||
"../src/js/worklets/core.js" worklet-factory
|
||||
"../src/js/worklets/shell/bottom_tabs.js" #js {}
|
||||
"../src/js/worklets/shell/home_stack.js" #js {}
|
||||
"../src/js/worklets/shell/floating_screen.js" #js {}
|
||||
"../src/js/worklets/bottom_sheet.js" #js {}
|
||||
"../src/js/worklets/record_audio.js" #js {}
|
||||
"../src/js/worklets/scroll_view.js" #js {}
|
||||
"../src/js/worklets/onboarding_carousel.js" #js {}
|
||||
"../src/js/worklets/chat/messenger/lightbox.js" #js {}
|
||||
"../src/js/worklets/chat/messenger/messages.js" #js {}
|
||||
"../src/js/worklets/chat/messenger/navigation.js" #js {}
|
||||
"../src/js/worklets/chat/messenger/composer.js" #js {}
|
||||
"../src/js/worklets/chat/messenger/placeholder.js" #js {}
|
||||
"../src/js/worklets/parallax.js" #js {}
|
||||
"../src/js/worklets/identifiers_highlighting.js" #js {}
|
||||
"./fleets.js" default-fleets
|
||||
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
|
||||
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
|
||||
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
|
||||
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
|
||||
"../translations/es_419.json" (js/JSON.parse (slurp
|
||||
"./translations/es_419.json"))
|
||||
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
|
||||
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
|
||||
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
|
||||
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
|
||||
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
|
||||
"../translations/pt_BR.json" (js/JSON.parse (slurp
|
||||
"./translations/pt_BR.json"))
|
||||
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
|
||||
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
|
||||
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
|
||||
"../translations/zh_hant.json" (js/JSON.parse (slurp
|
||||
"./translations/zh_hant.json"))
|
||||
"../translations/zh_TW.json" (js/JSON.parse (slurp
|
||||
"./translations/zh_TW.json"))
|
||||
nil))
|
||||
|
||||
@@ -11,17 +11,17 @@
|
||||
(defn token-requirement-list-row
|
||||
[tokens padding?]
|
||||
[rn/view {:style (style/token-row padding?)}
|
||||
(map-indexed (fn [token-index token]
|
||||
(let [{:keys [amount sufficient? purchasable?]} token]
|
||||
^{:key token-index}
|
||||
[rn/view {:style style/token-tag-spacing}
|
||||
[token-tag/view
|
||||
{:token-symbol (:symbol token)
|
||||
:token-value amount
|
||||
:size :size-24
|
||||
:options (cond
|
||||
sufficient? :hold
|
||||
purchasable? :add)}]]))
|
||||
(map-indexed (fn [token-index {:keys [img-src amount sufficient? purchasable?] :as token}]
|
||||
^{:key token-index}
|
||||
[rn/view {:style style/token-tag-spacing}
|
||||
[token-tag/view
|
||||
{:token-symbol (:symbol token)
|
||||
:token-img-src img-src
|
||||
:token-value amount
|
||||
:size :size-24
|
||||
:options (cond
|
||||
sufficient? :hold
|
||||
purchasable? :add)}]])
|
||||
tokens)])
|
||||
|
||||
(defn- internal-view
|
||||
|
||||
@@ -76,15 +76,19 @@
|
||||
(str description " · " (i18n/label :t/on-device))])
|
||||
|
||||
(defn- context-tag-subtitle
|
||||
[{:keys [community-logo community-name]}]
|
||||
[rn/view
|
||||
{:accessibility-label :context-tag-wrapper
|
||||
:style {:flex-wrap :wrap}}
|
||||
[context-tag/view
|
||||
{:type :community
|
||||
:community-name community-name
|
||||
:community-logo community-logo
|
||||
:size 24}]])
|
||||
[{:keys [context-tag-type community-logo community-name account-name emoji customization-color]}]
|
||||
(let [tag-type (or context-tag-type :account)]
|
||||
[rn/view
|
||||
{:accessibility-label :context-tag-wrapper
|
||||
:style {:flex-wrap :wrap}}
|
||||
[context-tag/view
|
||||
{:type tag-type
|
||||
:account-name account-name
|
||||
:emoji emoji
|
||||
:community-name community-name
|
||||
:community-logo community-logo
|
||||
:size 24
|
||||
:customization-color customization-color}]]))
|
||||
|
||||
(defn- description-subtitle
|
||||
[{:keys [theme blur? description]}]
|
||||
@@ -95,7 +99,8 @@
|
||||
description])
|
||||
|
||||
(defn- subtitle
|
||||
[{:keys [type theme blur? keycard? networks description community-name community-logo]}]
|
||||
[{:keys [type theme blur? keycard? networks description community-name community-logo
|
||||
context-tag-type account-name emoji customization-color]}]
|
||||
(cond
|
||||
(= :keypair type)
|
||||
[keypair-subtitle
|
||||
@@ -118,8 +123,12 @@
|
||||
|
||||
(= :context-tag type)
|
||||
[context-tag-subtitle
|
||||
{:community-logo community-logo
|
||||
:community-name community-name}]
|
||||
{:context-tag-type context-tag-type
|
||||
:community-logo community-logo
|
||||
:community-name community-name
|
||||
:account-name account-name
|
||||
:emoji emoji
|
||||
:customization-color customization-color}]
|
||||
|
||||
(and (not= :label type) description)
|
||||
[description-subtitle
|
||||
@@ -173,6 +182,7 @@
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [title title-icon type theme description blur? community-name community-logo button-icon
|
||||
account-name emoji
|
||||
on-button-press
|
||||
on-button-long-press
|
||||
button-disabled? account-avatar-emoji account-avatar-type customization-color icon-avatar
|
||||
@@ -196,14 +206,17 @@
|
||||
:theme theme
|
||||
:blur? blur?}]
|
||||
[subtitle
|
||||
{:type type
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:keycard? keycard?
|
||||
:networks networks
|
||||
:description description
|
||||
:community-name community-name
|
||||
:community-logo community-logo}]]
|
||||
{:type type
|
||||
:theme theme
|
||||
:blur? blur?
|
||||
:keycard? keycard?
|
||||
:networks networks
|
||||
:description description
|
||||
:community-name community-name
|
||||
:community-logo community-logo
|
||||
:customization-color customization-color
|
||||
:account-name account-name
|
||||
:emoji emoji}]]
|
||||
[right-icon
|
||||
{:theme theme
|
||||
:type type
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
(ns quo.components.gradient.gradient-cover.style)
|
||||
|
||||
(defn root-container
|
||||
[opacity]
|
||||
{:height 252
|
||||
[opacity height]
|
||||
{:height (or height 252)
|
||||
:opacity opacity})
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
[react-native.linear-gradient :as linear-gradient]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [customization-color opacity container-style] :or {customization-color :blue}}]
|
||||
[{:keys [customization-color opacity container-style height] :or {customization-color :blue}}]
|
||||
(let [color-top (colors/custom-color customization-color 50 20)
|
||||
color-bottom (colors/custom-color customization-color 50 0)]
|
||||
[linear-gradient/linear-gradient
|
||||
@@ -14,6 +14,6 @@
|
||||
:colors [color-top color-bottom]
|
||||
:start {:x 0 :y 0}
|
||||
:end {:x 0 :y 1}
|
||||
:style (merge (style/root-container opacity) container-style)}]))
|
||||
:style (merge (style/root-container opacity height) container-style)}]))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
(ns quo.components.list-items.network-list.component-spec
|
||||
(:require [quo.components.list-items.network-list.view :as network-list]
|
||||
[quo.foundations.colors :as colors]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(defn- render
|
||||
[component]
|
||||
(h/render-with-theme-provider component :light))
|
||||
|
||||
(def props
|
||||
{:theme :light
|
||||
:state :default
|
||||
:label "Mainnet"
|
||||
:network-image 873
|
||||
:customization-color :blue
|
||||
:token-value "100.00 ETH"
|
||||
:fiat-value "€100.00"})
|
||||
|
||||
|
||||
(h/describe "List items: Network List"
|
||||
(h/test "default state explicit"
|
||||
(render [network-list/view props])
|
||||
(h/is-truthy (h/get-by-text "Mainnet")))
|
||||
|
||||
(h/test "Pressed state"
|
||||
(render [network-list/view props])
|
||||
(h/fire-event :on-press-in (h/get-by-label-text ::network-list))
|
||||
(h/wait-for #(h/has-style (h/query-by-label-text ::network-list)
|
||||
{:backgroundColor (colors/resolve-color :blue :light 5)})))
|
||||
|
||||
(h/test "Active state"
|
||||
(render [network-list/view (assoc props :state :active)])
|
||||
(h/has-style (h/query-by-label-text ::network-list)
|
||||
{:backgroundColor (colors/resolve-color :blue :light 10)}))
|
||||
|
||||
(h/test "Call on-press"
|
||||
(let [on-press (h/mock-fn)]
|
||||
(render [network-list/view (assoc props :on-press on-press)])
|
||||
(h/fire-event :on-press (h/get-by-label-text ::network-list))
|
||||
(h/was-called on-press))))
|
||||
@@ -0,0 +1,41 @@
|
||||
(ns quo.components.list-items.network-list.style
|
||||
(:require [quo.foundations.colors :as colors]))
|
||||
|
||||
(defn- background-color
|
||||
[state customization-color theme]
|
||||
(case state
|
||||
:pressed (colors/resolve-color customization-color theme 5)
|
||||
:active (colors/resolve-color customization-color theme 10)
|
||||
:transparent))
|
||||
|
||||
(defn container
|
||||
[state customization-color theme]
|
||||
{:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :center
|
||||
:padding-horizontal 12
|
||||
:padding-vertical 8
|
||||
:border-radius 12
|
||||
:height 56
|
||||
:background-color (background-color state customization-color theme)})
|
||||
|
||||
(def info
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:gap 2})
|
||||
|
||||
(def network-image
|
||||
{:border-width 1
|
||||
:border-radius 16
|
||||
:border-color colors/neutral-80-opa-5
|
||||
:margin-right 8
|
||||
:background-color colors/neutral-80-opa-5
|
||||
:height 32
|
||||
:width 32})
|
||||
|
||||
(def values-container
|
||||
{:align-items :flex-end})
|
||||
|
||||
(defn fiat-value
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
|
||||
@@ -0,0 +1,76 @@
|
||||
(ns quo.components.list-items.network-list.view
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.components.list-items.network-list.style :as style]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[schema.core :as schema]))
|
||||
|
||||
(defn- info
|
||||
[{:keys [network-image label]}]
|
||||
[rn/view {:style style/info}
|
||||
[rn/image
|
||||
{:source network-image
|
||||
:style style/network-image}]
|
||||
[rn/view
|
||||
[text/text
|
||||
{:weight :semi-bold
|
||||
:style {:text-transform :capitalize}
|
||||
:number-of-lines 1}
|
||||
(if (string/blank? label) "-" label)]]])
|
||||
|
||||
(defn- values
|
||||
[{:keys [token-value fiat-value theme]}]
|
||||
[rn/view {:style style/values-container}
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1}
|
||||
token-value]
|
||||
[text/text
|
||||
{:style (style/fiat-value theme)
|
||||
:size :paragraph-2
|
||||
:number-of-lines 1}
|
||||
fiat-value]])
|
||||
|
||||
(def ?schema
|
||||
[:=>
|
||||
[:catn
|
||||
[:props
|
||||
[:map {:closed true}
|
||||
[:network-image :int]
|
||||
[:label :string]
|
||||
[:fiat-value :string]
|
||||
[:token-value :string]
|
||||
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
|
||||
[:state [:enum :pressed :active :default]]
|
||||
[:on-press {:optional true} [:maybe fn?]]
|
||||
[:theme :schema.common/theme]]]]
|
||||
:any])
|
||||
|
||||
(defn- view-internal
|
||||
[]
|
||||
(let [pressed? (reagent/atom false)
|
||||
on-press-in #(reset! pressed? true)
|
||||
on-press-out #(reset! pressed? false)]
|
||||
(fn [{:keys [on-press state customization-color theme]
|
||||
:as props
|
||||
:or {customization-color :blue}}]
|
||||
(let [internal-state (if @pressed?
|
||||
:pressed
|
||||
state)]
|
||||
|
||||
[rn/pressable
|
||||
{:style (style/container internal-state customization-color theme)
|
||||
:on-press-in on-press-in
|
||||
:on-press-out on-press-out
|
||||
:on-press on-press
|
||||
:accessibility-label :network-list}
|
||||
[info props]
|
||||
[values props]]))))
|
||||
|
||||
(def view
|
||||
(quo.theme/with-theme
|
||||
(schema/instrument #'view-internal ?schema)))
|
||||
+28
-25
@@ -1,4 +1,4 @@
|
||||
(ns quo.components.list-items.user-list
|
||||
(ns quo.components.list-items.user
|
||||
(:require
|
||||
[quo.components.avatars.user-avatar.view :as user-avatar]
|
||||
[quo.components.icon :as icons]
|
||||
@@ -18,14 +18,14 @@
|
||||
:align-items :center})
|
||||
|
||||
(defn action-icon
|
||||
[{:keys [type on-press on-check disabled? checked?]}]
|
||||
[{:keys [type on-press on-check disabled? checked?]} theme]
|
||||
[rn/touchable-opacity
|
||||
{:on-press (when on-press on-press)}
|
||||
(case type
|
||||
:options
|
||||
[icons/icon :i/options
|
||||
{:size 20
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}]
|
||||
:checkbox
|
||||
[selectors/view
|
||||
{:type :checkbox
|
||||
@@ -37,30 +37,33 @@
|
||||
[text/text "not implemented"]
|
||||
[rn/view])])
|
||||
|
||||
(defn user-list
|
||||
(defn user
|
||||
[{:keys [short-chat-key primary-name secondary-name photo-path online? contact? verified?
|
||||
untrustworthy? on-press on-long-press accessory]}]
|
||||
[rn/touchable-opacity
|
||||
untrustworthy? on-press on-long-press accessory customization-color theme]}]
|
||||
[rn/touchable-highlight
|
||||
{:style container-style
|
||||
:underlay-color (colors/resolve-color customization-color theme 5)
|
||||
:accessibility-label :user-list
|
||||
:on-press (when on-press on-press)
|
||||
:on-long-press (when on-long-press on-long-press)}
|
||||
[user-avatar/user-avatar
|
||||
{:full-name primary-name
|
||||
:profile-picture photo-path
|
||||
:online? online?
|
||||
:size :small}]
|
||||
[rn/view {:style {:margin-horizontal 8 :flex 1}}
|
||||
[author/view
|
||||
{:primary-name primary-name
|
||||
:secondary-name secondary-name
|
||||
:contact? contact?
|
||||
:verified? verified?
|
||||
:untrustworthy? untrustworthy?}]
|
||||
(when short-chat-key
|
||||
[text/text
|
||||
{:size :paragraph-1
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
short-chat-key])]
|
||||
(when accessory
|
||||
[action-icon accessory])])
|
||||
[:<>
|
||||
[user-avatar/user-avatar
|
||||
{:full-name primary-name
|
||||
:profile-picture photo-path
|
||||
:online? online?
|
||||
:size :small}]
|
||||
[rn/view {:style {:margin-horizontal 8 :flex 1}}
|
||||
[author/view
|
||||
{:primary-name primary-name
|
||||
:secondary-name secondary-name
|
||||
:contact? contact?
|
||||
:verified? verified?
|
||||
:untrustworthy? untrustworthy?
|
||||
:size 15}]
|
||||
(when short-chat-key
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
short-chat-key])]
|
||||
(when accessory
|
||||
[action-icon accessory theme])]])
|
||||
@@ -38,7 +38,7 @@
|
||||
(defn icon-container
|
||||
[is-first?]
|
||||
{:margin-left (if is-first? 4 2)
|
||||
:margin-bottom 2})
|
||||
:margin-bottom 4})
|
||||
|
||||
(defn time-text
|
||||
[theme]
|
||||
|
||||
@@ -51,8 +51,7 @@
|
||||
[icons/icon :main-icons2/untrustworthy
|
||||
{:size 12
|
||||
:no-color true
|
||||
:container-style (style/icon-container contact?)}])
|
||||
]
|
||||
:container-style (style/icon-container contact?)}])]
|
||||
(when (and (not verified?) short-chat-key)
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
|
||||
@@ -34,10 +34,9 @@
|
||||
(= :pinned use-case)
|
||||
theme)
|
||||
container-style)}
|
||||
[rn/image
|
||||
{:style {:width 15 :height 15}
|
||||
:accessibility-label :emoji
|
||||
:source (reaction.resource/get-reaction emoji)}]
|
||||
[rn/text
|
||||
{:accessibility-label :emoji}
|
||||
(reaction.resource/system-emojis emoji)]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :semi-bold
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
(def ^:private reactions
|
||||
(resolve-all-reactions))
|
||||
|
||||
(def system-emojis
|
||||
{:reaction/thumbs-up "👍"
|
||||
:reaction/thumbs-down "👎"
|
||||
:reaction/love "❤"
|
||||
:reaction/laugh "😂"
|
||||
:reaction/sad "😢"
|
||||
:reaction/angry "😡"})
|
||||
|
||||
(defn get-reaction
|
||||
[reaction]
|
||||
(assert (keyword? reaction) "Reaction should be a keyword")
|
||||
|
||||
@@ -19,6 +19,5 @@
|
||||
(swap! pressed? not)
|
||||
(when on-press
|
||||
(on-press e)))}
|
||||
[rn/image
|
||||
{:source (reactions.resource/get-reaction emoji)
|
||||
:style {:width 20 :height 20}}]])))
|
||||
[rn/text
|
||||
(reactions.resource/system-emojis emoji)]])))
|
||||
|
||||
@@ -8,19 +8,20 @@
|
||||
|
||||
(defn- category-internal
|
||||
[{:keys [label data container-style] :as props}]
|
||||
[rn/view {:style (merge (style/container label) container-style)}
|
||||
(when label
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (style/label props)}
|
||||
label])
|
||||
[rn/view {:style (style/settings-items props)}
|
||||
(for [item data]
|
||||
^{:key item}
|
||||
[:<>
|
||||
[settings-item/view item]
|
||||
(when-not (= item (last data))
|
||||
[rn/view {:style (style/settings-separator props)}])])]])
|
||||
(let [settings-item (filter identity data)]
|
||||
[rn/view {:style (merge (style/container label) container-style)}
|
||||
(when label
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (style/label props)}
|
||||
label])
|
||||
[rn/view {:style (style/settings-items props)}
|
||||
(for [item settings-item]
|
||||
^{:key item}
|
||||
[:<>
|
||||
[settings-item/view item]
|
||||
(when-not (= item (last settings-item))
|
||||
[rn/view {:style (style/settings-separator props)}])])]]))
|
||||
|
||||
(def settings-category (quo.theme/with-theme category-internal))
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
network-image]
|
||||
:or {subtitle-type :default}}]
|
||||
[rn/view {:style style/subtitle-container}
|
||||
(when (not= :small size)
|
||||
(when (and subtitle-type (not= :small size))
|
||||
[rn/view {:style (style/subtitle-icon-container subtitle-type)}
|
||||
(case subtitle-type
|
||||
:icon [icons/icon icon
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
:padding-top 12
|
||||
:padding-bottom 8})
|
||||
|
||||
(def gradient-bg
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0})
|
||||
|
||||
;;; Header
|
||||
(def header-container
|
||||
{:flex-direction :row
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
[oops.core :as oops]
|
||||
[quo.components.avatars.account-avatar.view :as account-avatar]
|
||||
[quo.components.buttons.button.view :as button]
|
||||
[quo.components.gradient.gradient-cover.view :as gradient-cover]
|
||||
[quo.components.markdown.text :as text]
|
||||
[quo.components.share.qr-code.view :as qr-code]
|
||||
[quo.components.share.share-qr-code.style :as style]
|
||||
[quo.components.tabs.tab.view :as tab]
|
||||
[quo.theme]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[react-native.platform :as platform]
|
||||
[reagent.core :as reagent]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
@@ -126,41 +125,44 @@
|
||||
[{:keys [share-qr-type qr-image-uri component-width customization-color full-name
|
||||
profile-picture emoji on-share-press]
|
||||
:as props}]
|
||||
[rn/view {:style style/content-container}
|
||||
[rn/view
|
||||
{:style style/share-qr-container}
|
||||
[:<>
|
||||
[rn/view {:style style/gradient-bg}
|
||||
[gradient-cover/view {:customization-color customization-color :height 463}]]
|
||||
[rn/view {:style style/content-container}
|
||||
[rn/view
|
||||
{:style style/share-qr-inner-container}
|
||||
[account-avatar/view
|
||||
{:customization-color customization-color
|
||||
:emoji emoji
|
||||
:size 32}]
|
||||
[text/text
|
||||
{:size :heading-2
|
||||
:weight :semi-bold
|
||||
:style {:margin-left 8}} full-name]]
|
||||
[share-button {:on-press on-share-press}]]
|
||||
(when (#{:wallet-legacy :wallet-multichain} share-qr-type)
|
||||
[header props])
|
||||
[quo.theme/provider {:theme :light}
|
||||
[qr-code/view
|
||||
{:qr-image-uri qr-image-uri
|
||||
:size (style/qr-code-size component-width)
|
||||
:avatar (if (= share-qr-type :profile)
|
||||
:profile
|
||||
:wallet-account)
|
||||
:customization-color customization-color
|
||||
:full-name full-name
|
||||
:profile-picture profile-picture
|
||||
:emoji emoji}]]
|
||||
[rn/view {:style style/bottom-container}
|
||||
(case share-qr-type
|
||||
:profile [profile-bottom props]
|
||||
:wallet-legacy [wallet-legacy-bottom props]
|
||||
:wallet-multichain [wallet-multichain-bottom props]
|
||||
nil)]])
|
||||
{:style style/share-qr-container}
|
||||
[rn/view
|
||||
{:style style/share-qr-inner-container}
|
||||
[account-avatar/view
|
||||
{:customization-color customization-color
|
||||
:emoji emoji
|
||||
:size 32}]
|
||||
[text/text
|
||||
{:size :heading-2
|
||||
:weight :semi-bold
|
||||
:style {:margin-left 8}} full-name]]
|
||||
[share-button {:on-press on-share-press}]]
|
||||
(when (#{:wallet-legacy :wallet-multichain} share-qr-type)
|
||||
[header props])
|
||||
[quo.theme/provider {:theme :light}
|
||||
[qr-code/view
|
||||
{:qr-image-uri qr-image-uri
|
||||
:size (style/qr-code-size component-width)
|
||||
:avatar (if (= share-qr-type :profile)
|
||||
:profile
|
||||
:wallet-account)
|
||||
:customization-color customization-color
|
||||
:full-name full-name
|
||||
:profile-picture profile-picture
|
||||
:emoji emoji}]]
|
||||
[rn/view {:style style/bottom-container}
|
||||
(case share-qr-type
|
||||
:profile [profile-bottom props]
|
||||
:wallet-legacy [wallet-legacy-bottom props]
|
||||
:wallet-multichain [wallet-multichain-bottom props]
|
||||
nil)]]])
|
||||
|
||||
(defn view
|
||||
(defn- view-internal
|
||||
"Receives the following properties:
|
||||
- type: :profile | :wallet-legacy | :wallet-multichain
|
||||
- qr-image-uri: Image source value.
|
||||
@@ -190,16 +192,9 @@
|
||||
Sometimes while using a blur layer on top of another on Android, this component looks
|
||||
bad because of the `blur/view`, so we can set `unblur-on-android? true` to fix it.
|
||||
"
|
||||
[{:keys [unblur-on-android?] :as props}]
|
||||
[props]
|
||||
(reagent/with-let [component-width (reagent/atom nil)
|
||||
container-component (if (and platform/android? unblur-on-android?)
|
||||
[rn/view {:background-color style/overlay-color}]
|
||||
[blur/view
|
||||
{:blur-radius 20
|
||||
:blur-amount 20
|
||||
:blur-type :transparent
|
||||
:overlay-color style/overlay-color
|
||||
:background-color style/overlay-color}])]
|
||||
container-component [rn/view {:background-color style/overlay-color}]]
|
||||
[quo.theme/provider {:theme :dark}
|
||||
[rn/view
|
||||
{:accessibility-label :share-qr-code
|
||||
@@ -211,3 +206,6 @@
|
||||
(-> props
|
||||
(assoc :component-width @component-width)
|
||||
(clojure.set/rename-keys {:type :share-qr-type}))]))]]))
|
||||
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
- :token-symbol - string"
|
||||
[]
|
||||
(let [container-width (reagent/atom 0)]
|
||||
(fn [{:keys [options size blur? theme token-value token-symbol]
|
||||
(fn [{:keys [options size blur? theme token-value token-img-src token-symbol]
|
||||
:or {size :size-24}}]
|
||||
[rn/view
|
||||
{:on-layout #(reset! container-width
|
||||
@@ -42,11 +42,12 @@
|
||||
[])}
|
||||
[rn/view {:style (style/container size options blur? theme)}
|
||||
[token/view
|
||||
{:style (style/token-img size)
|
||||
:token token-symbol
|
||||
:size (case size
|
||||
:size-24 :size-20
|
||||
:size-32 :size-28)}]
|
||||
{:style (style/token-img size)
|
||||
:token token-symbol
|
||||
:size (case size
|
||||
:size-24 :size-20
|
||||
:size-32 :size-28)
|
||||
:image-source token-img-src}]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
[:token {:optional true} [:or keyword? string?]]
|
||||
[:style {:optional true} map?]
|
||||
;; Ignores `token` and uses this as parameter to `rn/image`'s source.
|
||||
[:image-source {:optional true} [:or :schema.common/image-source :string]]
|
||||
;; If true, adds `data:image/png;base64,` as prefix to the string passed as `image-source`
|
||||
[:add-b64-prefix? {:optional true} boolean?]]]
|
||||
[:image-source {:optional true} [:or :schema.common/image-source :string]]]]
|
||||
:any])
|
||||
|
||||
(defn- size->number
|
||||
@@ -52,28 +50,32 @@
|
||||
first
|
||||
string/capitalize)]])
|
||||
|
||||
(defn- normalize-b64-string
|
||||
[b64-image]
|
||||
(if (string/starts-with? b64-image "data:image/")
|
||||
b64-image
|
||||
(str b64-png-image-prefix b64-image)))
|
||||
|
||||
(defn view-internal
|
||||
"Render a token image.
|
||||
Props:
|
||||
- style: extra styles to apply to the `rn/image` component.
|
||||
- size: `:size-nn` or just `nn`, being `nn` any number. Defaults to 32.
|
||||
- token: string or keyword, it can contain upper case letters or not.
|
||||
E.g. all of these are valid and resolve to the same:
|
||||
:token/snt | :snt | :SNT | \"SNT\" | \"snt\".
|
||||
- image-source: Ignores `token` and uses this as parameter to `rn/image`'s source.
|
||||
- add-b64-prefix?: If true, adds `data:image/png;base64,` as prefix to the string
|
||||
passed as `image-source`.
|
||||
- style: extra styles to apply to the `rn/image` component.
|
||||
- size: `:size-nn` or just `nn`, being `nn` any number. Defaults to 32.
|
||||
- token: string or keyword, it can contain upper case letters or not.
|
||||
E.g. all of these are valid and resolve to the same:
|
||||
:token/snt | :snt | :SNT | \"SNT\" | \"snt\".
|
||||
- image-source: Ignores `token` and uses this as parameter to `rn/image`'s source, it
|
||||
can be a b64 string representing an image.
|
||||
"
|
||||
[{:keys [token size style image-source add-b64-prefix?]
|
||||
[{:keys [token size style image-source]
|
||||
:or {size 32}}]
|
||||
(let [b64-string (if (and image-source add-b64-prefix?)
|
||||
(str b64-png-image-prefix image-source)
|
||||
image-source)
|
||||
source (or b64-string (token-loader/get-token-image token))]
|
||||
(if source
|
||||
(let [img-src (if (string? image-source)
|
||||
(normalize-b64-string image-source)
|
||||
image-source)]
|
||||
(if-let [existing-source (or img-src (token-loader/get-token-image token))]
|
||||
[rn/image
|
||||
{:style (token-style style size)
|
||||
:source source}]
|
||||
:source existing-source}]
|
||||
[temp-empty-symbol token size style])))
|
||||
|
||||
(def view (schema/instrument #'view-internal ?schema))
|
||||
|
||||
@@ -33,22 +33,22 @@
|
||||
[rn/view
|
||||
{:style style/networks-container
|
||||
:accessibility-label :networks}
|
||||
(when (pos? ethereum)
|
||||
(when (and ethereum (pos? (:amount ethereum)))
|
||||
[network-amount
|
||||
{:network :ethereum
|
||||
:amount (str ethereum " ETH")
|
||||
:amount (str (:amount ethereum) " " (or (:token-symbol ethereum) "ETH"))
|
||||
:divider? (or show-arbitrum? show-optimism?)
|
||||
:theme theme}])
|
||||
(when show-optimism?
|
||||
[network-amount
|
||||
{:network :optimism
|
||||
:amount (str optimism " OPT")
|
||||
:amount (str (:amount optimism) " " (or (:token-symbol optimism) "OPT"))
|
||||
:divider? show-arbitrum?
|
||||
:theme theme}])
|
||||
(when show-arbitrum?
|
||||
[network-amount
|
||||
{:network :arbitrum
|
||||
:amount (str arbitrum " ARB")
|
||||
:amount (str (:amount arbitrum) " " (or (:token-symbol arbitrum) "ARB"))
|
||||
:theme theme}])]))
|
||||
|
||||
(defn- view-internal
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
(ns quo.components.wallet.token-input.style
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.foundations.typography :as typography]
|
||||
[react-native.platform :as platform]))
|
||||
[quo.foundations.typography :as typography]))
|
||||
|
||||
(defn main-container
|
||||
[width]
|
||||
@@ -16,23 +15,51 @@
|
||||
:flex-direction :row
|
||||
:justify-content :space-between})
|
||||
|
||||
(defn token-name
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
|
||||
:margin-right 8
|
||||
:padding-bottom 2})
|
||||
|
||||
(def token-label-container
|
||||
{:position :absolute
|
||||
:left 40 ; token image size + margin
|
||||
:right 0
|
||||
:bottom 0
|
||||
:top 0
|
||||
:flex-direction :row
|
||||
:align-items :flex-end})
|
||||
|
||||
(def text-input-container
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 40 ; token image size + margin
|
||||
:right 0})
|
||||
|
||||
(def text-input-dimensions
|
||||
(-> typography/heading-1
|
||||
(dissoc :letter-spacing)
|
||||
(assoc :font-weight "600"
|
||||
:margin-right 5
|
||||
:padding-left 0
|
||||
:padding-right 0
|
||||
:padding-top 0
|
||||
:padding-bottom 0
|
||||
:height "100%")))
|
||||
|
||||
(defn text-input
|
||||
[theme]
|
||||
(merge typography/heading-1
|
||||
{:font-weight "600"
|
||||
:margin-left 8
|
||||
:margin-right (if platform/ios? 6 4)
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white theme)
|
||||
:padding 0
|
||||
:text-align :center
|
||||
:height "100%"}))
|
||||
(assoc text-input-dimensions :color (colors/theme-colors colors/neutral-100 colors/white theme)))
|
||||
|
||||
(defn placeholder-text
|
||||
[theme]
|
||||
(colors/theme-colors colors/neutral-40 colors/neutral-50 theme))
|
||||
|
||||
(defn divider
|
||||
[width theme]
|
||||
{:height 1
|
||||
:width width
|
||||
:background-color (colors/theme-colors colors/neutral-10 colors/neutral-90 theme)
|
||||
:margin-vertical 8})
|
||||
[theme]
|
||||
{:margin-vertical 8
|
||||
:background-color (colors/theme-colors colors/neutral-10 colors/neutral-90 theme)})
|
||||
|
||||
(def data-container
|
||||
{:padding-top 4
|
||||
@@ -40,3 +67,7 @@
|
||||
:flex-direction :row
|
||||
:justify-content :space-between
|
||||
:align-items :center})
|
||||
|
||||
(defn fiat-amount
|
||||
[theme]
|
||||
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
|
||||
|
||||
@@ -7,85 +7,134 @@
|
||||
[quo.components.tags.network-tags.view :as network-tag]
|
||||
[quo.components.utilities.token.view :as token]
|
||||
[quo.components.wallet.token-input.style :as style]
|
||||
[quo.foundations.colors :as colors]
|
||||
[quo.foundations.common :as common]
|
||||
[quo.theme :as quo.theme]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn fiat-format
|
||||
[currency num-value conversion]
|
||||
(str (get common/currency-label currency) (.toFixed (* num-value conversion) 2)))
|
||||
|
||||
(defn crypto-format
|
||||
[num-value conversion crypto-decimals token]
|
||||
(str (.toFixed (/ num-value conversion) (or crypto-decimals 2))
|
||||
" "
|
||||
(string/upper-case (or (clj->js token) ""))))
|
||||
|
||||
(defn calc-value
|
||||
[crypto? currency token value conversion]
|
||||
(let [num-value (if (string? value) (parse-double (or value "0")) value)]
|
||||
[{:keys [crypto? currency token value conversion crypto-decimals]}]
|
||||
(let [num-value (if (string? value)
|
||||
(or (parse-double value) 0)
|
||||
value)]
|
||||
(if crypto?
|
||||
(str (get common/currency-label currency) (.toFixed (* num-value conversion) 2))
|
||||
(str (.toFixed (/ num-value conversion) 2) " " (string/upper-case (or (clj->js token) ""))))))
|
||||
(fiat-format currency num-value conversion)
|
||||
(crypto-format num-value conversion crypto-decimals token))))
|
||||
|
||||
(defn- data-info
|
||||
[{:keys [theme token crypto-decimals conversion networks title crypto? currency amount]}]
|
||||
[rn/view {:style style/data-container}
|
||||
[network-tag/view {:networks networks :title title}]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style (style/fiat-amount theme)}
|
||||
(calc-value {:crypto? crypto?
|
||||
:currency currency
|
||||
:token token
|
||||
:value amount
|
||||
:conversion conversion
|
||||
:crypto-decimals crypto-decimals})]])
|
||||
|
||||
(defn- token-name-text
|
||||
[theme text]
|
||||
[text/text
|
||||
{:style (style/token-name theme)
|
||||
:size :paragraph-2
|
||||
:weight :semi-bold}
|
||||
(string/upper-case (or (clj->js text) ""))])
|
||||
|
||||
(defn- token-label
|
||||
[{:keys [theme value text]}]
|
||||
[rn/view
|
||||
{:style style/token-label-container
|
||||
:pointer-events :none}
|
||||
[rn/text-input
|
||||
{:max-length 12
|
||||
:style style/text-input-dimensions
|
||||
:editable false
|
||||
:placeholder "0"
|
||||
:opacity 0
|
||||
:value value}]
|
||||
[token-name-text theme text]])
|
||||
|
||||
(defn input-section
|
||||
[{:keys [on-change-text value value-atom]}]
|
||||
(let [input-ref (atom nil)
|
||||
set-ref #(reset! input-ref %)
|
||||
focus-input #(when-let [ref ^js @input-ref]
|
||||
(.focus ref))
|
||||
controlled-input? (some? value)
|
||||
handle-on-change-text (fn [v]
|
||||
(when-not controlled-input?
|
||||
(reset! value-atom v))
|
||||
(when on-change-text
|
||||
(on-change-text v)))]
|
||||
(fn [{:keys [theme token customization-color show-keyboard? crypto? currency value]
|
||||
:or {show-keyboard? true}}]
|
||||
[rn/pressable
|
||||
{:on-press focus-input
|
||||
:style {:flex 1}}
|
||||
[token/view
|
||||
{:token token
|
||||
:size :size-32}]
|
||||
[rn/view {:style style/text-input-container}
|
||||
[rn/text-input
|
||||
(cond-> {:style (style/text-input theme)
|
||||
:placeholder-text-color (style/placeholder-text theme)
|
||||
:auto-focus true
|
||||
:ref set-ref
|
||||
:placeholder "0"
|
||||
:keyboard-type :numeric
|
||||
:max-length 12
|
||||
:on-change-text handle-on-change-text
|
||||
:selection-color customization-color
|
||||
:show-soft-input-on-focus show-keyboard?}
|
||||
controlled-input? (assoc :value value)
|
||||
(not controlled-input?) (assoc :default-value @value-atom))]]
|
||||
[token-label
|
||||
{:theme theme
|
||||
:text (if crypto? token currency)
|
||||
:value (if controlled-input? value @value-atom)}]])))
|
||||
|
||||
(defn- view-internal
|
||||
[{external-value :value}]
|
||||
(let [width (:width (rn/get-window))
|
||||
value (reagent/atom nil)
|
||||
crypto? (reagent/atom true)
|
||||
input-ref (atom nil)
|
||||
controlled-input? (some? external-value)]
|
||||
(fn [{:keys [theme token currency conversion networks title customization-color
|
||||
on-change-text on-swap container-style show-keyboard?]
|
||||
:or {show-keyboard? true}
|
||||
external-value :value}]
|
||||
[rn/view
|
||||
{:style (merge
|
||||
(style/main-container width)
|
||||
container-style)}
|
||||
[{:keys [on-swap]}]
|
||||
(let [width (:width (rn/get-window))
|
||||
value-atom (reagent/atom nil)
|
||||
crypto? (reagent/atom true)
|
||||
handle-on-swap (fn []
|
||||
(swap! crypto? not)
|
||||
(when on-swap
|
||||
(on-swap @crypto?)))]
|
||||
(fn [{:keys [theme container-style value] :as props}]
|
||||
[rn/view {:style (merge (style/main-container width) container-style)}
|
||||
[rn/view {:style style/amount-container}
|
||||
[rn/pressable
|
||||
{:on-press #(when @input-ref (.focus ^js @input-ref))
|
||||
:style {:flex-direction :row
|
||||
:flex-grow 1
|
||||
:align-items :flex-end}}
|
||||
[token/view {:token token :size :size-32}]
|
||||
[rn/text-input
|
||||
(cond-> {:auto-focus true
|
||||
:ref #(reset! input-ref %)
|
||||
:placeholder "0"
|
||||
:placeholder-text-color (colors/theme-colors colors/neutral-40
|
||||
colors/neutral-50
|
||||
theme)
|
||||
:keyboard-type :numeric
|
||||
:max-length 12
|
||||
:on-change-text (fn [v]
|
||||
(when-not controlled-input?
|
||||
(reset! value v))
|
||||
(when on-change-text
|
||||
(on-change-text v)))
|
||||
:style (style/text-input theme)
|
||||
:selection-color customization-color
|
||||
:show-soft-input-on-focus show-keyboard?}
|
||||
controlled-input? (assoc :value external-value)
|
||||
(not controlled-input?) (assoc :default-value @value))]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :semi-bold
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)
|
||||
:margin-right 8
|
||||
:padding-bottom 2}}
|
||||
(string/upper-case (or (clj->js (if @crypto? token currency)) ""))]]
|
||||
[input-section
|
||||
(assoc props
|
||||
:value-atom value-atom
|
||||
:crypto? @crypto?)]
|
||||
[button/button
|
||||
{:icon true
|
||||
:icon-only? true
|
||||
:size 32
|
||||
:on-press (fn []
|
||||
(swap! crypto? not)
|
||||
(when on-swap
|
||||
(on-swap @crypto?)))
|
||||
:on-press handle-on-swap
|
||||
:type :outline
|
||||
:accessibility-label :reorder}
|
||||
:i/reorder]]
|
||||
[divider-line/view {:container-style {:margin-vertical 8}}]
|
||||
[rn/view {:style style/data-container}
|
||||
[network-tag/view {:networks networks :title title}]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :medium
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)}}
|
||||
(calc-value @crypto? currency token (or external-value @value) conversion)]]])))
|
||||
[divider-line/view {:container-style (style/divider theme)}]
|
||||
[data-info
|
||||
(assoc props
|
||||
:crypto? @crypto?
|
||||
:amount (or value @value-atom))]])))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
||||
|
||||
+5
-3
@@ -80,13 +80,14 @@
|
||||
quo.components.list-items.community.view
|
||||
quo.components.list-items.dapp.view
|
||||
quo.components.list-items.menu-item
|
||||
quo.components.list-items.network-list.view
|
||||
quo.components.list-items.preview-list.view
|
||||
quo.components.list-items.quiz-item.view
|
||||
quo.components.list-items.saved-address.view
|
||||
quo.components.list-items.saved-contact-address.view
|
||||
quo.components.list-items.token-network.view
|
||||
quo.components.list-items.token-value.view
|
||||
quo.components.list-items.user-list
|
||||
quo.components.list-items.user
|
||||
quo.components.loaders.skeleton-list.view
|
||||
quo.components.markdown.list.view
|
||||
quo.components.markdown.text
|
||||
@@ -299,16 +300,17 @@
|
||||
(def account-list-card quo.components.list-items.account-list-card.view/view)
|
||||
(def address quo.components.list-items.address.view/view)
|
||||
(def channel quo.components.list-items.channel.view/view)
|
||||
(def community-list-item quo.components.list-items.community.view/view)
|
||||
(def community-list quo.components.list-items.community.view/view)
|
||||
(def dapp quo.components.list-items.dapp.view/view)
|
||||
(def menu-item quo.components.list-items.menu-item/menu-item)
|
||||
(def network-list quo.components.list-items.network-list.view/view)
|
||||
(def preview-list quo.components.list-items.preview-list.view/view)
|
||||
(def quiz-item quo.components.list-items.quiz-item.view/view)
|
||||
(def saved-address quo.components.list-items.saved-address.view/view)
|
||||
(def saved-contact-address quo.components.list-items.saved-contact-address.view/view)
|
||||
(def token-network quo.components.list-items.token-network.view/view)
|
||||
(def token-value quo.components.list-items.token-value.view/view)
|
||||
(def user-list quo.components.list-items.user-list/user-list)
|
||||
(def user quo.components.list-items.user/user)
|
||||
|
||||
;;;; Loaders
|
||||
(def skeleton-list quo.components.loaders.skeleton-list.view/view)
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
quo.components.list-items.channel.component-spec
|
||||
quo.components.list-items.community.component-spec
|
||||
quo.components.list-items.dapp.component-spec
|
||||
quo.components.list-items.network-list.component-spec
|
||||
quo.components.list-items.quiz-item.component-spec
|
||||
quo.components.list-items.saved-address.component-spec
|
||||
quo.components.list-items.saved-contact-address.component-spec
|
||||
|
||||
@@ -17,19 +17,19 @@
|
||||
(fn [^js data]
|
||||
(let [^js section (.-section data)]
|
||||
(reagent/as-element [f
|
||||
{:title (.-title section)
|
||||
{:index (.-index section)
|
||||
:title (.-title section)
|
||||
:data (.-data section)}]))))
|
||||
|
||||
(defn- wrap-per-section-render-fn
|
||||
[props]
|
||||
(update
|
||||
(if-let [f (:render-fn props)]
|
||||
(assoc (dissoc props :render-fn :render-data)
|
||||
:renderItem
|
||||
(memo-wrap-render-fn f (:render-data props)))
|
||||
props)
|
||||
:data
|
||||
to-array))
|
||||
[index props]
|
||||
(-> (if-let [f (:render-fn props)]
|
||||
(assoc (dissoc props :render-fn :render-data)
|
||||
:renderItem
|
||||
(memo-wrap-render-fn f (:render-data props)))
|
||||
props)
|
||||
(update :data to-array)
|
||||
(assoc :index index)))
|
||||
|
||||
(defn section-list
|
||||
"A wrapper for SectionList.
|
||||
@@ -42,6 +42,8 @@
|
||||
props
|
||||
(when render-section-footer-fn
|
||||
{:renderSectionFooter (wrap-render-section-header-fn render-section-footer-fn)})
|
||||
{:sections (clj->js (map wrap-per-section-render-fn sections))
|
||||
{:sections (->> sections
|
||||
(map-indexed wrap-per-section-render-fn)
|
||||
(clj->js))
|
||||
:renderSectionHeader (wrap-render-section-header-fn render-section-header-fn)
|
||||
:style style})])
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
(ns status-im.common.contact-list.style)
|
||||
|
||||
(defn contacts-section-header
|
||||
[first-item?]
|
||||
{:padding-top (if first-item? 0 8)})
|
||||
@@ -1,7 +1,10 @@
|
||||
(ns status-im.common.contact-list.view
|
||||
(:require
|
||||
[quo.core :as quo]))
|
||||
[quo.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[status-im.common.contact-list.style :as style]))
|
||||
|
||||
(defn contacts-section-header
|
||||
[{:keys [title]}]
|
||||
[quo/divider-label title])
|
||||
[{:keys [title index]}]
|
||||
[rn/view (style/contacts-section-header (= index 0))
|
||||
[quo/divider-label title]])
|
||||
|
||||
@@ -6,17 +6,21 @@
|
||||
|
||||
(defn contact-list-item
|
||||
[{:keys [on-press on-long-press accessory]}
|
||||
{:keys [primary-name secondary-name public-key compressed-key ens-verified added?]}]
|
||||
(let [photo-path (rf/sub [:chats/photo-path public-key])
|
||||
online? (rf/sub [:visibility-status-updates/online? public-key])]
|
||||
[quo/user-list
|
||||
{:short-chat-key (address/get-shortened-compressed-key (or compressed-key public-key))
|
||||
:primary-name primary-name
|
||||
:secondary-name secondary-name
|
||||
:photo-path photo-path
|
||||
:online? online?
|
||||
:verified? ens-verified
|
||||
:contact? added?
|
||||
:on-press on-press
|
||||
:on-long-press on-long-press
|
||||
:accessory accessory}]))
|
||||
{:keys [primary-name secondary-name public-key compressed-key ens-verified added?]}
|
||||
theme]
|
||||
(let [photo-path (rf/sub [:chats/photo-path public-key])
|
||||
online? (rf/sub [:visibility-status-updates/online? public-key])
|
||||
customization-color (rf/sub [:profile/customization-color])]
|
||||
[quo/user
|
||||
{:customization-color customization-color
|
||||
:theme theme
|
||||
:short-chat-key (address/get-shortened-compressed-key (or compressed-key public-key))
|
||||
:primary-name primary-name
|
||||
:secondary-name secondary-name
|
||||
:photo-path photo-path
|
||||
:online? online?
|
||||
:verified? ens-verified
|
||||
:contact? added?
|
||||
:on-press on-press
|
||||
:on-long-press on-long-press
|
||||
:accessory accessory}]))
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
:no-contacts
|
||||
{:light (js/require "../resources/images/ui2/no-contacts-light.png")
|
||||
:dark (js/require "../resources/images/ui2/no-contacts-dark.png")}
|
||||
:no-contacts-to-chat
|
||||
{:light (js/require "../resources/images/ui2/no-contacts-to-chat-light.png")
|
||||
:dark (js/require "../resources/images/ui2/no-contacts-to-chat-dark.png")}
|
||||
:keycard-chip
|
||||
{:light (js/require "../resources/images/ui2/keycard-chip-light.png")
|
||||
:dark (js/require "../resources/images/ui2/keycard-chip-dark.png")}
|
||||
|
||||
@@ -259,12 +259,6 @@
|
||||
{:type :wallet-account
|
||||
:account (when account (string/lower-case account))})
|
||||
|
||||
(defn community-route-type
|
||||
[route-params]
|
||||
(if (string/starts-with? (:community-id route-params) "z")
|
||||
:desktop-community
|
||||
:community))
|
||||
|
||||
(defn handle-uri
|
||||
[chain chats uri cb]
|
||||
(let [{:keys [handler route-params query-params]} (match-uri uri)]
|
||||
@@ -297,14 +291,14 @@
|
||||
(cb {:type handler :community-id (:community-id route-params)})
|
||||
|
||||
(and (= handler :community) (:community-id route-params))
|
||||
(cb {:type (community-route-type route-params)
|
||||
(cb {:type :community
|
||||
:community-id (:community-id route-params)})
|
||||
|
||||
(and (= handler :community-chat) (:community-channel-id route-params) (:community-id route-params))
|
||||
(match-community-channel-async route-params cb)
|
||||
|
||||
(and (= handler :community-chat) (:community-id route-params))
|
||||
(cb {:type (community-route-type route-params)
|
||||
(cb {:type :community
|
||||
:community-id (:community-id route-params)})
|
||||
|
||||
;; NOTE: removed in `match-uri`, might need this in the future
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
(ns status-im.common.serialization
|
||||
(:require
|
||||
[native-module.core :as native-module]
|
||||
[status-im.constants :as constants]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(rf/reg-fx :serialization/deserialize-and-compress-key
|
||||
(fn [{:keys [serialized-key on-success on-error]}]
|
||||
(native-module/deserialize-and-compress-key
|
||||
serialized-key
|
||||
(fn [resp]
|
||||
(let [{:keys [error]} (transforms/json->clj resp)]
|
||||
(if error
|
||||
(on-error error)
|
||||
(on-success resp)))))))
|
||||
|
||||
(rf/reg-fx :serialization/decompress-public-key
|
||||
(fn [{:keys [compressed-key on-success on-error]}]
|
||||
(native-module/compressed-key->public-key
|
||||
compressed-key
|
||||
constants/deserialization-key
|
||||
(fn [resp]
|
||||
(let [{:keys [error]} (transforms/json->clj resp)]
|
||||
(if error
|
||||
(on-error error)
|
||||
(on-success (str "0x" (subs resp 5)))))))))
|
||||
@@ -2,12 +2,12 @@
|
||||
(:require
|
||||
[clojure.string :as string]
|
||||
[goog.string :as gstring]
|
||||
[native-module.core :as native-module]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-native.async-storage :as async-storage]
|
||||
[react-native.core :as rn]
|
||||
[schema.core :as schema]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.contexts.communities.events :as communities.events]
|
||||
[status-im.navigation.events :as navigation]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.ethereum.chain :as chain]
|
||||
@@ -68,32 +68,6 @@
|
||||
(log/info "universal-links: handling community request " community-id)
|
||||
(navigation/navigate-to cofx :community-requests-to-join {:community-id community-id}))
|
||||
|
||||
(rf/defn handle-community
|
||||
[cofx {:keys [community-id]}]
|
||||
(log/info "universal-links: handling community" community-id)
|
||||
(navigation/navigate-to cofx :community {:community-id community-id}))
|
||||
|
||||
(rf/defn handle-navigation-to-desktop-community-from-mobile
|
||||
{:events [:handle-navigation-to-desktop-community-from-mobile]}
|
||||
[cofx deserialized-key]
|
||||
(rf/merge
|
||||
cofx
|
||||
{:dispatch [:navigate-to :community-overview deserialized-key]}
|
||||
(navigation/pop-to-root :shell-stack)))
|
||||
|
||||
(rf/defn handle-desktop-community
|
||||
[_ {:keys [community-id]}]
|
||||
(native-module/deserialize-and-compress-key
|
||||
community-id
|
||||
(fn [deserialized-key]
|
||||
(rf/dispatch [:chat.ui/fetch-community (str deserialized-key)])
|
||||
(rf/dispatch [:handle-navigation-to-desktop-community-from-mobile (str deserialized-key)]))))
|
||||
|
||||
(rf/defn handle-community-chat
|
||||
[cofx {:keys [chat-id]}]
|
||||
(log/info "universal-links: handling community chat" chat-id)
|
||||
{:dispatch [:chat/pop-to-root-and-navigate-to-chat chat-id]})
|
||||
|
||||
(rf/defn handle-view-profile
|
||||
[{:keys [db] :as cofx} {:keys [public-key ens-name]}]
|
||||
(log/info "universal-links: handling view profile" public-key)
|
||||
@@ -143,14 +117,13 @@
|
||||
|
||||
(rf/defn on-handle
|
||||
{:events [::match-value]}
|
||||
[cofx url {:keys [type] :as data}]
|
||||
[cofx url {:keys [type chat-id] :as data}]
|
||||
(case type
|
||||
:group-chat (handle-group-chat cofx data)
|
||||
:private-chat (handle-private-chat cofx data)
|
||||
:community-requests (handle-community-requests cofx data)
|
||||
:community (handle-community cofx data)
|
||||
:desktop-community (handle-desktop-community cofx data)
|
||||
:community-chat (handle-community-chat cofx data)
|
||||
:community (communities.events/navigate-to-serialized-community cofx data)
|
||||
:community-chat {:dispatch [:communities/navigate-to-community-chat chat-id :pop-to-root]}
|
||||
:contact (handle-view-profile cofx data)
|
||||
:browser (handle-browse cofx data)
|
||||
:eip681 (handle-eip681 cofx data)
|
||||
|
||||
@@ -219,7 +219,8 @@
|
||||
(def regx-community-universal-link #"((^https?://status.app/)|(^status-app://))c/([\x00-\x7F]+)$")
|
||||
(def regx-deep-link #"((^ethereum:.*)|(^status-app://[\x00-\x7F]+$))")
|
||||
(def regx-ens #"^(?=.{5,255}$)([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$")
|
||||
(def regx-address #"^0x[a-fA-F0-9]{40}$")
|
||||
(def regx-multichain-address #"^(?:(?:eth:|arb1:|opt:)(?=:|))*0x[0-9a-fA-F]{40}$")
|
||||
|
||||
(def regx-address-contains #"(?i)0x[a-fA-F0-9]{40}")
|
||||
(def regx-starts-with-uuid #"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
|
||||
(def regx-full-or-partial-address #"^0x[a-fA-F0-9]{1,40}$")
|
||||
@@ -337,6 +338,13 @@
|
||||
(def ^:const local-pairing-action-sync-device 3)
|
||||
(def ^:const local-pairing-action-pairing-installation 4)
|
||||
|
||||
(def ^:const serialization-key
|
||||
"We pass this serialization key as a parameter to MultiformatSerializePublicKey
|
||||
function at status-go, This key determines the output base of the serialization.
|
||||
according to https://specs.status.im/spec/2#public-key-serialization we serialize
|
||||
keys with base58btc encoding"
|
||||
"z")
|
||||
|
||||
(def ^:const deserialization-key
|
||||
"We pass this deserialization key as a parameter to MultiformatDeserializePublicKey
|
||||
function at status-go, This key determines the output base of the deserialization.
|
||||
@@ -432,3 +440,6 @@
|
||||
(def ^:const send-type-stickers-buy 4)
|
||||
(def ^:const send-type-bridge 5)
|
||||
(def ^:const send-type-erc-721-transfer 6)
|
||||
|
||||
(def ^:const bridge-name-transfer "Transfer")
|
||||
(def ^:const bridge-name-erc-721-transfer "ERC721Transfer")
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
{:icon :i/add-user
|
||||
:accessibility-label :add-a-contact
|
||||
:label (i18n/label :t/add-a-contact)
|
||||
:sub-label (i18n/label :t/enter-a-chat-key)
|
||||
:sub-label (i18n/label :t/enter-chat-key)
|
||||
:add-divider? true
|
||||
:on-press #(debounce/dispatch-and-chill
|
||||
[:open-modal :new-contact]
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
(ns status-im.contexts.chat.home.add-new-contact.effects
|
||||
(:require
|
||||
[legacy.status-im.ethereum.ens :as ens]
|
||||
[native-module.core :as native-module]
|
||||
[status-im.constants :as constants]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(rf/reg-fx :effects.contacts/decompress-public-key
|
||||
(fn [{:keys [compressed-key on-success on-error]}]
|
||||
(native-module/compressed-key->public-key
|
||||
compressed-key
|
||||
constants/deserialization-key
|
||||
(fn [resp]
|
||||
(let [{:keys [error]} (transforms/json->clj resp)]
|
||||
(if error
|
||||
(on-error error)
|
||||
(on-success (str "0x" (subs resp 5)))))))))
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-fx :effects.contacts/resolve-public-key-from-ens
|
||||
(fn [{:keys [chain-id ens on-success on-error]}]
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
:empty {:db (dissoc db :contacts/new-identity)}
|
||||
(:valid :invalid) {:db (assoc db :contacts/new-identity contact)}
|
||||
:decompress-key {:db (assoc db :contacts/new-identity contact)
|
||||
:effects.contacts/decompress-public-key
|
||||
:serialization/decompress-public-key
|
||||
{:compressed-key id
|
||||
:on-success
|
||||
(dispatcher :contacts/set-new-identity-success input)
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
:type :compressed-key
|
||||
:public-key nil ; not yet...
|
||||
:state :decompress-key}))
|
||||
:effects.contacts/decompress-public-key
|
||||
:serialization/decompress-public-key
|
||||
{:compressed-key user-ckey
|
||||
:on-success [:contacts/set-new-identity-success user-ckey]
|
||||
:on-error [:contacts/set-new-identity-error user-ckey]}})))
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
[]
|
||||
{:size :heading-1
|
||||
:weight :semi-bold
|
||||
:style {:margin-top 32
|
||||
:style {:margin-top 28
|
||||
:margin-bottom 6
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white)}})
|
||||
|
||||
|
||||
@@ -16,33 +16,38 @@
|
||||
|
||||
(defn- no-contacts-view
|
||||
[{:keys [theme]}]
|
||||
[rn/view
|
||||
{:style (style/no-contacts)}
|
||||
[rn/image {:source (resources/get-themed-image :no-contacts theme)}]
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-1
|
||||
:style {:margin-bottom 2
|
||||
:margin-top 12}}
|
||||
(i18n/label :t/you-have-no-contacts)]
|
||||
[quo/text
|
||||
{:weight :regular
|
||||
:size :label}
|
||||
(i18n/label :t/dont-yell-at-me)]
|
||||
[quo/button
|
||||
{:type :primary
|
||||
:size 32
|
||||
:container-style {:margin-top 20
|
||||
:margin-bottom 12}
|
||||
:on-press #(rf/dispatch [:invite.events/share-link])}
|
||||
(i18n/label :t/invite-friends)]
|
||||
[quo/button
|
||||
{:type :grey
|
||||
:size 32
|
||||
:on-press #(do
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:open-modal :new-contact]))}
|
||||
(i18n/label :t/add-a-contact)]])
|
||||
(let [customization-color (rf/sub [:profile/customization-color])]
|
||||
[rn/view
|
||||
{:style (style/no-contacts)}
|
||||
[rn/image {:source (resources/get-themed-image :no-contacts-to-chat theme)}]
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-1
|
||||
:style {:margin-bottom 2
|
||||
:margin-top 12}}
|
||||
(i18n/label :t/you-have-no-contacts)]
|
||||
[quo/text
|
||||
{:weight :regular
|
||||
:size :paragraph-2}
|
||||
(i18n/label :t/dont-yell-at-me)]
|
||||
[quo/button
|
||||
{:customization-color customization-color
|
||||
:theme theme
|
||||
:type :primary
|
||||
:size 32
|
||||
:container-style {:margin-top 20
|
||||
:margin-bottom 12}
|
||||
:on-press #(rf/dispatch [:invite.events/share-link])}
|
||||
(i18n/label :t/invite-friends)]
|
||||
[quo/button
|
||||
{:customization-color customization-color
|
||||
:theme theme
|
||||
:type :grey
|
||||
:size 32
|
||||
:on-press #(do
|
||||
(rf/dispatch [:navigate-back])
|
||||
(rf/dispatch [:open-modal :new-contact]))}
|
||||
(i18n/label :t/add-a-contact)]]))
|
||||
|
||||
(defn contact-item-render
|
||||
[_]
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
:shared-value scroll-shared-value})}])))
|
||||
|
||||
(defn contact-item-render
|
||||
[{:keys [public-key] :as item}]
|
||||
[{:keys [public-key] :as item} theme]
|
||||
(let [current-pk (rf/sub [:multiaccount/public-key])
|
||||
show-profile-actions #(rf/dispatch [:show-bottom-sheet
|
||||
{:content (fn [] [actions/contact-actions item])}])]
|
||||
@@ -83,7 +83,8 @@
|
||||
:on-long-press show-profile-actions
|
||||
:accessory {:type :options
|
||||
:on-press show-profile-actions}})
|
||||
item]))
|
||||
item
|
||||
theme]))
|
||||
|
||||
(defn contacts
|
||||
[{:keys [theme pending-contact-requests set-scroll-ref scroll-shared-value]}]
|
||||
@@ -105,7 +106,8 @@
|
||||
:sections items
|
||||
:sticky-section-headers-enabled false
|
||||
:render-section-header-fn contact-list/contacts-section-header
|
||||
:render-fn contact-item-render
|
||||
:render-fn (fn [data]
|
||||
(contact-item-render data theme))
|
||||
:scroll-event-throttle 8
|
||||
:on-scroll #(common.banner/set-scroll-shared-value
|
||||
{:scroll-input (oops/oget % "nativeEvent.contentOffset.y")
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
(js/setTimeout #(reanimated/set-shared-value background-y
|
||||
(- window-height))
|
||||
300)
|
||||
(rf/dispatch [:chat.ui/replace-mentions-and-send-current-message])
|
||||
(rf/dispatch [:chat.ui/send-current-message])
|
||||
(rf/dispatch [:chat.ui/set-input-maximized false])
|
||||
(rf/dispatch [:chat.ui/set-input-content-height comp-constants/input-height])
|
||||
(rf/dispatch [:chat.ui/set-chat-input-text nil])
|
||||
|
||||
@@ -16,13 +16,19 @@
|
||||
(defn reenter-screen-effect
|
||||
[{:keys [text-value saved-cursor-position maximized?]}
|
||||
{:keys [content-height]}
|
||||
{:keys [input-content-height input-text input-maximized?]}]
|
||||
(when (and (empty? @text-value) (not= input-text nil))
|
||||
(reset! text-value input-text)
|
||||
(reset! content-height input-content-height)
|
||||
(reset! saved-cursor-position (count input-text)))
|
||||
(when input-maximized?
|
||||
(reset! maximized? true)))
|
||||
{:keys [input-content-height input-text input-maximized?]}
|
||||
{:keys [height]}]
|
||||
(let [lines (utils/calc-lines input-content-height)
|
||||
minimized-height (if (or (= lines 1) (empty? input-text))
|
||||
constants/input-height
|
||||
constants/multiline-minimized-height)]
|
||||
(when (and (empty? @text-value) (not= input-text nil))
|
||||
(reset! text-value input-text)
|
||||
(reset! content-height input-content-height)
|
||||
(reset! saved-cursor-position (count input-text))
|
||||
(reanimated/set-shared-value height minimized-height))
|
||||
(when input-maximized?
|
||||
(reset! maximized? true))))
|
||||
|
||||
(defn maximized-effect
|
||||
[{:keys [maximized?]}
|
||||
@@ -101,16 +107,15 @@
|
||||
[max-height])
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reenter-screen-effect state dimensions subscriptions))
|
||||
(reenter-screen-effect state dimensions subscriptions animations))
|
||||
[max-height subscriptions]))
|
||||
|
||||
(defn use-edit
|
||||
[{:keys [input-ref]}
|
||||
{:keys [text-value saved-cursor-position cursor-position]}
|
||||
{:keys [edit input-with-mentions]}
|
||||
messages-list-on-layout-finished?]
|
||||
(let [mention? (some #(= :mention (first %)) (seq input-with-mentions))
|
||||
composer-just-opened? (not @messages-list-on-layout-finished?)]
|
||||
chat-screen-layout-calculations-complete?]
|
||||
(let [mention? (some #(= :mention (first %)) (seq input-with-mentions))]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(let [mention-text (reduce (fn [acc item]
|
||||
@@ -122,7 +127,9 @@
|
||||
;; NOTE: using text-value for cases when the user
|
||||
;; leaves the app with an unfinished edit and re-opens
|
||||
;; the chat.
|
||||
(and (seq @text-value) composer-just-opened?)
|
||||
(and (seq @text-value)
|
||||
(not (reanimated/get-shared-value
|
||||
chat-screen-layout-calculations-complete?)))
|
||||
@text-value
|
||||
:else (get-in edit [:content :text]))
|
||||
selection-pos (count edit-text)
|
||||
@@ -144,7 +151,9 @@
|
||||
;; UI. This way, `on-focus` will trigger first without changing the height, after which
|
||||
;; `on-content-size-change` will animate the height of the input based on the injected
|
||||
;; text.
|
||||
(js/setTimeout #(do (when @messages-list-on-layout-finished? (.focus ^js @input-ref))
|
||||
(js/setTimeout #(do (when (reanimated/get-shared-value
|
||||
chat-screen-layout-calculations-complete?)
|
||||
(.focus ^js @input-ref))
|
||||
(reagent/next-tick inject-edit-text))
|
||||
600))))
|
||||
[(:message-id edit)])))
|
||||
@@ -153,12 +162,12 @@
|
||||
[{:keys [input-ref]}
|
||||
{:keys [container-opacity]}
|
||||
{:keys [reply]}
|
||||
messages-list-on-layout-finished?]
|
||||
chat-screen-layout-calculations-complete?]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when reply
|
||||
(reanimated/animate container-opacity 1))
|
||||
(when (and reply @input-ref @messages-list-on-layout-finished?)
|
||||
(when (and reply @input-ref (reanimated/get-shared-value chat-screen-layout-calculations-complete?))
|
||||
(js/setTimeout #(.focus ^js @input-ref) 600)))
|
||||
[(:message-id reply)]))
|
||||
|
||||
|
||||
@@ -207,24 +207,12 @@
|
||||
(link-preview/reset-unfurled)
|
||||
(cancel-message-edit)))
|
||||
|
||||
(rf/defn replace-mentions-and-send-current-message
|
||||
(rf/defn send-current-message
|
||||
"Sends message from current chat input"
|
||||
{:events [:chat.ui/replace-mentions-and-send-current-message]}
|
||||
[{{:keys [current-chat-id] :as db} :db}]
|
||||
(let [{:keys [input-text metadata]} (get-in db [:chat/inputs current-chat-id])]
|
||||
{:json-rpc/call [{:method "wakuext_chatMentionReplaceWithPublicKey"
|
||||
:params [current-chat-id input-text]
|
||||
:on-error #(log/error "[wakuext_chatMentionReplaceWithPublicKey] on-error" %)
|
||||
:on-success #(rf/dispatch [:chat.ui/send-current-message-with-mentions
|
||||
current-chat-id
|
||||
(:editing-message metadata)
|
||||
input-text
|
||||
%])}]}))
|
||||
|
||||
(rf/defn send-current-message-with-mentions
|
||||
{:events [:chat.ui/send-current-message-with-mentions]}
|
||||
[{:keys [db] :as cofx} current-chat-id editing-message input-text new-text]
|
||||
(rf/merge cofx
|
||||
(if editing-message
|
||||
(send-edited-message new-text editing-message)
|
||||
(send-messages new-text current-chat-id))))
|
||||
{:events [:chat.ui/send-current-message]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx}]
|
||||
(let [{:keys [input-text metadata]} (get-in db [:chat/inputs current-chat-id])
|
||||
editing-message (:editing-message metadata)]
|
||||
(if editing-message
|
||||
(send-edited-message cofx input-text editing-message)
|
||||
(send-messages cofx input-text current-chat-id))))
|
||||
|
||||
@@ -31,8 +31,7 @@
|
||||
:position :absolute
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
:z-index 2})
|
||||
:right 0})
|
||||
|
||||
(defn bottom-gradient
|
||||
[]
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
(when show-bottom-gradient?
|
||||
[rn/pressable
|
||||
{:on-press #(when @input-ref (.focus ^js @input-ref))
|
||||
:style {:z-index 1}
|
||||
:accessibility-label :bottom-gradient}
|
||||
[linear-gradient/linear-gradient (style/bottom-gradient)]])]))
|
||||
|
||||
|
||||
@@ -16,18 +16,20 @@
|
||||
(defn focus
|
||||
"Animate to the `saved-height`, display background-overlay if needed, and set cursor position"
|
||||
[{:keys [input-ref] :as props}
|
||||
{:keys [text-value focused? lock-selection? saved-cursor-position composer-focused?]}
|
||||
{:keys [text-value focused? lock-selection? saved-cursor-position composer-focused? maximized?]}
|
||||
{:keys [height saved-height last-height opacity background-y container-opacity]
|
||||
:as animations}
|
||||
{:keys [max-height] :as dimensions}]
|
||||
(reanimated/set-shared-value composer-focused? true)
|
||||
(reset! focused? true)
|
||||
(rf/dispatch [:chat.ui/set-input-focused true])
|
||||
(let [last-height-value (reanimated/get-shared-value last-height)]
|
||||
(reanimated/animate height last-height-value)
|
||||
(reanimated/set-shared-value saved-height last-height-value)
|
||||
(let [last-height-value (reanimated/get-shared-value last-height)
|
||||
new-height (min max-height last-height-value)]
|
||||
(reanimated/animate height new-height)
|
||||
(reanimated/set-shared-value saved-height new-height)
|
||||
(reanimated/animate container-opacity 1)
|
||||
(when (> last-height-value (* constants/background-threshold max-height))
|
||||
(reset! maximized? true)
|
||||
(reanimated/animate opacity 1)
|
||||
(reanimated/set-shared-value background-y 0)))
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[status-im.contexts.chat.messenger.composer.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.worklets.chat.messages :as worklets]))
|
||||
[utils.worklets.chat.messenger.composer :as worklets]))
|
||||
|
||||
(defn bar
|
||||
[theme]
|
||||
|
||||
@@ -31,12 +31,12 @@
|
||||
(defn sheet-component
|
||||
[{:keys [insets
|
||||
chat-list-scroll-y
|
||||
chat-screen-layout-calculations-complete?
|
||||
window-height
|
||||
blur-height
|
||||
opacity
|
||||
background-y
|
||||
theme
|
||||
messages-list-on-layout-finished?]} props state]
|
||||
theme]} props state]
|
||||
(let [subscriptions (utils/init-subs)
|
||||
content-height (reagent/atom (or (:input-content-height ; Actual text height
|
||||
subscriptions)
|
||||
@@ -75,8 +75,8 @@
|
||||
animations
|
||||
dimensions
|
||||
subscriptions)
|
||||
(effects/use-edit props state subscriptions messages-list-on-layout-finished?)
|
||||
(effects/use-reply props animations subscriptions messages-list-on-layout-finished?)
|
||||
(effects/use-edit props state subscriptions chat-screen-layout-calculations-complete?)
|
||||
(effects/use-reply props animations subscriptions chat-screen-layout-calculations-complete?)
|
||||
(effects/update-input-mention props state subscriptions)
|
||||
(effects/link-previews props state animations subscriptions)
|
||||
(effects/use-images props state animations subscriptions)
|
||||
@@ -145,22 +145,20 @@
|
||||
[:f> actions/view props state animations window-height insets subscriptions]]]]]))
|
||||
|
||||
(defn f-composer
|
||||
[{:keys [insets chat-list-scroll-y messages-list-on-layout-finished?]}]
|
||||
[props]
|
||||
(let [window-height (:height (rn/get-window))
|
||||
theme (quo.theme/use-theme-value)
|
||||
opacity (reanimated/use-shared-value 0)
|
||||
background-y (reanimated/use-shared-value (- window-height)) ; Y position of background
|
||||
; overlay
|
||||
blur-height (reanimated/use-shared-value (+ constants/composer-default-height
|
||||
(:bottom insets)))
|
||||
extra-params {:insets insets
|
||||
:window-height window-height
|
||||
:chat-list-scroll-y chat-list-scroll-y
|
||||
:blur-height blur-height
|
||||
:opacity opacity
|
||||
:background-y background-y
|
||||
:theme theme
|
||||
:messages-list-on-layout-finished? messages-list-on-layout-finished?}
|
||||
(:bottom (:insets props))))
|
||||
extra-params (assoc props
|
||||
:window-height window-height
|
||||
:blur-height blur-height
|
||||
:opacity opacity
|
||||
:background-y background-y
|
||||
:theme theme)
|
||||
props (utils/init-non-reactive-state)
|
||||
state (utils/init-reactive-state)]
|
||||
[rn/view (when platform/ios? {:style {:z-index 1}})
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
[status-im.contexts.chat.messenger.lightbox.constants :as constants]
|
||||
[status-im.contexts.chat.messenger.lightbox.top-view :as top-view]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.worklets.chat.lightbox :as worklet]))
|
||||
[utils.worklets.chat.messenger.lightbox :as worklet]))
|
||||
|
||||
(defn clear-timers
|
||||
[timers]
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
(get constants/reactions (:emoji-id reaction))))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [message-id chat-id pinned-by theme]} user-message-content]
|
||||
[{:keys [message-id chat-id pinned-by preview? theme]} user-message-content]
|
||||
(let [reactions (rf/sub [:chats/message-reactions message-id chat-id])]
|
||||
[:<>
|
||||
(when (seq reactions)
|
||||
@@ -60,7 +60,7 @@
|
||||
{:container-style {:margin-left 44
|
||||
:margin-top 8}
|
||||
:reactions (map add-emoji-key reactions)
|
||||
:add-reaction? true
|
||||
:add-reaction? (not preview?)
|
||||
:use-case (when pinned-by :pinned)
|
||||
:on-press #(on-press (assoc % :message-id message-id))
|
||||
:on-long-press #(on-long-press (assoc %
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
(:require
|
||||
[quo.foundations.colors :as colors]))
|
||||
|
||||
(def ^:private message-padding-scaling-ratio 4.5)
|
||||
|
||||
(defn message-container
|
||||
([]
|
||||
(message-container false nil nil false))
|
||||
@@ -16,10 +18,16 @@
|
||||
(assoc :margin-top 4))))
|
||||
|
||||
(defn user-message-content
|
||||
[{:keys [outgoing outgoing-status]}]
|
||||
[{:keys [outgoing outgoing-status six-reactions? window-scale small-screen?]}]
|
||||
{:border-radius 16
|
||||
:padding-horizontal 8
|
||||
:padding-vertical 4
|
||||
:padding-top 4
|
||||
:padding-bottom (if (or small-screen?
|
||||
(and
|
||||
(> 3 window-scale)
|
||||
six-reactions?))
|
||||
(* message-padding-scaling-ratio window-scale)
|
||||
4)
|
||||
:opacity (if (and outgoing (= outgoing-status :sending))
|
||||
0.5
|
||||
1)})
|
||||
|
||||
@@ -119,31 +119,38 @@
|
||||
[]
|
||||
(let [show-delivery-state? (reagent/atom false)]
|
||||
(fn [{:keys [message-data context keyboard-shown? show-reactions? in-reaction-and-action-menu?
|
||||
show-user-info? theme]}]
|
||||
show-user-info? preview? theme]}]
|
||||
(let [{:keys [content-type quoted-message content
|
||||
outgoing outgoing-status pinned-by]} message-data
|
||||
first-image (first (:album message-data))
|
||||
outgoing-status (if (= content-type
|
||||
constants/content-type-album)
|
||||
(:outgoing-status first-image)
|
||||
outgoing-status)
|
||||
outgoing (if (= content-type
|
||||
constants/content-type-album)
|
||||
(:outgoing first-image)
|
||||
outgoing)
|
||||
context (assoc context
|
||||
:on-long-press
|
||||
#(on-long-press message-data
|
||||
context
|
||||
keyboard-shown?))
|
||||
response-to (:response-to content)
|
||||
height (rf/sub [:dimensions/window-height])
|
||||
{window-width :width} (rn/get-window)
|
||||
message-container-data {:window-width window-width
|
||||
:padding-right 20
|
||||
:padding-left 20
|
||||
:avatar-container-width 32
|
||||
:message-margin-left 8}]
|
||||
outgoing outgoing-status pinned-by
|
||||
message-id chat-id]} message-data
|
||||
first-image (first (:album message-data))
|
||||
outgoing-status (if (= content-type
|
||||
constants/content-type-album)
|
||||
(:outgoing-status first-image)
|
||||
outgoing-status)
|
||||
outgoing (if (= content-type
|
||||
constants/content-type-album)
|
||||
(:outgoing first-image)
|
||||
outgoing)
|
||||
context (assoc context
|
||||
:on-long-press
|
||||
#(on-long-press message-data
|
||||
context
|
||||
keyboard-shown?))
|
||||
response-to (:response-to content)
|
||||
height (rf/sub [:dimensions/window-height])
|
||||
{window-width :width
|
||||
window-scale :scale} (rn/get-window)
|
||||
message-container-data {:window-width window-width
|
||||
:padding-right 20
|
||||
:padding-left 20
|
||||
:avatar-container-width 32
|
||||
:message-margin-left 8}
|
||||
reactions (rf/sub [:chats/message-reactions message-id
|
||||
chat-id])
|
||||
six-reactions? (-> reactions
|
||||
count
|
||||
(= 6))]
|
||||
[rn/touchable-highlight
|
||||
{:accessibility-label (if (and outgoing (= outgoing-status :sending))
|
||||
:message-sending
|
||||
@@ -152,7 +159,10 @@
|
||||
:style (style/user-message-content
|
||||
{:first-in-group? (:first-in-group? message-data)
|
||||
:outgoing outgoing
|
||||
:outgoing-status outgoing-status})
|
||||
:outgoing-status outgoing-status
|
||||
:small-screen? rn/small-screen?
|
||||
:window-scale window-scale
|
||||
:six-reactions? six-reactions?})
|
||||
:on-press (fn []
|
||||
(if (and platform/ios? keyboard-shown?)
|
||||
(do
|
||||
@@ -212,7 +222,7 @@
|
||||
(when @show-delivery-state?
|
||||
[status/status outgoing-status])])]
|
||||
(when show-reactions?
|
||||
[reactions/message-reactions-row message-data
|
||||
[reactions/message-reactions-row (assoc message-data :preview? preview?)
|
||||
[rn/view {:pointer-events :none}
|
||||
[user-message-content-internal
|
||||
{:theme theme
|
||||
@@ -226,20 +236,24 @@
|
||||
(defn on-long-press
|
||||
[{:keys [deleted? deleted-for-me?] :as message-data} context keyboard-shown?]
|
||||
(rf/dispatch [:dismiss-keyboard])
|
||||
(rf/dispatch [:show-bottom-sheet
|
||||
{:content (drawers/reactions-and-actions message-data context)
|
||||
:border-radius 16
|
||||
:selected-item
|
||||
(if (or deleted? deleted-for-me?)
|
||||
(fn [] [content.deleted/deleted-message message-data])
|
||||
(fn []
|
||||
[rn/view {:pointer-events :none}
|
||||
[user-message-content
|
||||
{:message-data message-data
|
||||
:context context
|
||||
:keyboard-shown? keyboard-shown?
|
||||
:show-reactions? true
|
||||
:show-user-info? true}]]))}]))
|
||||
(rf/dispatch
|
||||
[:show-bottom-sheet
|
||||
{:content (drawers/reactions-and-actions message-data context)
|
||||
:border-radius 16
|
||||
:selected-item
|
||||
(if (or deleted? deleted-for-me?)
|
||||
(fn [] [content.deleted/deleted-message message-data])
|
||||
(fn []
|
||||
[rn/view
|
||||
{:pointer-events :none
|
||||
:padding-top 4}
|
||||
[user-message-content
|
||||
{:message-data message-data
|
||||
:context context
|
||||
:keyboard-shown? keyboard-shown?
|
||||
:show-reactions? true
|
||||
:show-user-info? true
|
||||
:preview? true}]]))}]))
|
||||
|
||||
(defn system-message?
|
||||
[content-type]
|
||||
|
||||
@@ -8,9 +8,7 @@
|
||||
:justify-content :center})
|
||||
|
||||
(def tab-icon
|
||||
{:margin-right 4
|
||||
:width 20
|
||||
:height 20})
|
||||
{:margin-right 4})
|
||||
|
||||
(defn tab-count
|
||||
[active? theme]
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
{:id reaction-type-int
|
||||
:accessibility-label (keyword (str "authors-for-reaction-" reaction-type-int))
|
||||
:label [rn/view {:style style/tab}
|
||||
[rn/image
|
||||
{:source (reactions.resource/get-reaction
|
||||
(get constants/reactions reaction-type-int))
|
||||
:style style/tab-icon}]
|
||||
[rn/text
|
||||
{:style style/tab-icon}
|
||||
(reactions.resource/system-emojis
|
||||
(get constants/reactions reaction-type-int))]
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:size :paragraph-1
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
(ns status-im.contexts.chat.messenger.messages.link-preview.events
|
||||
(:require
|
||||
[camel-snake-kebab.core :as csk]
|
||||
[legacy.status-im.mailserver.core :as mailserver]
|
||||
[schema.core :as schema]
|
||||
[status-im.contexts.profile.settings.events :as profile.settings.events]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.collection]
|
||||
@@ -42,96 +40,6 @@
|
||||
(boolean enabled?)
|
||||
{}))
|
||||
|
||||
(defn community-resolved
|
||||
[{:keys [db]} [community-id community]]
|
||||
(when community
|
||||
{:db (update db :communities/resolve-community-info dissoc community-id)
|
||||
:fx [[:dispatch [:communities/handle-community community]]
|
||||
[:dispatch
|
||||
[:chat.ui/cache-link-preview-data (community-link community-id) community]]]}))
|
||||
|
||||
(rf/reg-event-fx :chat.ui/community-resolved community-resolved)
|
||||
|
||||
(defn community-failed-to-resolve
|
||||
[{:keys [db]} [community-id]]
|
||||
{:db (update db :communities/resolve-community-info dissoc community-id)})
|
||||
|
||||
(rf/reg-event-fx :chat.ui/community-failed-to-resolve community-failed-to-resolve)
|
||||
|
||||
(defn fetch-community
|
||||
[{:keys [db]} [community-id]]
|
||||
(when community-id
|
||||
{:db (assoc-in db [:communities/resolve-community-info community-id] true)
|
||||
:json-rpc/call [{:method "wakuext_fetchCommunity"
|
||||
:params [{:CommunityKey community-id
|
||||
:TryDatabase true
|
||||
:WaitForResponse true}]
|
||||
:on-success (fn [community]
|
||||
(rf/dispatch [:chat.ui/community-resolved community-id community]))
|
||||
:on-error (fn [err]
|
||||
(rf/dispatch [:chat.ui/community-failed-to-resolve community-id])
|
||||
(log/error {:message
|
||||
"Failed to request community info from mailserver"
|
||||
:error err}))}]}))
|
||||
|
||||
(schema/=> fetch-community
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:schema [:catn [:community-id [:? :string]]]]]]
|
||||
[:maybe
|
||||
[:map
|
||||
[:db map?]
|
||||
[:json-rpc/call :schema.common/rpc-call]]]])
|
||||
|
||||
(rf/reg-event-fx :chat.ui/fetch-community fetch-community)
|
||||
|
||||
(defn spectate-community-success
|
||||
[{:keys [db]} [{:keys [communities]}]]
|
||||
(when-let [community (first communities)]
|
||||
{:db (-> db
|
||||
(assoc-in [:communities (:id community) :spectated] true)
|
||||
(assoc-in [:communities (:id community) :spectating] false))
|
||||
:fx [[:dispatch [:communities/handle-community community]]
|
||||
[:dispatch [::mailserver/request-messages]]]}))
|
||||
|
||||
(rf/reg-event-fx :chat.ui/spectate-community-success spectate-community-success)
|
||||
|
||||
(defn spectate-community-failed
|
||||
[{:keys [db]} [community-id]]
|
||||
{:db (assoc-in db [:communities community-id :spectating] false)})
|
||||
|
||||
(rf/reg-event-fx :chat.ui/spectate-community-failed spectate-community-failed)
|
||||
|
||||
(defn spectate-community
|
||||
[{:keys [db]} [community-id]]
|
||||
(let [{:keys [spectated spectating joined]} (get-in db [:communities community-id])]
|
||||
(when (and (not joined) (not spectated) (not spectating))
|
||||
{:db (assoc-in db [:communities community-id :spectating] true)
|
||||
:json-rpc/call [{:method "wakuext_spectateCommunity"
|
||||
:params [community-id]
|
||||
:on-success [:chat.ui/spectate-community-success]
|
||||
:on-error (fn [err]
|
||||
(log/error {:message
|
||||
"Failed to spectate community"
|
||||
:error err})
|
||||
(rf/dispatch [:chat.ui/spectate-community-failed
|
||||
community-id]))}]})))
|
||||
|
||||
(schema/=> spectate-community
|
||||
[:=>
|
||||
[:catn
|
||||
[:cofx :schema.re-frame/cofx]
|
||||
[:args
|
||||
[:schema [:catn [:community-id [:? :string]]]]]]
|
||||
[:maybe
|
||||
[:map
|
||||
[:db map?]
|
||||
[:json-rpc/call :schema.common/rpc-call]]]])
|
||||
|
||||
(rf/reg-event-fx :chat.ui/spectate-community spectate-community)
|
||||
|
||||
(rf/defn save-link-preview-whitelist
|
||||
{:events [:chat.ui/link-preview-whitelist-received]}
|
||||
[{:keys [db]} whitelist]
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
(ns status-im.contexts.chat.messenger.messages.link-preview.events-test
|
||||
(:require [cljs.test :as t]
|
||||
[legacy.status-im.mailserver.core :as mailserver]
|
||||
matcher-combinators.test
|
||||
[status-im.contexts.chat.messenger.messages.link-preview.events :as sut]))
|
||||
|
||||
(t/deftest fetch-community
|
||||
(t/testing "with community id"
|
||||
(t/testing "update resolving indicator in db"
|
||||
(t/is (match?
|
||||
{:db {:communities/resolve-community-info {"community-id" true}}}
|
||||
(sut/fetch-community {} ["community-id"]))))
|
||||
(t/testing "call the fetch community rpc method with correct community id"
|
||||
(t/is (match?
|
||||
{:json-rpc/call [{:method "wakuext_fetchCommunity"
|
||||
:params [{:CommunityKey "community-id"
|
||||
:TryDatabase true
|
||||
:WaitForResponse true}]}]}
|
||||
(sut/fetch-community {} ["community-id"])))))
|
||||
(t/testing "with no community id"
|
||||
(t/testing "do nothing"
|
||||
(t/is (match?
|
||||
nil
|
||||
(sut/fetch-community {} []))))))
|
||||
|
||||
(t/deftest community-failed-to-resolve
|
||||
(t/testing "given a community id"
|
||||
(t/testing "remove community id from resolving indicator in db"
|
||||
(t/is (match?
|
||||
nil
|
||||
(get-in (sut/community-failed-to-resolve {:db {:communities/resolve-community-info
|
||||
{"community-id" true}}}
|
||||
["community-id"])
|
||||
[:db :communities/resolve-community-info "community-id"]))))))
|
||||
|
||||
(t/deftest community-resolved
|
||||
(with-redefs [sut/community-link (fn [id] (str "community-link+" id))]
|
||||
(t/testing "given a community"
|
||||
(let [cofx {:db {:communities/resolve-community-info {"community-id" true}}}
|
||||
arg ["community-id" {:id "community-id"}]]
|
||||
(t/testing "remove community id from resolving indicator in db"
|
||||
(t/is (match?
|
||||
nil
|
||||
(get-in (sut/community-resolved cofx arg)
|
||||
[:db :communities/resolve-community-info "community-id"]))))
|
||||
(t/testing "dispatch fxs"
|
||||
(t/is (match?
|
||||
{:fx [[:dispatch [:communities/handle-community {:id "community-id"}]]
|
||||
[:dispatch
|
||||
[:chat.ui/cache-link-preview-data "community-link+community-id"
|
||||
{:id "community-id"}]]]}
|
||||
(sut/community-resolved cofx arg))))))
|
||||
(t/testing "given a joined community"
|
||||
(let [cofx {:db {:communities/resolve-community-info {"community-id" true}}}
|
||||
arg ["community-id" {:id "community-id" :joined true}]]
|
||||
(t/testing "dispatch fxs, do not spectate community"
|
||||
(t/is (match?
|
||||
{:fx [[:dispatch [:communities/handle-community {:id "community-id"}]]
|
||||
[:dispatch
|
||||
[:chat.ui/cache-link-preview-data "community-link+community-id"
|
||||
{:id "community-id"}]]]}
|
||||
(sut/community-resolved cofx arg))))))
|
||||
(t/testing "given a token-gated community"
|
||||
(let [cofx {:db {:communities/resolve-community-info {"community-id" true}}}
|
||||
arg ["community-id" {:id "community-id" :tokenPermissions [1]}]]
|
||||
(t/testing "dispatch fxs, do not spectate community"
|
||||
(t/is (match?
|
||||
{:fx [[:dispatch [:communities/handle-community {:id "community-id"}]]
|
||||
[:dispatch
|
||||
[:chat.ui/cache-link-preview-data "community-link+community-id"
|
||||
{:id "community-id"}]]]}
|
||||
(sut/community-resolved cofx arg))))))
|
||||
(t/testing "given nil community"
|
||||
(t/testing "do nothing"
|
||||
(t/is (match?
|
||||
nil
|
||||
(sut/community-resolved {} ["community-id" nil])))))))
|
||||
|
||||
(t/deftest spectate-community
|
||||
(t/testing "given a joined community"
|
||||
(t/testing "do nothing"
|
||||
(t/is (match?
|
||||
nil
|
||||
(sut/spectate-community {:db {:communities {"community-id" {:joined true}}}}
|
||||
["community-id"])))))
|
||||
(t/testing "given a spectated community"
|
||||
(t/testing "do nothing"
|
||||
(t/is (match?
|
||||
nil
|
||||
(sut/spectate-community {:db {:communities {"community-id" {:spectated true}}}}
|
||||
["community-id"])))))
|
||||
(t/testing "given a spectating community"
|
||||
(t/testing "do nothing"
|
||||
(t/is (match?
|
||||
nil
|
||||
(sut/spectate-community {:db {:communities {"community-id" {:spectating true}}}}
|
||||
["community-id"])))))
|
||||
(t/testing "given a community"
|
||||
(t/testing "mark community spectating"
|
||||
(t/is (match?
|
||||
{:db {:communities {"community-id" {:spectating true}}}}
|
||||
(sut/spectate-community {:db {:communities {"community-id" {}}}} ["community-id"]))))
|
||||
(t/testing "call spectate community rpc with correct community id"
|
||||
(t/is (match?
|
||||
{:json-rpc/call [{:method "wakuext_spectateCommunity"
|
||||
:params ["community-id"]}]}
|
||||
(sut/spectate-community {:db {:communities {"community-id" {}}}} ["community-id"]))))))
|
||||
|
||||
(t/deftest spectate-community-failed
|
||||
(t/testing "mark community spectating false"
|
||||
(t/is (match?
|
||||
{:db {:communities {"community-id" {:spectating false}}}}
|
||||
(sut/spectate-community-failed {} ["community-id"])))))
|
||||
|
||||
(t/deftest spectate-community-success
|
||||
(t/testing "given communities"
|
||||
(t/testing "mark first community spectating false"
|
||||
(t/is (match?
|
||||
{:db {:communities {"community-id" {:spectating false}}}}
|
||||
(sut/spectate-community-success {} [{:communities [{:id "community-id"}]}]))))
|
||||
(t/testing "mark first community spectated true"
|
||||
(t/is (match?
|
||||
{:db {:communities {"community-id" {:spectated true}}}}
|
||||
(sut/spectate-community-success {} [{:communities [{:id "community-id"}]}]))))
|
||||
(t/testing "dispatch fxs for first community"
|
||||
(t/is (match?
|
||||
{:fx [[:dispatch [:communities/handle-community {:id "community-id"}]]
|
||||
[:dispatch [::mailserver/request-messages]]]}
|
||||
(sut/spectate-community-success {} [{:communities [{:id "community-id"}]}])))))
|
||||
(t/testing "given empty community"
|
||||
(t/testing "do nothing"
|
||||
(t/is (match?
|
||||
nil
|
||||
(sut/spectate-community-success {} [{:communities []}])))))
|
||||
(t/testing "given nil community"
|
||||
(t/testing "do nothing"
|
||||
(t/is (match?
|
||||
nil
|
||||
(sut/spectate-community-success {} []))))))
|
||||
@@ -81,7 +81,7 @@
|
||||
(fn []
|
||||
(when-not cached-preview-data
|
||||
(let [community-id (community-id-from-link community-link)]
|
||||
(rf/dispatch [:chat.ui/fetch-community community-id]))))
|
||||
(rf/dispatch [:communities/fetch-community community-id]))))
|
||||
:reagent-render
|
||||
(fn []
|
||||
(when cached-preview-data
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
[status-im.contexts.shell.jump-to.constants :as jump-to.constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.worklets.chat.messages :as worklets]))
|
||||
[utils.worklets.chat.messenger.messages :as worklets]))
|
||||
|
||||
(defonce ^:const loading-indicator-extra-spacing 250)
|
||||
(defonce ^:const loading-indicator-page-loading-height 100)
|
||||
@@ -159,7 +159,7 @@
|
||||
unmute-chat-label
|
||||
mute-chat-label))
|
||||
:color cover-bg-color
|
||||
:icon (if muted? :i/muted :i/activity-center)
|
||||
:icon (if muted? :i/activity-center :i/muted)
|
||||
:on-press (fn []
|
||||
(if muted?
|
||||
(home.actions/unmute-chat-action chat-id)
|
||||
@@ -267,7 +267,7 @@
|
||||
|
||||
(defn on-layout
|
||||
[{:keys [event layout-height distance-atom distance-from-list-top
|
||||
calculations-complete? messages-list-on-layout-finished?]}]
|
||||
chat-screen-layout-calculations-complete?]}]
|
||||
(let [layout-height-new (oops/oget event "nativeEvent.layout.height")
|
||||
change (- layout-height-new @layout-height)
|
||||
new-distance (- @distance-atom change)]
|
||||
@@ -275,9 +275,8 @@
|
||||
(reanimated/set-shared-value distance-from-list-top new-distance)
|
||||
(reset! distance-atom new-distance)
|
||||
(reset! layout-height layout-height-new))
|
||||
(when-not (reanimated/get-shared-value calculations-complete?)
|
||||
(reanimated/set-shared-value calculations-complete? true))
|
||||
(js/setTimeout #(reset! messages-list-on-layout-finished? true) 1000)))
|
||||
(when-not (reanimated/get-shared-value chat-screen-layout-calculations-complete?)
|
||||
(reanimated/set-shared-value chat-screen-layout-calculations-complete? true))))
|
||||
|
||||
(defn on-scroll-fn
|
||||
[distance-atom layout-height-atom]
|
||||
@@ -288,7 +287,7 @@
|
||||
|
||||
(defn f-messages-list-content
|
||||
[{:keys [insets distance-from-list-top content-height layout-height cover-bg-color distance-atom
|
||||
calculations-complete? messages-list-on-layout-finished? chat-list-scroll-y]}]
|
||||
chat-screen-layout-calculations-complete? chat-list-scroll-y]}]
|
||||
(let [theme (quo.theme/use-theme-value)
|
||||
chat (rf/sub [:chats/current-chat-chat-view])
|
||||
{:keys [keyboard-shown]} (hooks/use-keyboard)
|
||||
@@ -351,8 +350,7 @@
|
||||
:layout-height layout-height
|
||||
:distance-atom distance-atom
|
||||
:distance-from-list-top distance-from-list-top
|
||||
:calculations-complete? calculations-complete?
|
||||
:messages-list-on-layout-finished?
|
||||
messages-list-on-layout-finished?})
|
||||
:chat-screen-layout-calculations-complete?
|
||||
chat-screen-layout-calculations-complete?})
|
||||
:scroll-enabled (not recording?)
|
||||
:content-inset-adjustment-behavior :never}]]]))
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
[status-im.contexts.chat.messenger.messages.pin.banner.view :as pin.banner]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.worklets.chat.messages :as worklets]))
|
||||
[utils.worklets.chat.messenger.navigation :as worklets]))
|
||||
|
||||
(defn f-header-content-container
|
||||
[{:keys [chat distance-from-list-top all-loaded? calculations-complete?]}]
|
||||
[{:keys [chat distance-from-list-top all-loaded? chat-screen-layout-calculations-complete?]}]
|
||||
(let [{:keys [chat-id group-chat chat-type chat-name
|
||||
emoji]} chat
|
||||
display-name (cond
|
||||
@@ -34,7 +34,7 @@
|
||||
header-opacity (worklets/navigation-header-opacity
|
||||
distance-from-list-top
|
||||
all-loaded?
|
||||
calculations-complete?
|
||||
chat-screen-layout-calculations-complete?
|
||||
(if platform/ios?
|
||||
messages.constants/content-animation-start-position-ios
|
||||
messages.constants/content-animation-start-position-android))
|
||||
@@ -104,7 +104,7 @@
|
||||
:top-offset navigation-view-height}]]))
|
||||
|
||||
(defn f-view
|
||||
[{:keys [distance-from-list-top calculations-complete?]}]
|
||||
[{:keys [distance-from-list-top chat-screen-layout-calculations-complete?]}]
|
||||
(let [{:keys [chat-id chat-type] :as chat} (rf/sub [:chats/current-chat-chat-view])
|
||||
all-loaded? (reanimated/use-shared-value false)
|
||||
all-loaded-sub (rf/sub [:chats/all-loaded? chat-id])
|
||||
@@ -129,10 +129,10 @@
|
||||
:on-press #(rf/dispatch [:navigate-back])}
|
||||
(if (= chat-type constants/community-chat-type) :i/arrow-left :i/close)]
|
||||
[:f> f-header-content-container
|
||||
{:chat chat
|
||||
:distance-from-list-top distance-from-list-top
|
||||
:all-loaded? all-loaded?
|
||||
:calculations-complete? calculations-complete?}]
|
||||
{:chat chat
|
||||
:distance-from-list-top distance-from-list-top
|
||||
:all-loaded? all-loaded?
|
||||
:chat-screen-layout-calculations-complete? chat-screen-layout-calculations-complete?}]
|
||||
[quo/button
|
||||
{:icon-only? true
|
||||
:type :grey
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.contexts.chat.messenger.composer.view :as composer.view]
|
||||
[status-im.contexts.chat.messenger.messages.list.style :as style]
|
||||
[status-im.contexts.chat.messenger.messages.list.view :as list.view]
|
||||
@@ -15,49 +14,47 @@
|
||||
;; improvement. Please avoid refactoring these files. Also if you are not already working on bug
|
||||
;; fixes related to the composer, please skip them. And ping me, so I can address them while refactoring
|
||||
(defn- f-chat-screen
|
||||
[calculations-complete?]
|
||||
(let [insets (safe-area/get-insets)
|
||||
content-height (atom 0)
|
||||
layout-height (atom 0)
|
||||
distance-atom (atom 0)
|
||||
messages-list-on-layout-finished? (reagent/atom false)
|
||||
distance-from-list-top (reanimated/use-shared-value 0)
|
||||
chat-list-scroll-y (reanimated/use-shared-value 0)]
|
||||
[chat-screen-layout-calculations-complete?]
|
||||
(let [insets (safe-area/get-insets)
|
||||
content-height (atom 0)
|
||||
layout-height (atom 0)
|
||||
distance-atom (atom 0)
|
||||
distance-from-list-top (reanimated/use-shared-value 0)
|
||||
chat-list-scroll-y (reanimated/use-shared-value 0)]
|
||||
[rn/keyboard-avoiding-view
|
||||
{:style style/keyboard-avoiding-container
|
||||
:keyboard-vertical-offset (- (:bottom insets))}
|
||||
[:f> messages.navigation/f-view
|
||||
{:distance-from-list-top distance-from-list-top
|
||||
:calculations-complete? calculations-complete?}]
|
||||
{:distance-from-list-top distance-from-list-top
|
||||
:chat-screen-layout-calculations-complete? chat-screen-layout-calculations-complete?}]
|
||||
[:f> list.view/f-messages-list-content
|
||||
{:insets insets
|
||||
:layout-height layout-height
|
||||
:content-height content-height
|
||||
:distance-atom distance-atom
|
||||
:calculations-complete? calculations-complete?
|
||||
:distance-from-list-top distance-from-list-top
|
||||
:chat-list-scroll-y chat-list-scroll-y
|
||||
:messages-list-on-layout-finished? messages-list-on-layout-finished?
|
||||
:cover-bg-color :turquoise}]
|
||||
{:insets insets
|
||||
:layout-height layout-height
|
||||
:content-height content-height
|
||||
:distance-atom distance-atom
|
||||
:chat-screen-layout-calculations-complete? chat-screen-layout-calculations-complete?
|
||||
:distance-from-list-top distance-from-list-top
|
||||
:chat-list-scroll-y chat-list-scroll-y
|
||||
:cover-bg-color :turquoise}]
|
||||
[composer.view/composer
|
||||
{:insets insets
|
||||
:chat-list-scroll-y chat-list-scroll-y
|
||||
:messages-list-on-layout-finished? messages-list-on-layout-finished?}]]))
|
||||
{:insets insets
|
||||
:chat-screen-layout-calculations-complete? chat-screen-layout-calculations-complete?
|
||||
:chat-list-scroll-y chat-list-scroll-y}]]))
|
||||
|
||||
(defn lazy-chat-screen
|
||||
[calculations-complete?]
|
||||
[chat-screen-layout-calculations-complete?]
|
||||
(let [screen-loaded? (rf/sub [:shell/chat-screen-loaded?])]
|
||||
(when-not screen-loaded?
|
||||
(reanimated/set-shared-value calculations-complete? false))
|
||||
(reanimated/set-shared-value chat-screen-layout-calculations-complete? false))
|
||||
(when screen-loaded?
|
||||
[:f> f-chat-screen calculations-complete?])))
|
||||
[:f> f-chat-screen chat-screen-layout-calculations-complete?])))
|
||||
|
||||
(defn- f-chat
|
||||
[]
|
||||
(let [calculations-complete? (reanimated/use-shared-value false)]
|
||||
(let [chat-screen-layout-calculations-complete? (reanimated/use-shared-value false)]
|
||||
[:<>
|
||||
[lazy-chat-screen calculations-complete?]
|
||||
[:f> placeholder.view/f-view calculations-complete?]]))
|
||||
[lazy-chat-screen chat-screen-layout-calculations-complete?]
|
||||
[:f> placeholder.view/f-view chat-screen-layout-calculations-complete?]]))
|
||||
|
||||
(defn chat
|
||||
[]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im.contexts.chat.messenger.placeholder.style :as style]
|
||||
[utils.worklets.chat.messages :as worklets]))
|
||||
[utils.worklets.chat.messenger.placeholder :as worklets]))
|
||||
|
||||
(defn- loading-skeleton
|
||||
[]
|
||||
@@ -15,9 +15,9 @@
|
||||
:animated? false}])
|
||||
|
||||
(defn f-view
|
||||
[calculations-complete?]
|
||||
[chat-screen-layout-calculations-complete?]
|
||||
(let [top (safe-area/get-top)
|
||||
opacity (worklets/placeholder-opacity calculations-complete?)
|
||||
z-index (worklets/placeholder-z-index calculations-complete?)]
|
||||
opacity (worklets/placeholder-opacity chat-screen-layout-calculations-complete?)
|
||||
z-index (worklets/placeholder-z-index chat-screen-layout-calculations-complete?)]
|
||||
[reanimated/view {:style (style/container top opacity z-index)}
|
||||
[loading-skeleton]]))
|
||||
|
||||
@@ -10,28 +10,21 @@
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- join-community-and-navigate-back
|
||||
[id addresses-for-permissions]
|
||||
[id]
|
||||
(rf/dispatch [:password-authentication/show
|
||||
{:content (fn [] [password-authentication/view])}
|
||||
{:label (i18n/label :t/join-open-community)
|
||||
:on-press #(rf/dispatch [:communities/request-to-join
|
||||
{:community-id id
|
||||
:password %
|
||||
:addresses-to-reveal addresses-for-permissions}])}])
|
||||
:on-press #(rf/dispatch [:communities/request-to-join-with-addresses
|
||||
{:community-id id
|
||||
:password %}])}])
|
||||
(rf/dispatch [:navigate-back]))
|
||||
|
||||
(defn f-view-internal
|
||||
[]
|
||||
(let [{id :community-id} (rf/sub [:get-screen-params])
|
||||
{:keys [name color images]} (rf/sub [:communities/community id])
|
||||
accounts (rf/sub [:wallet/accounts-with-customization-color])
|
||||
selected-permission-addresses (rf/sub [:communities/selected-permission-addresses])
|
||||
selected-accounts (filter #(contains? selected-permission-addresses
|
||||
(:address %))
|
||||
accounts)]
|
||||
(rn/use-effect (fn []
|
||||
(rf/dispatch [:communities/initialize-permission-addresses]))
|
||||
[])
|
||||
(let [{id :community-id} (rf/sub [:get-screen-params])
|
||||
{:keys [name color images]} (rf/sub [:communities/community id])
|
||||
airdrop-account (rf/sub [:communities/airdrop-account id])
|
||||
selected-accounts (rf/sub [:communities/selected-permission-accounts id])]
|
||||
[rn/view {:style style/container}
|
||||
[quo/page-nav
|
||||
{:text-align :left
|
||||
@@ -71,8 +64,8 @@
|
||||
:action :arrow
|
||||
:label :preview
|
||||
:label-props {:type :accounts
|
||||
:data [(first accounts)]}
|
||||
:description-props {:text (-> accounts first :name)}}]}]
|
||||
:data [airdrop-account]}
|
||||
:description-props {:text (:name airdrop-account)}}]}]
|
||||
[quo/text
|
||||
{:style style/section-title
|
||||
:accessibility-label :community-rules-title
|
||||
@@ -86,7 +79,7 @@
|
||||
:track-text (i18n/label :t/slide-to-request-to-join)
|
||||
:track-icon :i/face-id
|
||||
:customization-color color
|
||||
:on-complete #(join-community-and-navigate-back id selected-permission-addresses)}]]]))
|
||||
:on-complete #(join-community-and-navigate-back id)}]]]))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn- account-item
|
||||
[item _ _ selected-addresses]
|
||||
[item _ _ [selected-addresses community-id]]
|
||||
[quo/account-permissions
|
||||
{:account {:name (:name item)
|
||||
:address (:address item)
|
||||
@@ -17,7 +17,7 @@
|
||||
:token-details []
|
||||
:checked? (contains? selected-addresses (:address item))
|
||||
:on-change #(rf/dispatch [:communities/toggle-selected-permission-address
|
||||
(:address item)])
|
||||
(:address item) community-id])
|
||||
:container-style {:margin-bottom 8}}])
|
||||
|
||||
(defn view
|
||||
@@ -25,7 +25,7 @@
|
||||
(let [{id :community-id} (rf/sub [:get-screen-params])
|
||||
{:keys [name color images]} (rf/sub [:communities/community id])
|
||||
accounts (rf/sub [:wallet/accounts-with-customization-color])
|
||||
selected-addresses (rf/sub [:communities/selected-permission-addresses])]
|
||||
selected-addresses (rf/sub [:communities/selected-permission-addresses id])]
|
||||
[rn/safe-area-view {:style style/container}
|
||||
[quo/drawer-top
|
||||
{:type :context-tag
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
[rn/flat-list
|
||||
{:render-fn account-item
|
||||
:render-data selected-addresses
|
||||
:render-data [selected-addresses id]
|
||||
:content-container-style {:padding 20}
|
||||
:key-fn :address
|
||||
:data accounts}]
|
||||
@@ -60,7 +60,7 @@
|
||||
{:type :grey
|
||||
:container-style {:flex 1}
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:communities/reset-selected-permission-addresses])
|
||||
(rf/dispatch [:communities/reset-selected-permission-addresses id])
|
||||
(rf/dispatch [:navigate-back]))}
|
||||
(i18n/label :t/cancel)]
|
||||
[quo/button
|
||||
@@ -68,6 +68,6 @@
|
||||
:customization-color color
|
||||
:disabled? (empty? selected-addresses)
|
||||
:on-press (fn []
|
||||
(rf/dispatch [:communities/update-previous-permission-addresses])
|
||||
(rf/dispatch [:communities/update-previous-permission-addresses id])
|
||||
(rf/dispatch [:navigate-back]))}
|
||||
(i18n/label :t/confirm-changes)]]]))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
(ns status-im.contexts.communities.actions.airdrop-addresses.style)
|
||||
|
||||
(def account-list-container
|
||||
{:flex 1
|
||||
:padding-top 12
|
||||
:padding-bottom 8})
|
||||
{:padding-horizontal 8
|
||||
:padding-bottom 8})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user