diff --git a/externs.js b/externs.js index e292a401f1..cbcbe142a2 100644 --- a/externs.js +++ b/externs.js @@ -143,7 +143,10 @@ var TopLevel = { "extPost" : function () {}, "extractGroupMembershipSignatures" : function () {}, "identicon": function() {}, + "identiconAsync": function() {}, "generateAlias": function() {}, + "generateAliasAsync": function() {}, + "generateAliasAndIdenticonAsync": function() {}, "fallbacks" : function () {}, "fetch" : function () {}, "floor" : function () {}, diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index cbcf09a225..d9dc7d57ef 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -1144,11 +1144,76 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL return Statusgo.generateAlias(seed); } + @ReactMethod + public void generateAliasAsync(final String seed, final Callback callback) { + Log.d(TAG, "generateAliasAsync"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.generateAlias(seed); + + Log.d(TAG, res); + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + @ReactMethod(isBlockingSynchronousMethod = true) public String identicon(final String seed) { return Statusgo.identicon(seed); } + @ReactMethod + public void identiconAsync(final String seed, final Callback callback) { + Log.d(TAG, "identiconAsync"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable r = new Runnable() { + @Override + public void run() { + String res = Statusgo.identicon(seed); + + Log.d(TAG, res); + callback.invoke(res); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + + @ReactMethod + public void generateAliasAndIdenticonAsync(final String seed, final Callback callback) { + Log.d(TAG, "generateAliasAndIdenticonAsync"); + if (!checkAvailability()) { + callback.invoke(false); + return; + } + + Runnable r = new Runnable() { + @Override + public void run() { + String resIdenticon = Statusgo.identicon(seed); + String resAlias = Statusgo.generateAlias(seed); + + Log.d(TAG, resIdenticon); + Log.d(TAG, resAlias); + callback.invoke(resAlias, resIdenticon); + } + }; + + StatusThreadPoolExecutor.getInstance().execute(r); + } + @ReactMethod public void getNodesFromContract(final String rpcEndpoint, final String contractAddress, final Callback callback) { diff --git a/modules/react-native-status/desktop/rctstatus.cpp b/modules/react-native-status/desktop/rctstatus.cpp index 41637f254c..21a54df2bc 100644 --- a/modules/react-native-status/desktop/rctstatus.cpp +++ b/modules/react-native-status/desktop/rctstatus.cpp @@ -653,13 +653,38 @@ void RCTStatus::chaosModeUpdate(bool on, double callbackId) { QString RCTStatus::generateAlias(QString publicKey) { Q_D(RCTStatus); qCDebug(RCTSTATUS) << "::generateAlias call"; - // return GenerateGfycat(publicKey.toUtf8().data()); - return "test"; + return ""; +} + +void RCTStatus::generateAliasAsync(QString publicKey, double callbackId) { + Q_D(RCTStatus); + qCDebug(RCTSTATUS) << "::generateAliasAsync call"; + QByteArray b = publicKey.toUtf8(); + const char *result = GenerateAlias({b.data(), b.length()}); + qCDebug(RCTSTATUS) << "::generateAliasAsync call result"<bridge->invokePromiseCallback(callbackId, QVariantList{result}); } QString RCTStatus::identicon(QString publicKey) { Q_D(RCTStatus); qCDebug(RCTSTATUS) << "::identicon call"; - // return Identicon(publicKey.toUtf8().data()); - return "test"; + return ""; } + +void RCTStatus::identiconAsync(QString publicKey, double callbackId) { + Q_D(RCTStatus); + qCDebug(RCTSTATUS) << "::identiconAsync call"; + QByteArray b = publicKey.toUtf8(); + const char *result = Identicon({b.data(), b.length()}); + qCDebug(RCTSTATUS) << "::identiconAsync call result"<bridge->invokePromiseCallback(callbackId, QVariantList{result}); +} + +void RCTStatus::generateAliasAndIdenticonAsync(QString publicKey, double callbackId) { + Q_D(RCTStatus); + qCDebug(RCTSTATUS) << "::generateAliasAndIdenticonAsync call"; + QByteArray pubKey = publicKey.toUtf8(); + const char *alias = GenerateAlias({pubKey.data(), pubKey.length()}); + const char *identicon = Identicon({pubKey.data(), pubKey.length()}); + d->bridge->invokePromiseCallback(callbackId, QVariantList{alias, identicon}); +} \ No newline at end of file diff --git a/modules/react-native-status/desktop/rctstatus.h b/modules/react-native-status/desktop/rctstatus.h index 90c70c8125..603d88b82b 100644 --- a/modules/react-native-status/desktop/rctstatus.h +++ b/modules/react-native-status/desktop/rctstatus.h @@ -93,7 +93,10 @@ public: Q_INVOKABLE static void statusGoEventCallback(const char *event); Q_INVOKABLE QString identicon(QString publicKey); + Q_INVOKABLE void identiconAsync(QString publicKey, double callbackId); Q_INVOKABLE QString generateAlias(QString publicKey); + Q_INVOKABLE void generateAliasAsync(QString publicKey, double callbackId); + Q_INVOKABLE void generateAliasAndIdenticonAsync(QString publicKey, double callbackId); void emitStatusGoEvent(QString event); diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index b94ef4a4c2..03a59f075f 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -585,10 +585,38 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(generateAlias:(NSString *)publicKey) { return StatusgoGenerateAlias(publicKey); } +RCT_EXPORT_METHOD(generateAliasAsync:(NSString *)publicKey + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"generateAliasAsync() method called"); +#endif + NSString *result = StatusgoGenerateAlias(publicKey); + callback(@[result]); +} + RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(identicon:(NSString *)publicKey) { return StatusgoIdenticon(publicKey); } +RCT_EXPORT_METHOD(identiconAsync:(NSString *)publicKey + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"identiconAsync() method called"); +#endif + NSString *result = StatusgoIdenticon(publicKey); + callback(@[result]); +} + +RCT_EXPORT_METHOD(generateAliasAndIdenticonAsync:(NSString *)publicKey + callback:(RCTResponseSenderBlock)callback) { +#if DEBUG + NSLog(@"generateAliasAndIdenticonAsync() method called"); +#endif + NSString *identiconResult = StatusgoIdenticon(publicKey); + NSString *aliasResult = StatusgoGenerateAlias(publicKey); + callback(@[aliasResult, identiconResult]); +} + RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload callback:(RCTResponseSenderBlock)callback) { dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ diff --git a/react-native/src/desktop/status_im/react_native/js_dependencies.cljs b/react-native/src/desktop/status_im/react_native/js_dependencies.cljs index 080862fc73..f9e28d482d 100644 --- a/react-native/src/desktop/status_im/react_native/js_dependencies.cljs +++ b/react-native/src/desktop/status_im/react_native/js_dependencies.cljs @@ -36,5 +36,5 @@ (def net-info #js {:default #js {}}) (def react-native-mail #js {:mail (fn [])}) (def async-storage #js {}) -(def back-handler #js {}) -(def safe-area-context #js {}) \ No newline at end of file +(def back-handler #js {:addEventListener (fn [])}) +(def safe-area-context #js {}) diff --git a/src/status_im/chat/models.cljs b/src/status_im/chat/models.cljs index 0d8bddc9e1..dc49424b7b 100644 --- a/src/status_im/chat/models.cljs +++ b/src/status_im/chat/models.cljs @@ -3,6 +3,7 @@ [status-im.multiaccounts.model :as multiaccounts.model] [status-im.transport.filters.core :as transport.filters] [status-im.contact.core :as contact.core] + [status-im.contact.db :as contact.db] [status-im.data-store.chats :as chats-store] [status-im.data-store.messages :as messages-store] [status-im.ethereum.json-rpc :as json-rpc] @@ -226,6 +227,8 @@ (transport.filters/load-chat chat-id)) (when platform/desktop? (mark-messages-seen chat-id)) + (when (and (one-to-one-chat? cofx chat-id) (not (contact.db/contact-exists? db chat-id))) + (contact.core/create-contact chat-id)) (tribute-to-talk/check-tribute chat-id))) (fx/defn navigate-to-chat diff --git a/src/status_im/contact/core.cljs b/src/status_im/contact/core.cljs index f5115722f2..ec01ead4eb 100644 --- a/src/status_im/contact/core.cljs +++ b/src/status_im/contact/core.cljs @@ -147,7 +147,10 @@ (get-in db [:contacts/contacts public-key])) (assoc :tribute-to-talk (or tribute-to-talk {:disabled? true})))] - {:db (assoc-in db [:contacts/contacts public-key] contact)})) + {:db (assoc-in db [:contacts/contacts public-key] contact) + :insert-identicons [[public-key [:contacts/contacts public-key :identicon]]] + :insert-gfycats [[public-key [:contacts/contacts public-key :name]] + [public-key [:contacts/contacts public-key :alias]]]})) (defn add-ens-names [contacts names] (reduce-kv (fn [acc public-key-keyword result] diff --git a/src/status_im/contact/db.cljs b/src/status_im/contact/db.cljs index b160301f28..03fa4eb9f1 100644 --- a/src/status_im/contact/db.cljs +++ b/src/status_im/contact/db.cljs @@ -120,6 +120,10 @@ (assoc % :admin? true) %))))) +(defn contact-exists? + [db public-key] + (get-in db [:contacts/contacts public-key])) + (defn added? ([{:keys [system-tags]}] (contains? system-tags :contact/added)) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 188b3e0164..3c81eaeed0 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -1565,3 +1565,13 @@ request-command {:asset (name symbol) :amount (str (money/internal->formatted amount symbol decimals))}))))) + +(handlers/register-handler-fx + :identicon-generated + (fn [{:keys [db]} [_ path identicon]] + {:db (assoc-in db path identicon)})) + +(handlers/register-handler-fx + :gfycat-generated + (fn [{:keys [db]} [_ path gfycat]] + {:db (assoc-in db path gfycat)})) \ No newline at end of file diff --git a/src/status_im/multiaccounts/create/core.cljs b/src/status_im/multiaccounts/create/core.cljs index ec4716e950..1c8fa9b0d3 100644 --- a/src/status_im/multiaccounts/create/core.cljs +++ b/src/status_im/multiaccounts/create/core.cljs @@ -54,8 +54,18 @@ (let [{:keys [selected-id address key-code]} (:intro-wizard db) {:keys [address]} (get-selected-multiaccount cofx) hashed-password (ethereum/sha3 (security/safe-unmask-data key-code)) - callback #(re-frame/dispatch [::store-multiaccount-success key-code %])] - (log/debug "create-multiaccount") + callback (fn [result] + (let [derived-data (types/json->clj result) + publicKey (get-in derived-data [constants/path-whisper-keyword :publicKey])] + (status/gfycat-identicon-async + publicKey + (fn [name photo-path] + (let [derived-whisper (derived-data constants/path-whisper-keyword) + derived-data-extended (assoc-in derived-data + [constants/path-whisper-keyword] + (merge derived-whisper {:name name :photo-path photo-path}))] + (re-frame/dispatch [::store-multiaccount-success + key-code derived-data-extended]))))))] {::store-multiaccount [selected-id address hashed-password callback]})) (fx/defn prepare-intro-wizard @@ -188,10 +198,12 @@ :wallet true :path constants/path-default-wallet :name "Status account"}) - (let [{:keys [publicKey address]} + (let [{:keys [publicKey address name photo-path]} (get-in multiaccount [:derived constants/path-whisper-keyword])] {:publicKey publicKey :address address + :name name + :photo-path photo-path :path constants/path-whisper :chat true})]) @@ -216,9 +228,7 @@ :as multiaccount} password {:keys [seed-backed-up? login?] :or {login? true}}] - (let [[wallet-account {:keys [publicKey]} :as accounts-data] (prepare-accounts-data multiaccount) - name (gfycat/generate-gfy publicKey) - photo-path (identicon/identicon publicKey) + (let [[wallet-account {:keys [publicKey photo-path name]} :as accounts-data] (prepare-accounts-data multiaccount) multiaccount-data {:name name :address address :photo-path photo-path @@ -349,7 +359,7 @@ (assoc (get-selected-multiaccount cofx) :derived - (types/json->clj derived)) + derived) password {:seed-backed-up? false})) diff --git a/src/status_im/multiaccounts/recover/core.cljs b/src/status_im/multiaccounts/recover/core.cljs index 2e03bf45ff..abf7af117f 100644 --- a/src/status_im/multiaccounts/recover/core.cljs +++ b/src/status_im/multiaccounts/recover/core.cljs @@ -107,9 +107,15 @@ constants/path-whisper constants/path-default-wallet] (fn [result] - (let [derived-data (types/json->clj result)] - (re-frame/dispatch [::import-multiaccount-success - root-data derived-data]))))))))) + (let [derived-data (types/json->clj result) + public-key (get-in derived-data [constants/path-whisper-keyword :publicKey])] + (status/gfycat-identicon-async + public-key + (fn [name photo-path] + (let [derived-whisper (derived-data constants/path-whisper-keyword) + derived-data-extended (assoc-in derived-data [constants/path-whisper-keyword] (merge derived-whisper {:name name :photo-path photo-path}))] + (re-frame/dispatch [::import-multiaccount-success + root-data derived-data-extended])))))))))))) (fx/defn show-existing-multiaccount-alert [_ address] diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 169ef6824b..7f1b212b25 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -313,8 +313,24 @@ (log/debug "[native-module] generate-gfycat") (.generateAlias (status) public-key)) +(defn generate-gfycat-async + "Generate a 3 words random name based on the user public-key, asynchronously" + [public-key callback] + {:pre [(utils.db/valid-public-key? public-key)]} + (.generateAliasAsync (status) public-key callback)) + (defn identicon "Generate a icon based on a string, synchronously" [seed] (log/debug "[native-module] identicon") (.identicon (status) seed)) + +(defn identicon-async + "Generate a icon based on a string, asynchronously" + [seed callback] + (.identiconAsync (status) seed callback)) + +(defn gfycat-identicon-async + "Generate an icon based on a string and 3 words random name asynchronously" + [seed callback] + (.generateAliasAndIdenticonAsync (status) seed callback)) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index 9d332152cc..c80850db52 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -259,6 +259,8 @@ :<- [:intro-wizard] (fn [wizard-state] {:pubkey (get-in wizard-state [:derived constants/path-whisper-keyword :publicKey]) + :name (get-in wizard-state [:derived constants/path-whisper-keyword :name]) + :photo-path (get-in wizard-state [:derived constants/path-whisper-keyword :photo-path]) :processing? (:processing? wizard-state)})) (re-frame/reg-sub diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs index e57065d255..1aa18fda02 100644 --- a/src/status_im/ui/screens/chat/message/message.cljs +++ b/src/status_im/ui/screens/chat/message/message.cljs @@ -242,6 +242,7 @@ last-in-group? first-in-group? display-photo? + identicon display-username? from outgoing @@ -254,7 +255,7 @@ (when first-in-group? [react/touchable-highlight {:on-press #(when-not modal? (re-frame/dispatch [:chat.ui/show-profile from]))} [react/view - [photos/member-photo from]]])]) + [photos/member-photo from identicon]]])]) [react/view (style/group-message-view outgoing display-photo?) (when display-username? [react/touchable-opacity {:on-press #(re-frame/dispatch [:chat.ui/show-profile from])} diff --git a/src/status_im/ui/screens/chat/photos.cljs b/src/status_im/ui/screens/chat/photos.cljs index 5cf912f8dd..5ccd86022c 100644 --- a/src/status_im/ui/screens/chat/photos.cljs +++ b/src/status_im/ui/screens/chat/photos.cljs @@ -19,8 +19,8 @@ (when identicon? [react/view {:style (style/photo-border size)}])])) -(defview member-photo [from & [size]] +(defview member-photo [from & [identicon size]] (letsubs [photo-path [:chats/photo-path from]] - (photo photo-path + (photo (or photo-path identicon) {:accessibility-label :member-photo :size (or size style/default-size)}))) diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index 49f433ca79..811524dd9d 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -348,7 +348,7 @@ (let [next-count (min all-messages-count (+ @messages-to-load load-step))] (reset! messages-to-load next-count))) -(defview messages-view-desktop [{:keys [chat-id group-chat]} +(defview messages-view-desktop [{:keys [chat-id group-chat pending-invite-inviter-name]} modal?] (letsubs [messages [:chats/current-chat-messages-stream] current-public-key [:multiaccount/public-key] @@ -391,7 +391,9 @@ :current-public-key current-public-key :row message-obj :idx #(or (:message-id message-obj) (:value message-obj)) - :list-ref messages-list-ref}]))]]]))) + :list-ref messages-list-ref}]))]] + (if pending-invite-inviter-name + [group-chat-footer chat-id])]))) (defview chat-root [modal?] (letsubs [{:keys [public? chat-id chat-name show-input? group-chat contact] :as current-chat} diff --git a/src/status_im/ui/screens/intro/views.cljs b/src/status_im/ui/screens/intro/views.cljs index 2559cd4758..cad1932504 100644 --- a/src/status_im/ui/screens/intro/views.cljs +++ b/src/status_im/ui/screens/intro/views.cljs @@ -414,7 +414,7 @@ :margin-top 8}} (i18n/label :t/processing)]])]) -(defn recovery-success [pubkey] +(defn recovery-success [pubkey name photo-path] [react/view {:flex 1 :justify-content :space-between :background-color colors/white} @@ -430,7 +430,7 @@ [react/view {:justify-content :center :align-items :center :margin-bottom 11} - [react/image {:source {:uri (identicon/identicon pubkey)} + [react/image {:source {:uri photo-path} :style {:width 61 :height 61 :border-radius 30 @@ -441,7 +441,7 @@ :font-weight "500"} :number-of-lines 1 :ellipsize-mode :middle} - (gfy/generate-gfy pubkey)] + name] [react/text {:style {:text-align :center :margin-top 4 :color colors/gray @@ -558,7 +558,7 @@ wizard-state)]]])) (defview wizard-recovery-success [] - (letsubs [{:keys [pubkey processing?]} [:intro-wizard/recovery-success] + (letsubs [{:keys [pubkey processing? name photo-path]} [:intro-wizard/recovery-success] existing-account? [:intro-wizard/recover-existing-account?]] [react/view {:style {:flex 1}} [toolbar/toolbar @@ -570,7 +570,7 @@ [react/view {:style {:flex 1 :justify-content :space-between}} [top-bar {:step :recovery-success}] - [recovery-success pubkey] + [recovery-success pubkey name photo-path] [bottom-bar {:step :recovery-success :forward-action :multiaccounts.recover/re-encrypt-pressed :processing? processing? diff --git a/src/status_im/utils/gfycat/core.cljs b/src/status_im/utils/gfycat/core.cljs index 06bdf7760b..138b0c1c4a 100644 --- a/src/status_im/utils/gfycat/core.cljs +++ b/src/status_im/utils/gfycat/core.cljs @@ -1,5 +1,6 @@ (ns status-im.utils.gfycat.core (:require [status-im.native-module.core :as native-module] + [re-frame.core :as re-frame] [status-im.utils.datetime :as datetime])) (def unknown-gfy "Unknown") @@ -12,3 +13,11 @@ (native-module/generate-gfycat public-key))) (def generate-gfy (memoize build-gfy)) + +(re-frame/reg-fx + :insert-gfycats + (fn [key-path-seq] + (for [key-path key-path-seq] + (let [public-key (first key-path) + path-for-gfycat (second key-path)] + (native-module/generate-gfycat-async public-key #(re-frame/dispatch [:gfycat-generated path-for-gfycat %])))))) diff --git a/src/status_im/utils/identicon.cljs b/src/status_im/utils/identicon.cljs index e28f5274f9..91e61171c4 100644 --- a/src/status_im/utils/identicon.cljs +++ b/src/status_im/utils/identicon.cljs @@ -1,5 +1,16 @@ (ns status-im.utils.identicon (:require + [re-frame.core :as re-frame] [status-im.native-module.core :as native-module])) (def identicon (memoize native-module/identicon)) + +(def identicon-async native-module/identicon-async) + +(re-frame/reg-fx + :insert-identicons + (fn [key-path-seq] + (for [key-path key-path-seq] + (let [public-key (first key-path) + path-for-identicon (second key-path)] + (identicon-async public-key #(re-frame/dispatch [:identicon-generated path-for-identicon %]))))))