Handle new universal links

Fixes: #10192
Fixes: #10083
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2020-03-26 12:36:41 +01:00
parent c47aca0ddb
commit e5dbac877e
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
9 changed files with 78 additions and 39 deletions

View File

@ -31,6 +31,7 @@
[status-im.init.core :as init] [status-im.init.core :as init]
[status-im.log-level.core :as log-level] [status-im.log-level.core :as log-level]
status-im.waku.core status-im.waku.core
[status-im.utils.universal-links.core :as universal-links]
[status-im.mailserver.core :as mailserver] [status-im.mailserver.core :as mailserver]
[status-im.mailserver.constants :as mailserver.constants] [status-im.mailserver.constants :as mailserver.constants]
[status-im.mailserver.topics :as mailserver.topics] [status-im.mailserver.topics :as mailserver.topics]
@ -145,6 +146,11 @@
(update :hardwallet dissoc :application-info))} (update :hardwallet dissoc :application-info))}
(multiaccounts.login/open-login key-uid photo-path name public-key))))) (multiaccounts.login/open-login key-uid photo-path name public-key)))))
(handlers/register-handler-fx
:login/filters-initialized
(fn [cofx]
(universal-links/process-stored-event cofx)))
;; multiaccounts update module ;; multiaccounts update module
(handlers/register-handler-fx (handlers/register-handler-fx
@ -1180,4 +1186,4 @@
(fn [{:keys [db]} [_ theme]] (fn [{:keys [db]} [_ theme]]
(let [cur-theme (get-in db [:multiaccount :appearance])] (let [cur-theme (get-in db [:multiaccount :appearance])]
(when (or (nil? cur-theme) (zero? cur-theme)) (when (or (nil? cur-theme) (zero? cur-theme))
{::multiaccounts/switch-theme (if (= :dark theme) 2 1)})))) {::multiaccounts/switch-theme (if (= :dark theme) 2 1)}))))

View File

@ -24,7 +24,6 @@
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.security :as security] [status-im.utils.security :as security]
[status-im.utils.types :as types] [status-im.utils.types :as types]
[status-im.utils.universal-links.core :as universal-links]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.wallet.core :as wallet] [status-im.wallet.core :as wallet]
[taoensso.timbre :as log] [taoensso.timbre :as log]
@ -208,7 +207,6 @@
;; NOTE: initializing mailserver depends on user mailserver ;; NOTE: initializing mailserver depends on user mailserver
;; preference which is why we wait for config callback ;; preference which is why we wait for config callback
(protocol/initialize-protocol {:default-mailserver true}) (protocol/initialize-protocol {:default-mailserver true})
(universal-links/process-stored-event)
(check-network-version network-id) (check-network-version network-id)
(chat.loading/initialize-chats) (chat.loading/initialize-chats)
(contact/initialize-contacts) (contact/initialize-contacts)

View File

