fix_: ui part for share-all-future-addresses storage (PR #20549) (#20549)

Related status-go PR: https://github.com/status-im/status-go/pull/5354

6e056348...8458cafe

Signed-off-by: Vitaly Vlasov <mail@vitv.ly>
This commit is contained in:
Vit∀ly Vlasov 2024-07-18 15:36:38 +03:00 committed by GitHub
parent 97b19c5440
commit 2b0847ef76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 126 additions and 109 deletions

View File

@ -27,7 +27,7 @@
:on-error #(p-reject (str "failed to sign data\n" %))})))) :on-error #(p-reject (str "failed to sign data\n" %))}))))
(defn- edit-shared-addresses-for-community (defn- edit-shared-addresses-for-community
[community-id signatures addresses-to-reveal airdrop-address] [community-id signatures addresses-to-reveal airdrop-address _share-future-addresses?]
(promesa/create (promesa/create
(fn [p-resolve p-reject] (fn [p-resolve p-reject]
(rpc/call (rpc/call
@ -41,15 +41,16 @@
:on-error p-reject})))) :on-error p-reject}))))
(defn- request-to-join (defn- request-to-join
[community-id signatures addresses-to-reveal airdrop-address] [community-id signatures addresses-to-reveal airdrop-address share-future-addresses?]
(promesa/create (promesa/create
(fn [p-resolve p-reject] (fn [p-resolve p-reject]
(rpc/call (rpc/call
{:method :wakuext_requestToJoinCommunity {:method :wakuext_requestToJoinCommunity
:params [{:communityId community-id :params [{:communityId community-id
:signatures signatures :signatures signatures
:addressesToReveal addresses-to-reveal :addressesToReveal addresses-to-reveal
:airdropAddress airdrop-address}] :airdropAddress airdrop-address
:shareFutureAddresses share-future-addresses?}]
:js-response true :js-response true
:on-success p-resolve :on-success p-resolve
:on-error p-reject})))) :on-error p-reject}))))
@ -64,15 +65,18 @@
(defn- sign-and-call-endpoint (defn- sign-and-call-endpoint
[{:keys [community-id password pub-key [{:keys [community-id password pub-key
addresses-to-reveal airdrop-address addresses-to-reveal airdrop-address share-future-addresses?
on-success on-error on-success on-error
callback]}] callback]}]
(-> (promesa/let [sign-params (generate-requests-for-signing pub-key community-id addresses-to-reveal) (-> (promesa/let [sign-params (generate-requests-for-signing pub-key
community-id
addresses-to-reveal)
signatures (sign-data sign-params password) signatures (sign-data sign-params password)
result (callback community-id result (callback community-id
signatures signatures
addresses-to-reveal addresses-to-reveal
airdrop-address)] airdrop-address
share-future-addresses?)]
(run-callback-or-event on-success result)) (run-callback-or-event on-success result))
(promesa/catch #(run-callback-or-event on-error %)))) (promesa/catch #(run-callback-or-event on-error %))))
@ -87,6 +91,7 @@
[:or [:set string?] [:or [:set string?]
[:sequential string?]]] [:sequential string?]]]
[:airdrop-address string?] [:airdrop-address string?]
[:share-future-addresses? boolean?]
[:on-success [:or fn? :schema.re-frame/event]] [:on-success [:or fn? :schema.re-frame/event]]
[:on-error [:or fn? :schema.re-frame/event]] [:on-error [:or fn? :schema.re-frame/event]]
[:callback fn?]]] [:callback fn?]]]

View File

