mirror of
https://github.com/status-im/status-mobile.git
synced 2025-01-14 02:35:54 +00:00
Add universal links for android
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
parent
9df15ffc78
commit
b17d9bab37
@ -20,3 +20,4 @@ GROUP_CHATS_ENABLED=0
|
|||||||
USE_SYM_KEY=0
|
USE_SYM_KEY=0
|
||||||
MAINNET_WARNING_ENABLED=1
|
MAINNET_WARNING_ENABLED=1
|
||||||
SPAM_BUTTON_DETECTION_ENABLED=1
|
SPAM_BUTTON_DETECTION_ENABLED=1
|
||||||
|
UNIVERSAL_LINKS_ENABLED=0
|
||||||
|
@ -54,6 +54,14 @@
|
|||||||
<data android:scheme="app-settings"
|
<data android:scheme="app-settings"
|
||||||
android:host="notification" />
|
android:host="notification" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter android:autoVerify="true">
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="http" android:host="get.status.im" />
|
||||||
|
<data android:scheme="https" android:host="get.status.im" />
|
||||||
|
<data android:scheme="app" android:host="get.status.im" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service
|
<service
|
||||||
android:name=".module.StatusService"
|
android:name=".module.StatusService"
|
||||||
|
@ -96,6 +96,7 @@ public class MainActivity extends ReactActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewIntent(final Intent intent) {
|
public void onNewIntent(final Intent intent) {
|
||||||
|
super.onNewIntent(intent);
|
||||||
if (intent.getDataString() != null && intent.getData().getScheme().startsWith("app-settings")) {
|
if (intent.getDataString() != null && intent.getData().getScheme().startsWith("app-settings")) {
|
||||||
startActivity(createNotificationSettingsIntent());
|
startActivity(createNotificationSettingsIntent());
|
||||||
}
|
}
|
||||||
|
@ -374,15 +374,18 @@
|
|||||||
:confirm-button-text (i18n/label :t/clear)
|
:confirm-button-text (i18n/label :t/clear)
|
||||||
:on-accept #(re-frame/dispatch [:clear-history])}}))
|
:on-accept #(re-frame/dispatch [:clear-history])}}))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(defn create-new-public-chat [topic {:keys [db now] :as cofx}]
|
||||||
:create-new-public-chat
|
|
||||||
[re-frame/trim-v]
|
|
||||||
(fn [{:keys [db now] :as cofx} [topic]]
|
|
||||||
(handlers-macro/merge-fx cofx
|
(handlers-macro/merge-fx cofx
|
||||||
(models/add-public-chat topic)
|
(models/add-public-chat topic)
|
||||||
(navigation/navigate-to-clean :home)
|
(navigation/navigate-to-clean :home)
|
||||||
(navigate-to-chat topic {})
|
(navigate-to-chat topic {})
|
||||||
(public-chat/join-public-chat topic))))
|
(public-chat/join-public-chat topic)))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:create-new-public-chat
|
||||||
|
[re-frame/trim-v]
|
||||||
|
(fn [cofx [topic]]
|
||||||
|
(create-new-public-chat topic cofx)))
|
||||||
|
|
||||||
(defn- group-name-from-contacts [selected-contacts all-contacts username]
|
(defn- group-name-from-contacts [selected-contacts all-contacts username]
|
||||||
(->> selected-contacts
|
(->> selected-contacts
|
||||||
@ -407,13 +410,16 @@
|
|||||||
(navigate-to-chat random-id {})
|
(navigate-to-chat random-id {})
|
||||||
(transport.message/send (group-chat/GroupAdminUpdate. chat-name selected-contacts) random-id)))))
|
(transport.message/send (group-chat/GroupAdminUpdate. chat-name selected-contacts) random-id)))))
|
||||||
|
|
||||||
|
(defn show-profile [identity {:keys [db] :as cofx}]
|
||||||
|
(handlers-macro/merge-fx cofx
|
||||||
|
{:db (assoc db :contacts/identity identity)}
|
||||||
|
(navigation/navigate-forget :profile)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:show-profile
|
:show-profile
|
||||||
[re-frame/trim-v]
|
[re-frame/trim-v]
|
||||||
(fn [{:keys [db] :as cofx} [identity]]
|
(fn [cofx [identity]]
|
||||||
(handlers-macro/merge-fx cofx
|
(show-profile identity cofx)))
|
||||||
{:db (assoc db :contacts/identity identity)}
|
|
||||||
(navigation/navigate-forget :profile))))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:resend-message
|
:resend-message
|
||||||
|
@ -150,6 +150,10 @@
|
|||||||
|
|
||||||
(spec/def ::device-UUID (spec/nilable string?))
|
(spec/def ::device-UUID (spec/nilable string?))
|
||||||
|
|
||||||
|
;;;;UNIVERSAL LINKS
|
||||||
|
|
||||||
|
(spec/def :universal-links/url (spec/nilable string?))
|
||||||
|
|
||||||
(spec/def ::db (allowed-keys
|
(spec/def ::db (allowed-keys
|
||||||
:opt
|
:opt
|
||||||
[:contacts/contacts
|
[:contacts/contacts
|
||||||
@ -187,6 +191,7 @@
|
|||||||
:inbox/last-received
|
:inbox/last-received
|
||||||
:inbox/current-id
|
:inbox/current-id
|
||||||
:inbox/fetching?
|
:inbox/fetching?
|
||||||
|
:universal-links/url
|
||||||
:browser/browsers
|
:browser/browsers
|
||||||
:browser/options
|
:browser/options
|
||||||
:new/open-dapp
|
:new/open-dapp
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
status-im.ui.screens.group.chat-settings.events
|
status-im.ui.screens.group.chat-settings.events
|
||||||
status-im.ui.screens.group.events
|
status-im.ui.screens.group.events
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
|
[status-im.utils.universal-links.core :as universal-links]
|
||||||
|
|
||||||
|
status-im.utils.universal-links.events
|
||||||
status-im.ui.screens.add-new.new-chat.navigation
|
status-im.ui.screens.add-new.new-chat.navigation
|
||||||
status-im.ui.screens.network-settings.events
|
status-im.ui.screens.network-settings.events
|
||||||
status-im.ui.screens.profile.events
|
status-im.ui.screens.profile.events
|
||||||
@ -315,7 +318,7 @@
|
|||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:initialize-account
|
:initialize-account
|
||||||
(fn [_ [_ address events-after]]
|
(fn [cofx [_ address events-after]]
|
||||||
{:dispatch-n (cond-> [[:initialize-account-db address]
|
{:dispatch-n (cond-> [[:initialize-account-db address]
|
||||||
[:initialize-protocol address]
|
[:initialize-protocol address]
|
||||||
[:fetch-web3-node-version]
|
[:fetch-web3-node-version]
|
||||||
@ -329,7 +332,8 @@
|
|||||||
[:update-transactions]
|
[:update-transactions]
|
||||||
[:get-fcm-token]
|
[:get-fcm-token]
|
||||||
[:update-sign-in-time]
|
[:update-sign-in-time]
|
||||||
[:show-mainnet-is-default-alert]]
|
[:show-mainnet-is-default-alert]
|
||||||
|
(universal-links/stored-url-event cofx)]
|
||||||
(seq events-after) (into events-after))}))
|
(seq events-after) (into events-after))}))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
(:require-macros [status-im.utils.views :refer [defview letsubs] :as views])
|
(:require-macros [status-im.utils.views :refer [defview letsubs] :as views])
|
||||||
(:require [re-frame.core :refer [dispatch]]
|
(:require [re-frame.core :refer [dispatch]]
|
||||||
[status-im.utils.platform :refer [android?]]
|
[status-im.utils.platform :refer [android?]]
|
||||||
|
[status-im.utils.universal-links.core :as utils.universal-links]
|
||||||
[status-im.ui.components.react :refer [view modal create-main-screen-view] :as react]
|
[status-im.ui.components.react :refer [view modal create-main-screen-view] :as react]
|
||||||
[status-im.ui.components.styles :as common-styles]
|
[status-im.ui.components.styles :as common-styles]
|
||||||
[status-im.ui.screens.main-tabs.views :refer [main-tabs]]
|
[status-im.ui.screens.main-tabs.views :refer [main-tabs]]
|
||||||
@ -134,7 +135,9 @@
|
|||||||
(defview main []
|
(defview main []
|
||||||
(letsubs [signed-up? [:signed-up?]
|
(letsubs [signed-up? [:signed-up?]
|
||||||
view-id [:get :view-id]]
|
view-id [:get :view-id]]
|
||||||
{:component-will-update (fn [] (react/dismiss-keyboard!))}
|
{:component-did-mount utils.universal-links/initialize
|
||||||
|
:component-will-unmount utils.universal-links/finalize
|
||||||
|
:component-will-update (fn [] (react/dismiss-keyboard!))}
|
||||||
(when view-id
|
(when view-id
|
||||||
(let [component (get-main-component view-id)
|
(let [component (get-main-component view-id)
|
||||||
main-screen-view (create-main-screen-view view-id)]
|
main-screen-view (create-main-screen-view view-id)]
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
(def stub-status-go? (enabled? (get-config :STUB_STATUS_GO 0)))
|
(def stub-status-go? (enabled? (get-config :STUB_STATUS_GO 0)))
|
||||||
(def offline-inbox-enabled? (enabled? (get-config :OFFLINE_INBOX_ENABLED "1")))
|
(def offline-inbox-enabled? (enabled? (get-config :OFFLINE_INBOX_ENABLED "1")))
|
||||||
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
|
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
|
||||||
|
(def universal-links-enabled? (enabled? (get-config :UNIVERSAL_LINK_ENABLED "1")))
|
||||||
(def log-level
|
(def log-level
|
||||||
(-> (get-config :LOG_LEVEL "error")
|
(-> (get-config :LOG_LEVEL "error")
|
||||||
string/lower-case
|
string/lower-case
|
||||||
|
100
src/status_im/utils/universal_links/core.cljs
Normal file
100
src/status_im/utils/universal_links/core.cljs
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
(ns status-im.utils.universal-links.core
|
||||||
|
(:require
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[re-frame.core :as re-frame]
|
||||||
|
[status-im.utils.config :as config]
|
||||||
|
[status-im.chat.events :as chat.events]
|
||||||
|
[status-im.models.account :as models.account]
|
||||||
|
[status-im.ui.components.react :as react]))
|
||||||
|
|
||||||
|
(def public-chat-regex #".*/chat/public/(.*)$")
|
||||||
|
(def profile-regex #".*/user/(.*)$")
|
||||||
|
|
||||||
|
(defn handle-public-chat [public-chat cofx]
|
||||||
|
(log/info "universal-links: handling public chat " public-chat)
|
||||||
|
(chat.events/create-new-public-chat public-chat cofx))
|
||||||
|
|
||||||
|
(defn handle-view-profile [profile-id cofx]
|
||||||
|
(log/info "universal links: handling view profile" profile-id)
|
||||||
|
(chat.events/show-profile profile-id cofx))
|
||||||
|
|
||||||
|
(defn handle-not-found [full-url]
|
||||||
|
(log/info "universal links: no handler for " full-url))
|
||||||
|
|
||||||
|
(defn match-url [url regex]
|
||||||
|
(some->> url
|
||||||
|
(re-matches regex)
|
||||||
|
peek))
|
||||||
|
|
||||||
|
(defn stored-url-event
|
||||||
|
"Return an event description for processing a url if in the database"
|
||||||
|
[{:keys [db]}]
|
||||||
|
(when-let [url (:universal-links/url db)]
|
||||||
|
[:handle-universal-link url]))
|
||||||
|
|
||||||
|
(defn dispatch-url
|
||||||
|
"Dispatch url so we can get access to re-frame/db"
|
||||||
|
[url]
|
||||||
|
(if-not (nil? url)
|
||||||
|
(re-frame/dispatch [:handle-universal-link url])
|
||||||
|
(log/debug "universal links: no url")))
|
||||||
|
|
||||||
|
(defn store-url-for-later
|
||||||
|
"Store the url in the db to be processed on login"
|
||||||
|
[url {:keys [db]}]
|
||||||
|
(assoc-in {:db db} [:db :universal-links/url] url))
|
||||||
|
|
||||||
|
(defn clear-url
|
||||||
|
"Remove a url from the db"
|
||||||
|
[{:keys [db]}]
|
||||||
|
(update {:db db} :db dissoc :universal-links/url))
|
||||||
|
|
||||||
|
(defn route-url
|
||||||
|
"Match a url against a list of routes and handle accordingly"
|
||||||
|
[url cofx]
|
||||||
|
(cond
|
||||||
|
(match-url url public-chat-regex)
|
||||||
|
(handle-public-chat (match-url url public-chat-regex) cofx)
|
||||||
|
|
||||||
|
(match-url url profile-regex)
|
||||||
|
(handle-view-profile (match-url url profile-regex) cofx)
|
||||||
|
|
||||||
|
:else (handle-not-found url)))
|
||||||
|
|
||||||
|
(defn handle-url
|
||||||
|
"Store url in the database if the user is not logged in, to be processed
|
||||||
|
on login, otherwise just handle it"
|
||||||
|
[url cofx]
|
||||||
|
(if (models.account/logged-in? cofx)
|
||||||
|
(do
|
||||||
|
(clear-url cofx)
|
||||||
|
(route-url url cofx))
|
||||||
|
(store-url-for-later url cofx)))
|
||||||
|
|
||||||
|
(defn unwrap-js-url [e]
|
||||||
|
(-> e
|
||||||
|
(js->clj :keywordize-keys true)
|
||||||
|
:url))
|
||||||
|
|
||||||
|
(def url-event-listener
|
||||||
|
(comp dispatch-url unwrap-js-url))
|
||||||
|
|
||||||
|
(defn initialize
|
||||||
|
"Add an event listener for handling background->foreground transition
|
||||||
|
and handles incoming url if the app has been started by clicking on a link"
|
||||||
|
[]
|
||||||
|
(when config/universal-links-enabled?
|
||||||
|
(log/debug "universal-links: initializing")
|
||||||
|
(.. react/linking
|
||||||
|
(getInitialURL)
|
||||||
|
(then dispatch-url))
|
||||||
|
(.. react/linking
|
||||||
|
(addEventListener "url" url-event-listener))))
|
||||||
|
|
||||||
|
(defn finalize
|
||||||
|
"Remove event listener for url"
|
||||||
|
[]
|
||||||
|
(when config/universal-links-enabled?
|
||||||
|
(log/debug "universal-links: finalizing")
|
||||||
|
(.. react/linking
|
||||||
|
(removeEventListener "url" url-event-listener))))
|
14
src/status_im/utils/universal_links/events.cljs
Normal file
14
src/status_im/utils/universal_links/events.cljs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
(ns status-im.utils.universal-links.events
|
||||||
|
(:require [re-frame.core :as re-frame]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[status-im.utils.config :as config]
|
||||||
|
[status-im.utils.handlers :as handlers]
|
||||||
|
[status-im.utils.handlers-macro :as handlers-macro]
|
||||||
|
[status-im.utils.universal-links.core :as universal-links]))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:handle-universal-link
|
||||||
|
(fn [cofx [_ url]]
|
||||||
|
(log/debug "universal links: event received for " url)
|
||||||
|
(when config/universal-links-enabled?
|
||||||
|
(universal-links/handle-url url cofx))))
|
@ -42,6 +42,7 @@
|
|||||||
[status-im.test.utils.mixpanel]
|
[status-im.test.utils.mixpanel]
|
||||||
[status-im.test.utils.prices]
|
[status-im.test.utils.prices]
|
||||||
[status-im.test.utils.keychain.core]
|
[status-im.test.utils.keychain.core]
|
||||||
|
[status-im.test.utils.universal-links.core]
|
||||||
[status-im.test.ui.screens.accounts.login.events]))
|
[status-im.test.ui.screens.accounts.login.events]))
|
||||||
|
|
||||||
(enable-console-print!)
|
(enable-console-print!)
|
||||||
@ -95,4 +96,5 @@
|
|||||||
'status-im.test.utils.mixpanel
|
'status-im.test.utils.mixpanel
|
||||||
'status-im.test.utils.prices
|
'status-im.test.utils.prices
|
||||||
'status-im.test.utils.keychain.core
|
'status-im.test.utils.keychain.core
|
||||||
|
'status-im.test.utils.universal-links.core
|
||||||
'status-im.test.ui.screens.accounts.login.events)
|
'status-im.test.ui.screens.accounts.login.events)
|
||||||
|
57
test/cljs/status_im/test/utils/universal_links/core.cljs
Normal file
57
test/cljs/status_im/test/utils/universal_links/core.cljs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
(ns status-im.test.utils.universal-links.core
|
||||||
|
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||||
|
[re-frame.core :as re-frame]
|
||||||
|
[status-im.utils.universal-links.core :as links]))
|
||||||
|
|
||||||
|
(deftest handle-url-test
|
||||||
|
(testing "the user is not logged in"
|
||||||
|
(testing "it stores the url for later processing"
|
||||||
|
(is (= {:db {:universal-links/url "some-url"}}
|
||||||
|
(links/handle-url "some-url" {:db {}})))))
|
||||||
|
(testing "the user is logged in"
|
||||||
|
(let [db {:account/account {:public-key "pk"}
|
||||||
|
:universal-links/url "some-url"}]
|
||||||
|
(testing "it clears the url"
|
||||||
|
(is (nil? (get-in (links/handle-url "some-url"
|
||||||
|
{:db db})
|
||||||
|
[:db :universal-links/url]))))
|
||||||
|
(testing "a public chat link"
|
||||||
|
(testing "it joins the chat"
|
||||||
|
(is (get-in (links/handle-url "app://get.status.im/chat/public/status"
|
||||||
|
{:db db})
|
||||||
|
[:db :chats "status"]))))
|
||||||
|
(testing "a user profile link"
|
||||||
|
(testing "it loads the profile"
|
||||||
|
(let [actual (links/handle-url "app://get.status.im/user/profile-id"
|
||||||
|
{:db db})]
|
||||||
|
(is (= "profile-id" (get-in actual [:db :contacts/identity])))
|
||||||
|
(is (= :profile (get-in actual [:db :view-id]))))))
|
||||||
|
(testing "a not found url"
|
||||||
|
(testing "it does nothing"
|
||||||
|
(is (nil? (links/handle-url "app://get.status.im/not-existing"
|
||||||
|
{:db db}))))))))
|
||||||
|
|
||||||
|
(deftest url-event-listener
|
||||||
|
(testing "the url is not nil"
|
||||||
|
(testing "it dispatches the url"
|
||||||
|
(let [actual (atom nil)]
|
||||||
|
(with-redefs [re-frame/dispatch #(reset! actual %)]
|
||||||
|
(links/url-event-listener #js {:url "some-url"})
|
||||||
|
(is (= [:handle-universal-link "some-url"] @actual))))))
|
||||||
|
(testing "the url is nil"
|
||||||
|
(testing "it does not dispatches the url"
|
||||||
|
(let [actual (atom nil)]
|
||||||
|
(with-redefs [re-frame/dispatch #(reset! actual %)]
|
||||||
|
(links/url-event-listener #js {})
|
||||||
|
(is (= nil @actual)))))))
|
||||||
|
|
||||||
|
(deftest stored-url-event
|
||||||
|
(testing "the url is in the database"
|
||||||
|
(testing "it returns the event"
|
||||||
|
(= [:handle-universal-link "some-url"]
|
||||||
|
(links/stored-url-event {:db {:universal-links/url "some-url"}}))))
|
||||||
|
(testing "the url is not in the database"
|
||||||
|
(testing "it returns nil"
|
||||||
|
(= nil
|
||||||
|
(links/stored-url-event {:db {}})))))
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user