diff --git a/src/schema/common.cljs b/src/schema/common.cljs index ef6ae00fcb..c3e9dc45bd 100644 --- a/src/schema/common.cljs +++ b/src/schema/common.cljs @@ -8,7 +8,23 @@ (def ^:private ?customization-color [:or :string :keyword]) +(def ^:private ?public-key + [:re #"^0x04[0-9a-f]{128}$"]) + +(def ^:private ?rpc-call + [:sequential + {:min 1} + [:map + {:closed true} + [:method :string] + [:params [:sequential :any]] + [:js-response {:optional true} :any] + [:on-success [:or fn? [:cat keyword? [:* :any]]]] + [:on-error [:or fn? [:cat keyword? [:* :any]]]]]]) + (defn register-schemas [] (registry/register ::theme ?theme) - (registry/register ::customization-color ?customization-color)) + (registry/register ::customization-color ?customization-color) + (registry/register ::public-key ?public-key) + (registry/register ::rpc-call ?rpc-call)) diff --git a/src/schema/re_frame.cljs b/src/schema/re_frame.cljs new file mode 100644 index 0000000000..cbe2a95456 --- /dev/null +++ b/src/schema/re_frame.cljs @@ -0,0 +1,10 @@ +(ns schema.re-frame + (:require + [schema.registry :as registry])) + +(def ^:private ?cofx + [:map]) + +(defn register-schemas + [] + (registry/register ::cofx ?cofx)) diff --git a/src/status_im/profile/core.cljs b/src/status_im/profile/core.cljs index 4b52663d04..3d5adc21ea 100644 --- a/src/status_im/profile/core.cljs +++ b/src/status_im/profile/core.cljs @@ -3,10 +3,8 @@ [clojure.string :as string] [re-frame.core :as re-frame] [status-im.multiaccounts.update.core :as multiaccounts.update] - [status-im.ui.components.list-selection :as list-selection] [status-im.ui.components.react :as react] - [utils.re-frame :as rf] - [utils.universal-links :as universal-links])) + [utils.re-frame :as rf])) (re-frame/reg-fx :copy-to-clipboard @@ -37,12 +35,6 @@ 100)] (swap! tooltips assoc tooltip-id {:opacity 1.0 :interval-id interval-id :cnt 0})))))) -(re-frame/reg-fx - :profile/share-profile-link - (fn [contact-code] - (let [link (universal-links/generate-link :user :external contact-code)] - (list-selection/open-share {:message link})))) - (rf/defn finish-success {:events [:my-profile/finish-success]} [{:keys [db] :as cofx}] @@ -81,11 +73,6 @@ [_ tooltip-id] {:show-tooltip tooltip-id}) -(rf/defn share-profile-link - {:events [:profile/share-profile-link]} - [_ value] - {:profile/share-profile-link value}) - (rf/defn show-profile {:events [:chat.ui/show-profile]} [{:keys [db]} identity ens-name] diff --git a/src/status_im/ui/components/invite/events.cljs b/src/status_im/ui/components/invite/events.cljs index 5e911dda2c..7fe1bd723e 100644 --- a/src/status_im/ui/components/invite/events.cljs +++ b/src/status_im/ui/components/invite/events.cljs @@ -3,21 +3,16 @@ [re-frame.core :as re-frame] [status-im.ui.components.react :as react] [utils.i18n :as i18n] - [utils.re-frame :as rf] - [utils.universal-links :as universal-links])) + [utils.re-frame :as rf])) (re-frame/reg-fx ::share (fn [content] (.share ^js react/sharing (clj->js content)))) -(rf/defn share-link - {:events [:invite.events/share-link]} - [{:keys [db]}] - (let [{:keys [public-key preferred-name]} (get db :profile/profile) - profile-link (universal-links/generate-link :user - :external - (or preferred-name - public-key)) - message (i18n/label :t/join-me {:url profile-link})] - {::share {:message message}})) +(rf/reg-event-fx + :invite.events/share-link + (fn [{:keys [db]}] + (let [{:keys [universal-profile-url]} (get db :profile/profile) + message (i18n/label :t/join-me {:url universal-profile-url})] + {::share {:message message}}))) diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index 45ee408cfe..8ec216c9c2 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -21,55 +21,54 @@ [status-im2.config :as config] [status-im2.contexts.profile.utils :as profile.utils] [utils.ens.stateofus :as stateofus] - [utils.i18n :as i18n] - [utils.universal-links :as universal-links]) + [utils.i18n :as i18n]) (:require-macros [status-im.utils.views :as views])) (views/defview share-chat-key [] - (views/letsubs [{:keys [address ens-name]} [:popover/popover] - width (reagent/atom nil)] - (let [link (universal-links/generate-link :user :external (or ens-name address))] - [react/view {:on-layout #(reset! width (-> ^js % .-nativeEvent .-layout .-width))} - [react/view {:style {:padding-top 16 :padding-horizontal 16}} - (when @width - [qr-codes/qr-code - {:url address - :size (- @width 32)}]) - (when ens-name - [react/view - [copyable-text/copyable-text-view - {:label :t/ens-username - :container-style {:margin-top 12 :margin-bottom 4} - :copied-text ens-name} - [quo/text - {:monospace true - :accessibility-label :ens-username} - ens-name]] - [react/view - {:height 1 - :margin-top 12 - :margin-horizontal -16 - :background-color colors/gray-lighter}]]) - [copyable-text/copyable-text-view - {:label :t/chat-key - :container-style {:margin-top 12 :margin-bottom 4} - :copied-text address} - [quo/text - {:number-of-lines 1 - :ellipsize-mode :middle - :accessibility-label :chat-key - :monospace true} - address]]] - [react/view styles/share-link-button - [quo/button - {:on-press (fn [] - (re-frame/dispatch [:hide-popover]) - (js/setTimeout - #(list-selection/open-share {:message link}) - 250)) - :accessibility-label :share-my-contact-code-button} - (i18n/label :t/share-link)]]]))) + (views/letsubs [{:keys [address ens-name]} [:popover/popover] + {:keys [universal-profile-url]} [:profile/profile] + width (reagent/atom nil)] + [react/view {:on-layout #(reset! width (-> ^js % .-nativeEvent .-layout .-width))} + [react/view {:style {:padding-top 16 :padding-horizontal 16}} + (when @width + [qr-codes/qr-code + {:url address + :size (- @width 32)}]) + (when ens-name + [react/view + [copyable-text/copyable-text-view + {:label :t/ens-username + :container-style {:margin-top 12 :margin-bottom 4} + :copied-text ens-name} + [quo/text + {:monospace true + :accessibility-label :ens-username} + ens-name]] + [react/view + {:height 1 + :margin-top 12 + :margin-horizontal -16 + :background-color colors/gray-lighter}]]) + [copyable-text/copyable-text-view + {:label :t/chat-key + :container-style {:margin-top 12 :margin-bottom 4} + :copied-text address} + [quo/text + {:number-of-lines 1 + :ellipsize-mode :middle + :accessibility-label :chat-key + :monospace true} + address]]] + [react/view styles/share-link-button + [quo/button + {:on-press (fn [] + (re-frame/dispatch [:hide-popover]) + (js/setTimeout + #(list-selection/open-share {:message universal-profile-url}) + 250)) + :accessibility-label :share-my-contact-code-button} + (i18n/label :t/share-link)]]])) (defn content [] diff --git a/src/status_im2/common/universal_links.cljs b/src/status_im2/common/universal_links.cljs index 6e98509e43..821c6b5f1a 100644 --- a/src/status_im2/common/universal_links.cljs +++ b/src/status_im2/common/universal_links.cljs @@ -5,6 +5,7 @@ [re-frame.core :as re-frame] [react-native.async-storage :as async-storage] [react-native.core :as rn] + [schema.core :as schema] [status-im2.navigation.events :as navigation] [taoensso.timbre :as log] [utils.ethereum.chain :as chain] @@ -16,14 +17,6 @@ {:external "https://status.app" :internal "status-app:/"}) -(def links - {:private-chat "%s/p/%s" - :community-requests "%s/cr/%s" - :community "%s/c#%s" - :group-chat "%s/g/%s" - :user "%s/u#%s" - :browse "%s/b/%s"}) - (rf/defn handle-browse [_ {:keys [url]}] (log/info "universal-links: handling browse" url) @@ -183,6 +176,61 @@ {:db (dissoc db :universal-links/url)} (handle-url url)))) +(defn generate-profile-url + ([cofx] (generate-profile-url cofx nil)) + ([{:keys [db]} [{:keys [public-key cb]}]] + (let [profile-public-key (get-in db [:profile/profile :public-key]) + profile? (or (not public-key) (= public-key profile-public-key)) + ens-name? (if profile? + (get-in db [:profile/profile :ens-name?]) + (get-in db [:contacts/contacts public-key :ens-name])) + public-key (if profile? profile-public-key public-key)] + (when public-key + {:json-rpc/call + [{:method (if ens-name? "wakuext_shareUserURLWithENS" "wakuext_shareUserURLWithData") + :params [public-key] + :on-success (fn [url] + (rf/dispatch [:universal-links/save-profile-url public-key url]) + (when (fn? cb) (cb))) + :on-error #(log/error "failed to wakuext_shareUserURLWithData" + {:error % + :public-key public-key})}]})))) + +(schema/=> generate-profile-url + [:=> + [:catn + [:cofx :schema.re-frame/cofx] + [:args + [:schema + [:? + [:map + [:public-key {:optional true} :schema.common/public-key] + [:cb {:optional true} fn?]]]]]] + [:map + [:json-rpc/call :schema.common/rpc-call]]]) + +(rf/reg-event-fx :universal-links/generate-profile-url generate-profile-url) + +(defn save-profile-url + [{:keys [db]} [public-key url]] + (when url + {:db + (cond-> db + (get-in db [:contacts/contacts public-key]) + (assoc-in [:contacts/contacts public-key :universal-profile-url] url) + (= public-key (get-in db [:profile/profile :public-key])) + (assoc-in [:profile/profile :universal-profile-url] url))})) + +(schema/=> save-profile-url + [:=> + [:catn + [:cofx :schema.re-frame/cofx] + [:args + [:schema [:cat :schema.common/public-key :string]]]] + [:maybe :map]]) + +(rf/reg-event-fx :universal-links/save-profile-url save-profile-url) + (defn unwrap-js-url [e] (-> e diff --git a/src/status_im2/common/universal_links_test.cljs b/src/status_im2/common/universal_links_test.cljs index 7a079a05ee..d784440527 100644 --- a/src/status_im2/common/universal_links_test.cljs +++ b/src/status_im2/common/universal_links_test.cljs @@ -1,6 +1,6 @@ (ns status-im2.common.universal-links-test (:require - [cljs.test :refer-macros [deftest is testing]] + [cljs.test :refer-macros [deftest is are testing]] [re-frame.core :as re-frame] [status-im2.common.universal-links :as links])) @@ -34,3 +34,67 @@ (with-redefs [re-frame/dispatch #(reset! actual %)] (links/url-event-listener #js {}) (is (= nil @actual))))))) + +(deftest generate-profile-url + (testing "user has ens name" + (testing "it calls the ens rpc method with ens name as param" + (let [pubkey "pubkey" + db {:profile/profile {:ens-name? true :public-key pubkey}} + rst (links/generate-profile-url {:db db})] + (are [result expected] (= result expected) + "wakuext_shareUserURLWithENS" (-> rst :json-rpc/call first :method) + pubkey (-> rst :json-rpc/call first :params first))))) + (testing "user has no ens name" + (testing "it calls the ens rpc method with public keyas param" + (let [pubkey "pubkey" + db {:profile/profile {:public-key pubkey}} + rst (links/generate-profile-url {:db db})] + (are [result expected] (= result expected) + "wakuext_shareUserURLWithData" (-> rst :json-rpc/call first :method) + pubkey (-> rst :json-rpc/call first :params first))))) + (testing "contact has ens name" + (testing "it calls the ens rpc method with ens name as param" + (let [pubkey "pubkey" + ens "ensname.eth" + db {:contacts/contacts {pubkey {:ens-name ens}}} + rst (links/generate-profile-url {:db db} [{:public-key pubkey}])] + (are [result expected] (= result expected) + "wakuext_shareUserURLWithENS" (-> rst :json-rpc/call first :method) + pubkey (-> rst :json-rpc/call first :params first))))) + (testing "contact has no ens name" + (testing "it calls the ens rpc method with public keyas param" + (let [pubkey "pubkey" + db {:contacts/contacts {pubkey {:public-key pubkey}}} + rst (links/generate-profile-url {:db db} [{:public-key pubkey}])] + (are [result expected] (= result expected) + "wakuext_shareUserURLWithData" (-> rst :json-rpc/call first :method) + pubkey (-> rst :json-rpc/call first :params first)))))) + +(deftest save-profile-url + (testing "given a contact public key and profile url" + (testing "it updates the contact in db" + (let [pubkey "pubkey" + url "url" + db {:contacts/contacts {pubkey {:public-key pubkey}}} + rst (links/save-profile-url {:db db} [pubkey url])] + (is (= (get-in rst [:db :contacts/contacts pubkey :universal-profile-url]) url))))) + (testing "given a user public key and profile url" + (testing "it updates the user profile in db" + (let [pubkey "pubkey" + url "url" + db {:profile/profile {:public-key pubkey}} + rst (links/save-profile-url {:db db} [pubkey url])] + (is (= (get-in rst [:db :profile/profile :universal-profile-url]) url))))) + (testing "given a invalid url" + (testing "it returns the db untouched" + (let [pubkey "pubkey" + url "url" + db {:profile/profile {:public-key pubkey}} + rst (links/save-profile-url {:db db} ["invalid pubkey" url])] + (is (= (:db rst) db))))) + (testing "given a nil as url" + (testing "it returns nil" + (let [pubkey "pubkey" + db {:profile/profile {:public-key pubkey}} + rst (links/save-profile-url {:db db} ["invalid pubkey"])] + (is (nil? rst)))))) diff --git a/src/status_im2/contexts/profile/login/events.cljs b/src/status_im2/contexts/profile/login/events.cljs index ff58b75ebd..46b106e549 100644 --- a/src/status_im2/contexts/profile/login/events.cljs +++ b/src/status_im2/contexts/profile/login/events.cljs @@ -25,7 +25,6 @@ [status-im2.contexts.profile.settings.events :as profile.settings.events] [status-im2.contexts.push-notifications.events :as notifications] [status-im2.contexts.shell.activity-center.events :as activity-center] - [status-im2.contexts.wallet.events :as wallet] [status-im2.navigation.events :as navigation] [taoensso.timbre :as log] [utils.re-frame :as rf] @@ -85,7 +84,9 @@ :networks/current-network current-network :networks/networks (merge networks config/default-networks-by-id) :profile/profile (merge profile-overview settings)) - (assoc-in [:wallet :ui :tokens-loading?] true))} + (assoc-in [:wallet :ui :tokens-loading?] true)) + :fx [[:dispatch [:wallet/get-ethereum-chains]] + [:dispatch [:universal-links/generate-profile-url]]]} (notifications/load-preferences) (data-store.chats/fetch-chats-preview {:on-success @@ -100,7 +101,6 @@ (activity-center/notifications-fetch-pending-contact-requests) (activity-center/update-seen-state) (activity-center/notifications-fetch-unread-count) - (wallet/get-ethereum-chains) (redirect-to-root)))) ;; login phase 2, we want to load and show chats faster so we split login into 2 phases diff --git a/src/status_im2/contexts/quo_preview/share/share_qr_code.cljs b/src/status_im2/contexts/quo_preview/share/share_qr_code.cljs index 886d02cc0d..d107c47c05 100644 --- a/src/status_im2/contexts/quo_preview/share/share_qr_code.cljs +++ b/src/status_im2/contexts/quo_preview/share/share_qr_code.cljs @@ -68,7 +68,7 @@ (str (name network) ":"))) (def ^:private profile-link - "https://status.app/u#zQ34e1zlOdas0pKnvrweeedsasas12adjie8") + "https://status.app/u/CwWACgkKB0VlZWVlZWUD#zQ3shUeRSwU6rnUk5JfK2k5HRiM5Hy3wU3UZQrKVzopmAHcQv") (def ^:private wallet-address "0x39cf6E0Ba4C4530735616e1Ee7ff5FbCB726fBd2") diff --git a/src/status_im2/contexts/shell/share/view.cljs b/src/status_im2/contexts/shell/share/view.cljs index 0c71a9650d..ed9e1798f2 100644 --- a/src/status_im2/contexts/shell/share/view.cljs +++ b/src/status_im2/contexts/shell/share/view.cljs @@ -14,7 +14,6 @@ [status-im2.contexts.shell.share.style :as style] [utils.address :as address] [utils.i18n :as i18n] - [utils.image-server :as image-server] [utils.re-frame :as rf])) (defn header @@ -47,27 +46,25 @@ (defn profile-tab [] (let [{:keys [emoji-hash - compressed-key - customization-color] + customization-color + universal-profile-url] :as profile} (rf/sub [:profile/profile]) - profile-url (str image-server/status-profile-base-url compressed-key) abbreviated-url (address/get-abbreviated-profile-url - image-server/status-profile-base-url-without-https - compressed-key) + universal-profile-url) emoji-hash-string (string/join emoji-hash)] [:<> [rn/view {:style style/qr-code-container} [qr-codes/share-qr-code {:type :profile :unblur-on-android? true - :qr-data profile-url + :qr-data universal-profile-url :qr-data-label-shown abbreviated-url - :on-share-press #(list-selection/open-share {:message profile-url}) + :on-share-press #(list-selection/open-share {:message universal-profile-url}) :on-text-press #(rf/dispatch [:share/copy-text-and-show-toast - {:text-to-copy profile-url + {:text-to-copy universal-profile-url :post-copy-message (i18n/label :t/link-to-profile-copied)}]) :on-text-long-press #(rf/dispatch [:share/copy-text-and-show-toast - {:text-to-copy profile-url + {:text-to-copy universal-profile-url :post-copy-message (i18n/label :t/link-to-profile-copied)}]) :profile-picture (:uri (profile.utils/photo profile)) :full-name (profile.utils/displayed-name profile) diff --git a/src/status_im2/contexts/wallet/events.cljs b/src/status_im2/contexts/wallet/events.cljs index f0480715f4..19086d584d 100644 --- a/src/status_im2/contexts/wallet/events.cljs +++ b/src/status_im2/contexts/wallet/events.cljs @@ -190,14 +190,14 @@ (first derived-address-details)]))] {:fx [[:dispatch [:wallet/create-derived-addresses account-details on-success]]]}))) -(rf/defn get-ethereum-chains - {:events [:wallet/get-ethereum-chains]} - [{:keys [db]}] - {:fx [[:json-rpc/call - [{:method "wallet_getEthereumChains" - :params [] - :on-success [:wallet/get-ethereum-chains-success] - :on-error #(log/info "failed to get networks " %)}]]]}) +(rf/reg-event-fx + :wallet/get-ethereum-chains + (fn [_] + {:json-rpc/call + [{:method "wallet_getEthereumChains" + :params [] + :on-success [:wallet/get-ethereum-chains-success] + :on-error #(log/info "failed to get networks " %)}]})) (rf/reg-event-fx :wallet/get-ethereum-chains-success diff --git a/src/status_im2/setup/schema.cljs b/src/status_im2/setup/schema.cljs index 7a5ff28955..92352cc1a9 100644 --- a/src/status_im2/setup/schema.cljs +++ b/src/status_im2/setup/schema.cljs @@ -9,6 +9,7 @@ malli.util schema.common [schema.core :as schema] + schema.re-frame schema.registry [taoensso.timbre :as log])) @@ -76,7 +77,8 @@ `:schema.common/theme`." [] (schema.registry/merge (malli.util/schemas)) - (schema.common/register-schemas)) + (schema.common/register-schemas) + (schema.re-frame/register-schemas)) (defn setup! "Configure Malli and initializes instrumentation. diff --git a/src/utils/address.cljs b/src/utils/address.cljs index 4ba40b2b96..6b51eb5b1e 100644 --- a/src/utils/address.cljs +++ b/src/utils/address.cljs @@ -42,22 +42,22 @@ (get-shortened-key (eip55/address->checksum (normalized-hex address))))) (defn get-abbreviated-profile-url - "The goal here is to generate a string that begins with - status.app/u# joined with the 1st 5 characters - of the compressed public key followed by an ellipsis followed by - the last 10 characters of the compressed public key" - [base-url public-key] - (if (and public-key base-url (> (count public-key) 17) (= "status.app/u#" base-url)) - (let [first-part-of-public-pk (subs public-key 0 5) - ellipsis "..." - public-key-size (count public-key) - last-part-of-public-key (subs public-key (- public-key-size 10) public-key-size) - abbreviated-url (str base-url - first-part-of-public-pk - ellipsis - last-part-of-public-key)] - abbreviated-url) - nil)) + "The goal here is to generate a string that begins with status.app/u/ joined + with the 1st 5 characters of the encoded data followed by an ellipsis + followed by the last 10 characters of the compressed public key" + [universal-profile-url] + (when-let [re-find-result (re-find #"^https://(status.app/u/)(.*)#(.*)$" universal-profile-url)] + (let [[_whole-url base-url encoded-data public-key] re-find-result] + (when (> (count public-key) 9) + (let [first-part-of-encoded-data (subs encoded-data 0 5) + ellipsis "..." + public-key-size (count public-key) + last-part-of-public-key (subs public-key (- public-key-size 10) public-key-size) + abbreviated-url (str base-url + first-part-of-encoded-data + ellipsis + last-part-of-public-key)] + abbreviated-url))))) (defn get-shortened-compressed-key "The goal here is to generate a string that begins with 1st 3 diff --git a/src/utils/address_test.cljs b/src/utils/address_test.cljs index cc5676df08..225143a12d 100644 --- a/src/utils/address_test.cljs +++ b/src/utils/address_test.cljs @@ -22,22 +22,17 @@ (deftest test-get-abbreviated-profile-url (testing "Ensure the function correctly generates an abbreviated profile URL for a valid public key" - (is (= "status.app/u#zQ3sh...mrdYpzeFUa" + (is (= "status.app/u/abcde...mrdYpzeFUa" (utils.address/get-abbreviated-profile-url - "status.app/u#" - "zQ3shPrnUhhR42JJn3QdhodGest8w8MjiH8hPaimrdYpzeFUa")))) + "https://status.app/u/abcdefg#zQ3shPrnUhhR42JJn3QdhodGest8w8MjiH8hPaimrdYpzeFUa")))) (testing "Ensure the function returns nil when given an empty public key" - (is (nil? (utils.address/get-abbreviated-profile-url "status.app/u#" "")))) - - (testing "Ensure the function returns nil when given a nil public key" - (is (nil? (utils.address/get-abbreviated-profile-url "status.app/u#" nil)))) + (is (nil? (utils.address/get-abbreviated-profile-url "status.app/u/abcdefg"))) + (is (nil? (utils.address/get-abbreviated-profile-url "status.app/u/abcdefg#")))) (testing "Ensure the function returns nil when given an incorrect base URL" (is (nil? (utils.address/get-abbreviated-profile-url - "status.app/uwu#" - "zQ3shPrnUhhR42JJn3QdhodGest8w8MjiH8hPaimrdYpzeFUa")))) + "https://status.app/uwu/abcdefg#zQ3shPrnUhhR42JJn3QdhodGest8w8MjiH8hPaimrdYpzeFUa")))) - (testing "Ensure the function returns nil when given a public key shorter than 17 characters" - (is (nil? (utils.address/get-abbreviated-profile-url "status.app/u#" "abc"))) - (is (nil? (utils.address/get-abbreviated-profile-url "status.app/u#" "1234"))))) + (testing "Ensure the function returns nil when given a public key shorter than 10 characters" + (is (nil? (utils.address/get-abbreviated-profile-url "https://status.app/u/abcdefg#012345678"))))) diff --git a/src/utils/image_server.cljs b/src/utils/image_server.cljs index b4fafeb515..2fe848705a 100644 --- a/src/utils/image_server.cljs +++ b/src/utils/image_server.cljs @@ -11,8 +11,6 @@ (def ^:const account-initials-action "/accountInitials") (def ^:const contact-images-action "/contactImages") (def ^:const generate-qr-action "/GenerateQRCode") -(def ^:const status-profile-base-url "https://status.app/u#") -(def ^:const status-profile-base-url-without-https "status.app/u#") (defn get-font-file-ready "setup font file and get the absolute path to it @@ -264,30 +262,6 @@ :ring? (if (nil? override-ring?) ring? override-ring?) :ring-width ring-width}))) -(defn get-account-qr-image-uri - [{:keys [key-uid public-key port qr-size]}] - (let [profile-qr-url (str status-profile-base-url public-key) - base-64-qr-url (js/btoa profile-qr-url) - profile-image-type "large" - error-correction-level (correction-level->index :highest) - superimpose-profile? true - media-server-url (str image-server-uri-prefix - port - generate-qr-action - "?level=" - error-correction-level - "&url=" - base-64-qr-url - "&keyUid=" - key-uid - "&allowProfileImage=" - superimpose-profile? - "&size=" - (* 2 qr-size) - "&imageName=" - profile-image-type)] - media-server-url)) - (defn get-qr-image-uri-for-any-url [{:keys [url port qr-size error-level]}] (let [qr-url-base64 (js/btoa url)