@ -17,13 +17,12 @@
(rf/reg-event-fx :communities/initialize-permission-addresses initialize-permission-addresses) (rf/reg-event-fx :communities/initialize-permission-addresses initialize-permission-addresses)
(defn do-init-permission-addresses (defn do-init-permission-addresses
[{:keys [db]} [community-id revealed-accounts]] [{:keys [db]} [community-id revealed-accounts share-future-addresses?]]
(let [wallet-accounts (utils/sorted-operable-non-watch-only-accounts db) (let [wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
addresses-to-reveal (if (seq revealed-accounts) addresses-to-reveal (if (seq revealed-accounts)
(set (keys revealed-accounts)) (set (keys revealed-accounts))
;; Reveal all addresses as fallback. ;; Reveal all addresses as fallback.
(set (map :address wallet-accounts))) (set (map :address wallet-accounts)))
;; When there are no revealed addresses, such as when joining a ;; When there are no revealed addresses, such as when joining a
;; community, use first address for airdrops. ;; community, use first address for airdrops.
airdrop-address (or (->> revealed-accounts airdrop-address (or (->> revealed-accounts
@ -35,10 +34,7 @@
first first
:address))] :address))]
{:db (-> db {:db (-> db
;; Set to false by default while we don't persist the user's choice (assoc-in [:communities/selected-share-all-addresses community-id] share-future-addresses?)
;; in status-go, otherwise whenever the view is mounted, the choice
;; of selected addresses won't be respected.
(assoc-in [:communities/selected-share-all-addresses community-id] false)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal) (assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal)
(assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address)) (assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address))
:fx [[:dispatch :fx [[:dispatch
@ -60,36 +56,44 @@
is selecting an airdrop address, we must submit to status-go the current is selecting an airdrop address, we must submit to status-go the current
choice of addresses to share, and vice-versa. If we omit addresses to share, choice of addresses to share, and vice-versa. If we omit addresses to share,
status-go will default to all available." status-go will default to all available."
[{:keys [db]} [{:keys [community-id password on-success addresses airdrop-address]}]] [{:keys [db]}
(let [pub-key (get-in db [:profile/profile :public-key]) [{:keys [community-id password on-success addresses airdrop-address share-future-addresses?]}]]
wallet-accounts (utils/sorted-operable-non-watch-only-accounts db) (let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (if (seq addresses) wallet-accounts (utils/sorted-operable-non-watch-only-accounts db)
(set addresses) addresses-to-reveal (if (seq addresses)
(get-in db [:communities/all-addresses-to-reveal community-id])) (set addresses)
new-airdrop-address (if (contains? addresses-to-reveal airdrop-address) (get-in db [:communities/all-addresses-to-reveal community-id]))
airdrop-address new-airdrop-address (if (contains? addresses-to-reveal airdrop-address)
(->> wallet-accounts airdrop-address
(filter #(contains? addresses-to-reveal (:address %))) (->> wallet-accounts
first (filter #(contains? addresses-to-reveal (:address %)))
:address))] first
:address))
share-future-addresses? (if (nil? share-future-addresses?)
(get-in db [:communities/selected-share-all-addresses community-id])
share-future-addresses?)]
{:fx [[:effects.community/edit-shared-addresses {:fx [[:effects.community/edit-shared-addresses
{:community-id community-id {:community-id community-id
:password password :password password
:pub-key pub-key :pub-key pub-key
:addresses-to-reveal addresses-to-reveal :addresses-to-reveal addresses-to-reveal
:airdrop-address new-airdrop-address :share-future-addresses? share-future-addresses?
:on-success (fn [] :airdrop-address new-airdrop-address
(when (fn? on-success) :on-success (fn []
(on-success addresses-to-reveal new-airdrop-address)) (when (fn? on-success)
(rf/dispatch [:communities/edit-shared-addresses-success (on-success addresses-to-reveal
community-id addresses-to-reveal airdrop-address])) new-airdrop-address
:on-error [:communities/edit-shared-addresses-failure community-id]}]]})) share-future-addresses?))
(rf/dispatch [:communities/edit-shared-addresses-success
community-id addresses-to-reveal airdrop-address]))
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}))
(rf/reg-event-fx :communities/edit-shared-addresses edit-shared-addresses) (rf/reg-event-fx :communities/edit-shared-addresses edit-shared-addresses)
(rf/reg-event-fx :communities/edit-shared-addresses-success (rf/reg-event-fx :communities/edit-shared-addresses-success
(fn [_ [community-id addresses-to-reveal airdrop-address]] (fn [_ [community-id addresses-to-reveal airdrop-address share-future-addresses?]]
{:fx [[:dispatch [:communities/set-airdrop-address community-id airdrop-address]] {:fx [[:dispatch [:communities/set-airdrop-address community-id airdrop-address]]
[:dispatch [:communities/set-share-all-addresses community-id share-future-addresses?]]
[:dispatch [:communities/set-addresses-to-reveal community-id addresses-to-reveal]]]})) [:dispatch [:communities/set-addresses-to-reveal community-id addresses-to-reveal]]]}))
(rf/reg-event-fx :communities/edit-shared-addresses-failure (rf/reg-event-fx :communities/edit-shared-addresses-failure

View File

@ -70,7 +70,7 @@
(is (is
(match? (match?
{:db (-> (:db cofx) {:db (-> (:db cofx)
(assoc-in [:communities/selected-share-all-addresses community-id] false) (assoc-in [:communities/selected-share-all-addresses community-id] true)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal) (assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal)
(assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address)) (assoc-in [:communities/all-airdrop-addresses community-id] airdrop-address))
:fx [[:dispatch :fx [[:dispatch
@ -82,7 +82,7 @@
[:dispatch [:dispatch
[:communities/check-permissions-to-join-during-selection community-id [:communities/check-permissions-to-join-during-selection community-id
addresses-to-reveal]]]} addresses-to-reveal]]]}
(sut/do-init-permission-addresses cofx [community-id revealed-accounts]))))) (sut/do-init-permission-addresses cofx [community-id revealed-accounts true])))))
;; Expect to mark all addresses to be revealed and first one to receive ;; Expect to mark all addresses to be revealed and first one to receive
;; airdrops when no addresses were previously revealed. ;; airdrops when no addresses were previously revealed.
@ -93,7 +93,7 @@
(is (is
(match? (match?
{:db (-> (:db cofx) {:db (-> (:db cofx)
(assoc-in [:communities/selected-share-all-addresses community-id] false) (assoc-in [:communities/selected-share-all-addresses community-id] true)
(assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal)) (assoc-in [:communities/all-addresses-to-reveal community-id] addresses-to-reveal))
:fx [[:dispatch :fx [[:dispatch
[:communities/check-permissions-to-join-community [:communities/check-permissions-to-join-community
@ -101,7 +101,7 @@
[:dispatch [:dispatch
[:communities/check-permissions-to-join-during-selection community-id [:communities/check-permissions-to-join-during-selection community-id
addresses-to-reveal]]]} addresses-to-reveal]]]}
(sut/do-init-permission-addresses cofx [community-id revealed-accounts])))))) (sut/do-init-permission-addresses cofx [community-id revealed-accounts true]))))))
(deftest edit-shared-addresses-test (deftest edit-shared-addresses-test
(testing (testing
@ -109,6 +109,7 @@
fallback to all wallet addresses" fallback to all wallet addresses"
(let [pub-key "abcdef" (let [pub-key "abcdef"
revealed-addresses #{"0xB" "0xC"} revealed-addresses #{"0xB" "0xC"}
share-future-addresses? true
cofx {:db {:profile/profile {:public-key pub-key} cofx {:db {:profile/profile {:public-key pub-key}
:communities/all-addresses-to-reveal {community-id revealed-addresses}}} :communities/all-addresses-to-reveal {community-id revealed-addresses}}}
airdrop-address "0xB" airdrop-address "0xB"
@ -116,22 +117,24 @@
actual actual
(sut/edit-shared-addresses (sut/edit-shared-addresses
cofx cofx
[{:community-id community-id [{:community-id community-id
:password password :password password
:airdrop-address airdrop-address :airdrop-address airdrop-address
:on-success (fn [new-addresses-to-reveal] :share-future-addresses? share-future-addresses?
(is (match? revealed-addresses new-addresses-to-reveal)))}]) :on-success (fn [new-addresses-to-reveal]
(is (match? revealed-addresses new-addresses-to-reveal)))}])
on-success-wrapper (-> actual :fx first second :on-success)] on-success-wrapper (-> actual :fx first second :on-success)]
(is (match? (is (match?
{:fx [[:effects.community/edit-shared-addresses {:fx [[:effects.community/edit-shared-addresses
{:community-id community-id {:community-id community-id
:password password :password password
:pub-key pub-key :pub-key pub-key
:addresses-to-reveal revealed-addresses :addresses-to-reveal revealed-addresses
:airdrop-address airdrop-address :share-future-addresses? share-future-addresses?
:on-success fn? :airdrop-address airdrop-address
:on-error [:communities/edit-shared-addresses-failure community-id]}]]} :on-success fn?
:on-error [:communities/edit-shared-addresses-failure community-id]}]]}
actual)) actual))
(on-success-wrapper))) (on-success-wrapper)))

View File

@ -203,7 +203,9 @@
:addresses-for-permissions]) :addresses-for-permissions])
(rf/dispatch [:hide-bottom-sheet]))}]))}]) (rf/dispatch [:hide-bottom-sheet]))}]))}])
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses])) (rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses]))
(rf/dispatch [:communities/set-addresses-to-reveal id addresses-to-reveal]))) (do
(rf/dispatch [:communities/set-share-all-addresses id flag-share-all-addresses])
(rf/dispatch [:communities/set-addresses-to-reveal id addresses-to-reveal]))))
highest-role (rf/sub [:communities/highest-role-for-selection id]) highest-role (rf/sub [:communities/highest-role-for-selection id])
[unmodified-role _] (rn/use-state highest-role)] [unmodified-role _] (rn/use-state highest-role)]
@ -261,6 +263,7 @@
can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id]) can-edit-addresses? (rf/sub [:communities/can-edit-shared-addresses? id])
wallet-accounts (rf/sub [:wallet/operable-accounts-without-watched-accounts]) wallet-accounts (rf/sub [:wallet/operable-accounts-without-watched-accounts])
joined (rf/sub [:communities/community-joined id])
unmodified-addresses-to-reveal (rf/sub [:communities/addresses-to-reveal id]) unmodified-addresses-to-reveal (rf/sub [:communities/addresses-to-reveal id])
[addresses-to-reveal set-addresses-to-reveal] (rn/use-state unmodified-addresses-to-reveal) [addresses-to-reveal set-addresses-to-reveal] (rn/use-state unmodified-addresses-to-reveal)
@ -285,7 +288,6 @@
(set-flag-share-all-addresses new-value) (set-flag-share-all-addresses new-value)
(when new-value (when new-value
(set-addresses-to-reveal (set (map :address wallet-accounts))))))] (set-addresses-to-reveal (set (map :address wallet-accounts))))))]
(rn/use-mount (rn/use-mount
(fn [] (fn []
(when-not flag-share-all-addresses (when-not flag-share-all-addresses
@ -310,6 +312,7 @@
flag-share-all-addresses] flag-share-all-addresses]
:header [quo/page-setting :header [quo/page-setting
{:checked? flag-share-all-addresses {:checked? flag-share-all-addresses
:disabled? joined
:customization-color color :customization-color color
:on-change toggle-flag-share-all-addresses :on-change toggle-flag-share-all-addresses
:setting-text (i18n/label :setting-text (i18n/label

View File

@ -16,6 +16,7 @@
[status-im.navigation.events :as navigation] [status-im.navigation.events :as navigation]
[status-im.navigation.transitions :as transitions] [status-im.navigation.transitions :as transitions]
[taoensso.timbre :as log] [taoensso.timbre :as log]
[utils.collection :as collection-utils]
[utils.re-frame :as rf])) [utils.re-frame :as rf]))
(defn handle-community (defn handle-community
@ -372,15 +373,14 @@
(when (and community joined (not fetching-revealed-accounts)) (when (and community joined (not fetching-revealed-accounts))
{:db (assoc-in db [:communities community-id :fetching-revealed-accounts] true) {:db (assoc-in db [:communities community-id :fetching-revealed-accounts] true)
:json-rpc/call :json-rpc/call
[{:method "wakuext_getRevealedAccounts" [{:method "wakuext_latestRequestToJoinForCommunity"
:params [community-id (get-in db [:profile/profile :public-key])] :params [community-id]
:js-response true :on-success [:communities/get-revealed-accounts-success community-id on-success]
:on-success [:communities/get-revealed-accounts-success community-id on-success] :on-error (fn [err]
:on-error (fn [err] (log/error {:message "failed to fetch revealed accounts"
(log/error {:message "failed to fetch revealed accounts" :community-id community-id
:community-id community-id :err err})
:err err}) (rf/dispatch [:communities/get-revealed-accounts-failed community-id]))}]})))
(rf/dispatch [:communities/get-revealed-accounts-failed community-id]))}]})))
(rf/reg-event-fx :communities/get-revealed-accounts get-revealed-accounts) (rf/reg-event-fx :communities/get-revealed-accounts get-revealed-accounts)
@ -399,22 +399,18 @@
[:json-rpc/call :schema.common/rpc-call]]]]) [:json-rpc/call :schema.common/rpc-call]]]])
(rf/reg-event-fx :communities/get-revealed-accounts-success (rf/reg-event-fx :communities/get-revealed-accounts-success
(fn [{:keys [db]} [community-id on-success revealed-accounts-js]] (fn [{:keys [db]} [community-id on-success request-to-join]]
(when-let [community (get-in db [:communities community-id])] (when-let [community (get-in db [:communities community-id])]
(let [revealed-accounts (let [revealed-accounts (collection-utils/index-by :address (:revealedAccounts request-to-join))
(reduce share-future-addresses? (:shareFutureAddresses request-to-join)
(fn [acc {:keys [address] :as revealed-account}]
(assoc acc address revealed-account))
{}
(data-store.communities/<-revealed-accounts-rpc revealed-accounts-js))
community-with-revealed-accounts community-with-revealed-accounts
(-> community (-> community
(assoc :revealed-accounts revealed-accounts) (assoc :revealed-accounts revealed-accounts)
(assoc :share-future-addresses? share-future-addresses?)
(dissoc :fetching-revealed-accounts))] (dissoc :fetching-revealed-accounts))]
{:db (assoc-in db [:communities community-id] community-with-revealed-accounts) {:db (assoc-in db [:communities community-id] community-with-revealed-accounts)
:fx [(when (vector? on-success) :fx [(when (vector? on-success)
[:dispatch (conj on-success revealed-accounts)])]})))) [:dispatch (conj on-success revealed-accounts share-future-addresses?)])]}))))
(rf/reg-event-fx :communities/get-revealed-accounts-failed (rf/reg-event-fx :communities/get-revealed-accounts-failed
(fn [{:keys [db]} [community-id]] (fn [{:keys [db]} [community-id]]

View File

@ -146,8 +146,8 @@
effects (events/get-revealed-accounts {:db db} [community-id])] effects (events/get-revealed-accounts {:db db} [community-id])]
(is (match? (assoc-in db [:communities community-id :fetching-revealed-accounts] true) (is (match? (assoc-in db [:communities community-id :fetching-revealed-accounts] true)
(:db effects))) (:db effects)))
(is (match? {:method "wakuext_getRevealedAccounts" (is (match? {:method "wakuext_latestRequestToJoinForCommunity"
:params [community-id "profile-public-key"]} :params [community-id]}
(-> effects :json-rpc/call first (select-keys [:method :params])))))))) (-> effects :json-rpc/call first (select-keys [:method :params]))))))))
(deftest handle-community-test (deftest handle-community-test

View File

@ -117,13 +117,14 @@
{:community community-name})}]]]}))) {:community community-name})}]]]})))
(defn request-to-join-with-signatures (defn request-to-join-with-signatures
[_ [community-id addresses-to-reveal signatures]] [_ [community-id addresses-to-reveal signatures share-future-addresses?]]
{:fx [[:json-rpc/call {:fx [[:json-rpc/call
[{:method "wakuext_requestToJoinCommunity" [{:method "wakuext_requestToJoinCommunity"
:params [{:communityId community-id :params [{:communityId community-id
:signatures signatures :signatures signatures
:addressesToReveal addresses-to-reveal :addressesToReveal addresses-to-reveal
:airdropAddress (first addresses-to-reveal)}] :shareFutureAddresses share-future-addresses?
:airdropAddress (first addresses-to-reveal)}]
:js-response true :js-response true
:on-success [:communities/requested-to-join] :on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]]}) :on-error [:communities/requested-to-join-error community-id]}]]]})
@ -153,16 +154,18 @@
(defn request-to-join-with-addresses (defn request-to-join-with-addresses
[{:keys [db]} [{:keys [db]}
[{:keys [community-id password]}]] [{:keys [community-id password]}]]
(let [pub-key (get-in db [:profile/profile :public-key]) (let [pub-key (get-in db [:profile/profile :public-key])
addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id]) addresses-to-reveal (get-in db [:communities/all-addresses-to-reveal community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])] share-future-addresses? (get-in db [:communities/selected-share-all-addresses community-id])
airdrop-address (get-in db [:communities/all-airdrop-addresses community-id])]
{:fx [[:effects.community/request-to-join {:fx [[:effects.community/request-to-join
{:community-id community-id {:community-id community-id
:password password :password password
:pub-key pub-key :pub-key pub-key
:addresses-to-reveal addresses-to-reveal :addresses-to-reveal addresses-to-reveal
:airdrop-address airdrop-address :airdrop-address airdrop-address
:on-success [:communities/requested-to-join] :share-future-addresses? share-future-addresses?
:on-error [:communities/requested-to-join-error community-id]}]]})) :on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error community-id]}]]}))
(rf/reg-event-fx :communities/request-to-join-with-addresses request-to-join-with-addresses) (rf/reg-event-fx :communities/request-to-join-with-addresses request-to-join-with-addresses)