@ -206,13 +206,17 @@
:discovery? discovery :discovery? discovery
:topic topic}) :topic topic})
(fx/defn set-filters-initialized [{:keys [db]}]
{:db (update db :filters/initialized inc)})
;; We check that both chats & contacts have been initialized ;; We check that both chats & contacts have been initialized
(defn filters-initialized? [db] (defn filters-initialized? [db]
(>= (:filters/initialized db) 2)) (>= (:filters/initialized db) 2))
(fx/defn set-filters-initialized [{:keys [db] :as cofx}]
(fx/merge
cofx
{:db (update db :filters/initialized inc)}
#(when (filters-initialized? (:db %))
{:dispatch [:login/filters-initialized]})))
(fx/defn handle-filters-added (fx/defn handle-filters-added
"Called every time we load a filter from statusgo, either from explicit call "Called every time we load a filter from statusgo, either from explicit call
or through signals. It stores the filter in the db and upsert the relevant or through signals. It stores the filter in the db and upsert the relevant

View File

@ -21,17 +21,17 @@
;; TODO(yenda) investigate why `handle-universal-link` event is ;; TODO(yenda) investigate why `handle-universal-link` event is
;; dispatched 7 times for the same link ;; dispatched 7 times for the same link
(def public-chat-regex #".*/chat/public/(.*)$") (def public-chat-regex #"(?:https?://join\.)?status[.-]im(?::/)?/(?:chat/public/([a-z0-9\-]+)$|([a-z0-9\-]+))$")
(def profile-regex #".*/user/(.*)$") (def profile-regex #"(?:https?://join\.)?status[.-]im(?::/)?/(?:u/(0x.*)$|u/(.*)$|user/(.*))$")
(def browse-regex #".*/browse/(.*)$") (def browse-regex #"(?:https?://join\.)?status[.-]im(?::/)?/(?:b/(.*)$|browse/(.*))$")
;; domains should be without the trailing slash ;; domains should be without the trailing slash
(def domains {:external "https://join.status.im" (def domains {:external "https://join.status.im"
:internal "status-im:/"}) :internal "status-im:/"})
(def links {:public-chat "%s/chat/public/%s" (def links {:public-chat "%s/%s"
:user "%s/user/%s" :user "%s/u/%s"
:browse "%s/browse/%s"}) :browse "%s/b/%s"})
(defn generate-link [link-type domain-type param] (defn generate-link [link-type domain-type param]
(gstring/format (get links link-type) (gstring/format (get links link-type)
@ -41,7 +41,10 @@
(defn match-url [url regex] (defn match-url [url regex]
(some->> url (some->> url
(re-matches regex) (re-matches regex)
peek)) rest
reverse
(remove nil?)
first))
(defn is-request-url? [url] (defn is-request-url? [url]
(string/starts-with? url "ethereum:")) (string/starts-with? url "ethereum:"))
@ -116,8 +119,6 @@
"Match a url against a list of routes and handle accordingly" "Match a url against a list of routes and handle accordingly"
[cofx url] [cofx url]
(cond (cond
(match-url url public-chat-regex)
(handle-public-chat cofx (match-url url public-chat-regex))
(spec/valid? :global/public-key (match-url url profile-regex)) (spec/valid? :global/public-key (match-url url profile-regex))
(handle-view-profile cofx {:public-key (match-url url profile-regex)}) (handle-view-profile cofx {:public-key (match-url url profile-regex)})
@ -132,6 +133,10 @@
(is-request-url? url) (is-request-url? url)
(handle-eip681 cofx url) (handle-eip681 cofx url)
;; This needs to stay last, as it's a bit of a catch-all regex
(match-url url public-chat-regex)
(handle-public-chat cofx (match-url url public-chat-regex))
:else (handle-not-found url))) :else (handle-not-found url)))
(fx/defn store-url-for-later (fx/defn store-url-for-later

View File

@ -10,14 +10,12 @@ class TestDeepLinks(SingleDeviceTestCase):
@marks.testrail_id(5396) @marks.testrail_id(5396)
@marks.high @marks.high
@marks.skip
# TODO: skipped because universal links won't work in emulators regardless of OS version
def test_open_public_chat_using_deep_link(self): def test_open_public_chat_using_deep_link(self):
sign_in_view = SignInView(self.driver) sign_in_view = SignInView(self.driver)
sign_in_view.create_user() sign_in_view.create_user()
self.driver.close_app() self.driver.close_app()
chat_name = sign_in_view.get_public_chat_name() chat_name = sign_in_view.get_public_chat_name()
deep_link = 'https://join.status.im/chat/public/%s' % chat_name deep_link = 'https://join.status.im/%s' % chat_name
sign_in_view.open_weblink_and_login(deep_link) sign_in_view.open_weblink_and_login(deep_link)
chat_view = sign_in_view.get_chat_view() chat_view = sign_in_view.get_chat_view()
try: try:
@ -27,30 +25,28 @@ class TestDeepLinks(SingleDeviceTestCase):
@marks.testrail_id(5441) @marks.testrail_id(5441)
@marks.medium @marks.medium
@marks.skip
# TODO: skipped because universal links won't work in emulators regardless of OS version
def test_open_user_profile_using_deep_link(self): def test_open_user_profile_using_deep_link(self):
sign_in_view = SignInView(self.driver) sign_in_view = SignInView(self.driver)
sign_in_view.create_user() sign_in_view.create_user()
for user_ident in ens_user['ens'], basic_user['public_key']: profile = sign_in_view.profile_button.click()
profile.switch_network('Mainnet with upstream RPC')
for user_ident in ens_user['ens'], ens_user['ens_another_domain'], ens_user['public_key'],:
self.driver.close_app() self.driver.close_app()
deep_link = 'https://join.status.im/user/%s' % user_ident deep_link = 'https://join.status.im/u/%s' % user_ident
sign_in_view.open_weblink_and_login(deep_link) sign_in_view.open_weblink_and_login(deep_link)
chat_view = sign_in_view.get_chat_view() chat_view = sign_in_view.get_chat_view()
for text in basic_user['username'], 'Add to contacts': for text in ens_user['username'], 'Add to contacts':
if not chat_view.element_by_text(text).scroll_to_element(10): if not chat_view.element_by_text(text).scroll_to_element(10):
self.driver.fail("User profile screen is not opened") self.driver.fail("User profile screen is not opened")
@marks.testrail_id(5442) @marks.testrail_id(5442)
@marks.medium @marks.medium
@marks.skip
# TODO: skipped because universal links won't work in emulators regardless of OS version
def test_open_dapp_using_deep_link(self): def test_open_dapp_using_deep_link(self):
sign_in_view = SignInView(self.driver) sign_in_view = SignInView(self.driver)
sign_in_view.create_user() sign_in_view.create_user()
self.driver.close_app() self.driver.close_app()
dapp_name = test_dapp_url dapp_name = test_dapp_url
dapp_deep_link = 'https://join.status.im/browse/%s' % dapp_name dapp_deep_link = 'https://join.status.im/b/%s' % dapp_name
sign_in_view.open_weblink_and_login(dapp_deep_link) sign_in_view.open_weblink_and_login(dapp_deep_link)
web_view = sign_in_view.get_chat_view() web_view = sign_in_view.get_chat_view()
try: try:
@ -61,13 +57,11 @@ class TestDeepLinks(SingleDeviceTestCase):
@marks.testrail_id(5780) @marks.testrail_id(5780)
@marks.medium @marks.medium
@marks.skip
# TODO: skipped because universal links won't work in emulators regardless of OS version
def test_open_own_user_profile_using_deep_link(self): def test_open_own_user_profile_using_deep_link(self):
sign_in_view = SignInView(self.driver) sign_in_view = SignInView(self.driver)
sign_in_view.recover_access(passphrase=basic_user['passphrase']) sign_in_view.recover_access(passphrase=basic_user['passphrase'])
self.driver.close_app() self.driver.close_app()
deep_link = 'https://join.status.im/user/%s' % basic_user['public_key'] deep_link = 'https://join.status.im/u/%s' % basic_user['public_key']
sign_in_view.open_weblink_and_login(deep_link) sign_in_view.open_weblink_and_login(deep_link)
profile_view = sign_in_view.get_profile_view() profile_view = sign_in_view.get_profile_view()
if profile_view.default_username_text.text != basic_user['username'] \ if profile_view.default_username_text.text != basic_user['username'] \
@ -77,13 +71,11 @@ class TestDeepLinks(SingleDeviceTestCase):
@marks.testrail_id(5781) @marks.testrail_id(5781)
@marks.medium @marks.medium
@marks.skip
# TODO: skipped because universal links won't work in emulators regardless of OS version
def test_deep_link_with_invalid_user_public_key(self): def test_deep_link_with_invalid_user_public_key(self):
sign_in_view = SignInView(self.driver) sign_in_view = SignInView(self.driver)
sign_in_view.create_user() sign_in_view.create_user()
self.driver.close_app() self.driver.close_app()
deep_link = 'https://join.status.im/user/%s' % basic_user['public_key'][:-10] deep_link = 'https://join.status.im/u/%s' % basic_user['public_key'][:-10]
sign_in_view.open_weblink_and_login(deep_link) sign_in_view.open_weblink_and_login(deep_link)
home_view = sign_in_view.get_home_view() home_view = sign_in_view.get_home_view()
home_view.plus_button.click_until_presence_of_element(home_view.start_new_chat_button) home_view.plus_button.click_until_presence_of_element(home_view.start_new_chat_button)

View File

@ -184,7 +184,7 @@ class BaseElement(object):
width, height = size['width'], size['height'] width, height = size['width'], size['height']
self.driver.swipe(start_x=x + width * 0.75, start_y=y + height / 2, end_x=x, end_y=y + height / 2) self.driver.swipe(start_x=x + width * 0.75, start_y=y + height / 2, end_x=x, end_y=y + height / 2)
def swipe_to_web_element(self, depth=400): def swipe_to_web_element(self, depth=700):
element = self.find_element() element = self.find_element()
location = element.location location = element.location
x, y = location['x'], location['y'] x, y = location['x'], location['y']

View File

@ -43,11 +43,13 @@ class AllowButton(BaseButton):
except NoSuchElementException: except NoSuchElementException:
pass pass
class SearchEditBox(BaseEditBox): class SearchEditBox(BaseEditBox):
def __init__(self, driver): def __init__(self, driver):
super(SearchEditBox, self).__init__(driver) super(SearchEditBox, self).__init__(driver)
self.locator = self.Locator.text_selector("Search or type web address") self.locator = self.Locator.text_selector("Search or type web address")
class DenyButton(BaseButton): class DenyButton(BaseButton):
def __init__(self, driver): def __init__(self, driver):
super(DenyButton, self).__init__(driver) super(DenyButton, self).__init__(driver)
@ -402,6 +404,13 @@ class BaseView(object):
pass pass
iterations += 1 iterations += 1
def rooted_device_continue(self):
try:
self.continue_button.wait_for_element(3)
self.continue_button.click()
except (NoSuchElementException, TimeoutException):
pass
def close_native_device_dialog(self, alert_text_part): def close_native_device_dialog(self, alert_text_part):
element = self.element_by_text_part(alert_text_part) element = self.element_by_text_part(alert_text_part)
if element.is_element_present(1): if element.is_element_present(1):
@ -673,12 +682,9 @@ class BaseView(object):
self.driver.back() self.driver.back()
self.driver.back() self.driver.back()
def open_universal_web_link(self, deep_link): def open_universal_web_link(self, deep_link):
start_web_browser(self.driver) start_web_browser(self.driver)
self.search_in_google_edit_box.set_value(deep_link) self.driver.get(deep_link)
self.confirm()
self.open_in_status_button.click()
# Method-helper # Method-helper
def write_page_source_to_file(self, full_path_to_file): def write_page_source_to_file(self, full_path_to_file):

View File

@ -248,4 +248,5 @@ class SignInView(BaseView):
def open_weblink_and_login(self, url_weblink): def open_weblink_and_login(self, url_weblink):
self.open_universal_web_link(url_weblink) self.open_universal_web_link(url_weblink)
self.rooted_device_continue()
self.sign_in() self.sign_in()

View File

@ -19,26 +19,53 @@
(is (nil? (get-in (links/handle-url {:db db} "some-url") (is (nil? (get-in (links/handle-url {:db db} "some-url")
[:db :universal-links/url])))) [:db :universal-links/url]))))
(testing "a public chat link" (testing "a public chat link"
(testing "it joins the chat, short version"
(is (get-in (links/handle-url {:db db} "status-im://status")
[:db :chats "status"])))
(testing "it joins the chat, short version, https"
(is (get-in (links/handle-url {:db db} "https://join.status.im/status")
[:db :chats "status"])))
(testing "it joins the chat" (testing "it joins the chat"
(is (get-in (links/handle-url {:db db} "status-im://chat/public/status") (is (get-in (links/handle-url {:db db} "status-im://chat/public/status")
[:db :chats "status"])))) [:db :chats "status"]))))
(testing "a browse dapp link" (testing "a browse dapp link"
(testing "it open the dapps short version"
(is
(= "www.cryptokitties.co"
(:browser/show-browser-selection (links/handle-url {:db db} "status-im://b/www.cryptokitties.co")))))
(testing "it open the dapps short version, https"
(is
(= "www.cryptokitties.co"
(:browser/show-browser-selection (links/handle-url {:db db} "https://join.status.im/b/www.cryptokitties.co")))))
(testing "it open the dapps" (testing "it open the dapps"
(is (is
(= "www.cryptokitties.co" (= "www.cryptokitties.co"
(:browser/show-browser-selection (links/handle-url {:db db} "status-im://browse/www.cryptokitties.co")))))) (:browser/show-browser-selection (links/handle-url {:db db} "status-im://browse/www.cryptokitties.co"))))))
(testing "a user profile link" (testing "a user profile link"
(testing "it loads the profile, short version"
(let [actual (links/handle-url {:db db} "status-im://u/0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")]
(is (= "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073" (get-in actual [:db :contacts/identity])))))
(testing "it loads the profile, short version https"
(let [actual (links/handle-url {:db db} "https://join.status.im/u/0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")]
(is (= "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073" (get-in actual [:db :contacts/identity])))))
(testing "it loads the profile" (testing "it loads the profile"
(let [actual (links/handle-url {:db db} "status-im://user/0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")] (let [actual (links/handle-url {:db db} "status-im://user/0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")]
(is (= "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073" (get-in actual [:db :contacts/identity])))))) (is (= "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073" (get-in actual [:db :contacts/identity]))))))
(testing "Handle a custom string as a an profile link with ens-name"
(is (= (get-in (links/handle-url {:db db} "status-im://u/CONTACTCODE")
[:resolve-public-key :contact-identity])
"CONTACTCODE")))
(testing "Handle a custom string as a an profile link with ens-name, http"
(is (= (get-in (links/handle-url {:db db} "https://join.status.im/u/statuse2e")
[:resolve-public-key :contact-identity])
"statuse2e")))
(testing "Handle a custom string as a an profile link with ens-name" (testing "Handle a custom string as a an profile link with ens-name"
(is (= (get-in (links/handle-url {:db db} "status-im://user/CONTACTCODE") (is (= (get-in (links/handle-url {:db db} "status-im://user/CONTACTCODE")
[:resolve-public-key :contact-identity]) [:resolve-public-key :contact-identity])
"CONTACTCODE"))) "CONTACTCODE")))
(testing "a not found url" (testing "a not found url"
(testing "it does nothing" (testing "it does nothing"
(is (nil? (links/handle-url {:db db} "status-im://not-existing"))))))))) (is (nil? (links/handle-url {:db db} "status-im://blah/not-existing")))))))))
(deftest url-event-listener (deftest url-event-listener
(testing "the url is not nil" (testing "the url is not nil"