View File

@ -37,19 +37,22 @@
(sut/sign-data cofx [community-id password sign-params]))))) (sut/sign-data cofx [community-id password sign-params])))))
(deftest request-to-join-with-signatures-test (deftest request-to-join-with-signatures-test
(let [cofx {:db {}} (let [cofx {:db {}}
addresses-to-reveal [account-pub-key "0x2"] addresses-to-reveal [account-pub-key "0x2"]
signatures ["11111" "222222"] share-future-addresses? true
expected {:fx [[:json-rpc/call signatures ["11111" "222222"]
[{:method "wakuext_requestToJoinCommunity" expected {:fx [[:json-rpc/call
:params [{:communityId community-id [{:method "wakuext_requestToJoinCommunity"
:signatures signatures :params [{:communityId community-id
:addressesToReveal addresses-to-reveal :signatures signatures
:airdropAddress "0x1"}] :addressesToReveal addresses-to-reveal
:js-response true :shareFutureAddresses share-future-addresses?
:on-success [:communities/requested-to-join] :airdropAddress "0x1"}]
:on-error [:communities/requested-to-join-error :js-response true
community-id]}]]]}] :on-success [:communities/requested-to-join]
:on-error [:communities/requested-to-join-error
community-id]}]]]}]
(is (match? expected (is (match? expected
(sut/request-to-join-with-signatures cofx (sut/request-to-join-with-signatures cofx
[community-id addresses-to-reveal signatures]))))) [community-id addresses-to-reveal signatures
share-future-addresses?])))))

View File

@ -3,7 +3,7 @@
"_comment": "Instead use: scripts/update-status-go.sh <rev>", "_comment": "Instead use: scripts/update-status-go.sh <rev>",
"owner": "status-im", "owner": "status-im",
"repo": "status-go", "repo": "status-go",
"version": "v0.182.35", "version": "v0.182.36",
"commit-sha1": "484b8aca1a12718c9b3940f8a398e60ee4419600", "commit-sha1": "8458cafef9f876c11a58dc9ede14fc58a5a0f968",
"src-sha256": "1xnpx86k54lnsw93xw6la0sggd9y0nm7hn70cm0qgmq2qg8pzb3a" "src-sha256": "0nkb0zpqgpklh8mlrgcglh5v32qlfly9hd7ia3a1icx5kdp2q2wp"
} }