[#5038] desktop deep links
Add support for status-im://chat/public/status type of links Signed-off-by: Dmitry Novotochinov <dmitry.novot@gmail.com>
This commit is contained in:
parent
299c44afef
commit
248e60e1d3
|
@ -58,6 +58,7 @@
|
||||||
"rn-snoopy/stream/buffer",
|
"rn-snoopy/stream/buffer",
|
||||||
"react-native/Libraries/vendor/emitter/EventEmitter",
|
"react-native/Libraries/vendor/emitter/EventEmitter",
|
||||||
"react-native-fetch-polyfill",
|
"react-native-fetch-polyfill",
|
||||||
|
"react-native-desktop-linking",
|
||||||
"text-encoding",
|
"text-encoding",
|
||||||
"js-sha3",
|
"js-sha3",
|
||||||
"web3-utils"
|
"web3-utils"
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"node_modules/react-native-keychain/desktop",
|
"node_modules/react-native-keychain/desktop",
|
||||||
"node_modules/react-native-securerandom/desktop",
|
"node_modules/react-native-securerandom/desktop",
|
||||||
"modules/react-native-status/desktop",
|
"modules/react-native-status/desktop",
|
||||||
|
"modules/react-native-desktop-linking/desktop",
|
||||||
"node_modules/google-breakpad"
|
"node_modules/google-breakpad"
|
||||||
],
|
],
|
||||||
"desktopFonts": [
|
"desktopFonts": [
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
||||||
|
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES}
|
||||||
|
\"DesktopLinking\" PARENT_SCOPE)
|
||||||
|
|
||||||
|
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/desktoplinking.cpp PARENT_SCOPE)
|
||||||
|
|
||||||
|
include(${CMAKE_ROOT}/Modules/ExternalProject.cmake)
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include "desktoplinking.h"
|
||||||
|
#include "bridge.h"
|
||||||
|
#include "eventdispatcher.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QFileOpenEvent>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct RegisterQMLMetaType {
|
||||||
|
RegisterQMLMetaType() { qRegisterMetaType<DesktopLinking *>(); }
|
||||||
|
} registerMetaType;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class DesktopLinkingPrivate {
|
||||||
|
public:
|
||||||
|
Bridge *bridge = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
DesktopLinking::DesktopLinking(QObject *parent)
|
||||||
|
: QObject(parent), d_ptr(new DesktopLinkingPrivate) {
|
||||||
|
|
||||||
|
QCoreApplication::instance()->installEventFilter(this);
|
||||||
|
connect(this, &DesktopLinking::urlOpened, this, &DesktopLinking::handleURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
DesktopLinking::~DesktopLinking() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesktopLinking::setBridge(Bridge *bridge) {
|
||||||
|
Q_D(DesktopLinking);
|
||||||
|
d->bridge = bridge;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString DesktopLinking::moduleName() { return "DesktopLinking"; }
|
||||||
|
|
||||||
|
QList<ModuleMethod *> DesktopLinking::methodsToExport() {
|
||||||
|
return QList<ModuleMethod *>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap DesktopLinking::constantsToExport() { return QVariantMap(); }
|
||||||
|
|
||||||
|
void DesktopLinking::handleURL(const QString url) {
|
||||||
|
Q_D(DesktopLinking);
|
||||||
|
qDebug() << "call of DesktopLinking::handleURL with param path: " << url;
|
||||||
|
d->bridge->eventDispatcher()->sendDeviceEvent("urlOpened", QVariantMap{{"url", url}});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DesktopLinking::eventFilter(QObject* obj, QEvent* event) {
|
||||||
|
if (event->type() == QEvent::FileOpen)
|
||||||
|
{
|
||||||
|
QFileOpenEvent* fileEvent = static_cast<QFileOpenEvent*>(event);
|
||||||
|
if (!fileEvent->url().isEmpty())
|
||||||
|
{
|
||||||
|
auto m_lastUrl = fileEvent->url().toString();
|
||||||
|
emit urlOpened(m_lastUrl);
|
||||||
|
}
|
||||||
|
else if (!fileEvent->file().isEmpty())
|
||||||
|
{
|
||||||
|
emit fileOpened(fileEvent->file());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// standard event processing
|
||||||
|
return QObject::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef DESKTOPLINKING_H
|
||||||
|
#define DESKTOPLINKING_H
|
||||||
|
|
||||||
|
#include "moduleinterface.h"
|
||||||
|
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
class DesktopLinkingPrivate;
|
||||||
|
class DesktopLinking : public QObject, public ModuleInterface {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_INTERFACES(ModuleInterface)
|
||||||
|
|
||||||
|
Q_DECLARE_PRIVATE(DesktopLinking)
|
||||||
|
|
||||||
|
public:
|
||||||
|
Q_INVOKABLE DesktopLinking(QObject* parent = 0);
|
||||||
|
~DesktopLinking();
|
||||||
|
|
||||||
|
void setBridge(Bridge* bridge) override;
|
||||||
|
|
||||||
|
QString moduleName() override;
|
||||||
|
QList<ModuleMethod*> methodsToExport() override;
|
||||||
|
QVariantMap constantsToExport() override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void urlOpened(QString path);
|
||||||
|
void fileOpened(QString path);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void handleURL(const QString url);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<DesktopLinkingPrivate> d_ptr;
|
||||||
|
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // DESKTOPLINKING_H
|
|
@ -0,0 +1,4 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const NativeModules = require('NativeModules');
|
||||||
|
module.exports = NativeModules.DesktopLinking;
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"nativePackage": true,
|
||||||
|
"name": "react-native-desktop-linking",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Handle status-im:// links",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": ""
|
||||||
|
}
|
|
@ -12,6 +12,8 @@
|
||||||
(def EventEmmiter (js/require "react-native/Libraries/vendor/emitter/EventEmitter"))
|
(def EventEmmiter (js/require "react-native/Libraries/vendor/emitter/EventEmitter"))
|
||||||
(def fetch (.-default (js/require "react-native-fetch-polyfill")))
|
(def fetch (.-default (js/require "react-native-fetch-polyfill")))
|
||||||
(def i18n (js/require "react-native-i18n"))
|
(def i18n (js/require "react-native-i18n"))
|
||||||
|
(def desktop-linking (.-DesktopLinking (.-NativeModules react-native)))
|
||||||
|
|
||||||
(def react-native-firebase #js {})
|
(def react-native-firebase #js {})
|
||||||
(def nfc-manager #js {})
|
(def nfc-manager #js {})
|
||||||
(def camera #js {:default #js {:constants {:Aspect "Portrait"}}})
|
(def camera #js {:default #js {:constants {:Aspect "Portrait"}}})
|
||||||
|
|
|
@ -27,3 +27,4 @@
|
||||||
(def snoopy-buffer (js/require "rn-snoopy/stream/buffer"))
|
(def snoopy-buffer (js/require "rn-snoopy/stream/buffer"))
|
||||||
(def background-timer (.-default (js/require "react-native-background-timer")))
|
(def background-timer (.-default (js/require "react-native-background-timer")))
|
||||||
(def react-navigation (js/require "react-navigation"))
|
(def react-navigation (js/require "react-navigation"))
|
||||||
|
(def desktop-linking #js {:addEventListener (fn [])})
|
||||||
|
|
|
@ -21,6 +21,7 @@ external_modules_dir=( \
|
||||||
'node_modules/react-native-securerandom/desktop' \
|
'node_modules/react-native-securerandom/desktop' \
|
||||||
'modules/react-native-status/desktop' \
|
'modules/react-native-status/desktop' \
|
||||||
'node_modules/google-breakpad' \
|
'node_modules/google-breakpad' \
|
||||||
|
'modules/react-native-desktop-linking/desktop' \
|
||||||
)
|
)
|
||||||
|
|
||||||
external_fonts=( \
|
external_fonts=( \
|
||||||
|
|
|
@ -8,11 +8,13 @@
|
||||||
[status-im.transport.message.protocol :as protocol]
|
[status-im.transport.message.protocol :as protocol]
|
||||||
[status-im.transport.message.public-chat :as public-chat]
|
[status-im.transport.message.public-chat :as public-chat]
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
|
[status-im.ui.components.desktop.events :as desktop.events]
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
[status-im.utils.clocks :as utils.clocks]
|
[status-im.utils.clocks :as utils.clocks]
|
||||||
[status-im.utils.fx :as fx]
|
[status-im.utils.fx :as fx]
|
||||||
[status-im.utils.gfycat.core :as gfycat]
|
[status-im.utils.gfycat.core :as gfycat]
|
||||||
[status-im.utils.utils :as utils]))
|
[status-im.utils.utils :as utils]
|
||||||
|
[status-im.utils.platform :as platform]))
|
||||||
|
|
||||||
(defn multi-user-chat? [cofx chat-id]
|
(defn multi-user-chat? [cofx chat-id]
|
||||||
(get-in cofx [:db :chats chat-id :group-chat]))
|
(get-in cofx [:db :chats chat-id :group-chat]))
|
||||||
|
@ -200,7 +202,9 @@
|
||||||
(add-public-chat topic)
|
(add-public-chat topic)
|
||||||
(navigate-to-chat topic {:modal? modal?
|
(navigate-to-chat topic {:modal? modal?
|
||||||
:navigation-reset? true})
|
:navigation-reset? true})
|
||||||
(public-chat/join-public-chat topic)))
|
(public-chat/join-public-chat topic)
|
||||||
|
(when platform/desktop?
|
||||||
|
(desktop.events/change-tab :home))))
|
||||||
|
|
||||||
(fx/defn disable-chat-cooldown
|
(fx/defn disable-chat-cooldown
|
||||||
"Turns off chat cooldown (protection against message spamming)"
|
"Turns off chat cooldown (protection against message spamming)"
|
||||||
|
|
|
@ -7,11 +7,12 @@
|
||||||
status-im.data-store.core
|
status-im.data-store.core
|
||||||
[status-im.ui.screens.desktop.views :as views]
|
[status-im.ui.screens.desktop.views :as views]
|
||||||
[status-im.core :as core]
|
[status-im.core :as core]
|
||||||
[status-im.ui.components.react :as react]))
|
[status-im.desktop.deep-links :as deep-links]))
|
||||||
|
|
||||||
(defn app-root []
|
(defn app-root []
|
||||||
(reagent/create-class
|
(reagent/create-class
|
||||||
{:reagent-render views/main}))
|
{:component-did-mount deep-links/add-event-listener
|
||||||
|
:reagent-render views/main}))
|
||||||
|
|
||||||
(defn init []
|
(defn init []
|
||||||
(core/init app-root))
|
(core/init app-root))
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
(ns status-im.desktop.deep-links
|
||||||
|
(:require [re-frame.core :as re-frame]
|
||||||
|
[status-im.react-native.js-dependencies :as js-dependencies]
|
||||||
|
[taoensso.timbre :as log]))
|
||||||
|
|
||||||
|
(defn add-event-listener []
|
||||||
|
(let [event-emitter (new (.-NativeEventEmitter js-dependencies/react-native)
|
||||||
|
js-dependencies/desktop-linking)]
|
||||||
|
(.addListener event-emitter
|
||||||
|
"urlOpened"
|
||||||
|
(fn [data]
|
||||||
|
(log/debug "urlOpened event with data:" data)
|
||||||
|
(let [url (get (js->clj data) "url")]
|
||||||
|
(re-frame/dispatch [:handle-universal-link url]))))))
|
|
@ -1,19 +1,40 @@
|
||||||
(ns status-im.ui.components.desktop.events
|
(ns status-im.ui.components.desktop.events
|
||||||
(:require [status-im.utils.handlers :as handlers]
|
(:require [status-im.utils.handlers :as handlers]
|
||||||
|
[status-im.utils.fx :as fx]
|
||||||
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
[status-im.utils.platform :as platform]))
|
[status-im.utils.platform :as platform]))
|
||||||
|
|
||||||
|
(fx/defn change-tab
|
||||||
|
[{:keys [db]} tab-name]
|
||||||
|
{:db (assoc-in db [:desktop/desktop :tab-view-id] tab-name)})
|
||||||
|
|
||||||
|
(fx/defn navigate-to
|
||||||
|
[{:keys [db] :as cofx} tab-name]
|
||||||
|
(navigation/navigate-to-cofx cofx
|
||||||
|
(if (and (= tab-name :home) (:current-chat-id db))
|
||||||
|
:chat
|
||||||
|
:home)
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(fx/defn fetch-desktop-version
|
||||||
|
[_ tab-name]
|
||||||
|
(when (and platform/isMacOs?
|
||||||
|
(= tab-name :profile))
|
||||||
|
{:http-get
|
||||||
|
{:url
|
||||||
|
"https://raw.githubusercontent.com/status-im/status-im.github.io/develop/env.sh"
|
||||||
|
:success-event-creator
|
||||||
|
(fn [o]
|
||||||
|
[:fetch-desktop-version-success o])}}))
|
||||||
|
|
||||||
|
(fx/defn show-desktop-tab
|
||||||
|
[cofx tab-name]
|
||||||
|
(fx/merge cofx
|
||||||
|
(change-tab tab-name)
|
||||||
|
(navigate-to tab-name)
|
||||||
|
(fetch-desktop-version tab-name)))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:show-desktop-tab
|
:show-desktop-tab
|
||||||
(fn [{:keys [db] :as cofx} [_ tab-name]]
|
(fn [cofx [_ tab-name]]
|
||||||
(merge {:db (assoc-in db [:desktop/desktop :tab-view-id] tab-name)
|
(show-desktop-tab cofx tab-name)))
|
||||||
:dispatch [:navigate-to (if (and (= tab-name :home) (:current-chat-id db))
|
|
||||||
:chat
|
|
||||||
:home)]}
|
|
||||||
(when (and platform/isMacOs?
|
|
||||||
(= tab-name :profile))
|
|
||||||
{:http-get
|
|
||||||
{:url
|
|
||||||
"https://raw.githubusercontent.com/status-im/status-im.github.io/develop/env.sh"
|
|
||||||
:success-event-creator
|
|
||||||
(fn [o]
|
|
||||||
[:fetch-desktop-version-success o])}}))))
|
|
||||||
|
|
|
@ -8,10 +8,12 @@
|
||||||
[status-im.ui.components.list-selection :as list-selection]
|
[status-im.ui.components.list-selection :as list-selection]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.screens.add-new.new-chat.db :as new-chat.db]
|
[status-im.ui.screens.add-new.new-chat.db :as new-chat.db]
|
||||||
|
[status-im.ui.screens.desktop.main.chat.events :as desktop.events]
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
[status-im.utils.config :as config]
|
[status-im.utils.config :as config]
|
||||||
[status-im.utils.fx :as fx]
|
[status-im.utils.fx :as fx]
|
||||||
[taoensso.timbre :as log]))
|
[taoensso.timbre :as log]
|
||||||
|
[status-im.utils.platform :as platform]))
|
||||||
|
|
||||||
;; 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
|
||||||
|
@ -63,7 +65,9 @@
|
||||||
(log/info "universal-links: handling view profile" profile-id)
|
(log/info "universal-links: handling view profile" profile-id)
|
||||||
(if (new-chat.db/own-whisper-identity? db profile-id)
|
(if (new-chat.db/own-whisper-identity? db profile-id)
|
||||||
(navigation/navigate-to-cofx cofx :my-profile nil)
|
(navigation/navigate-to-cofx cofx :my-profile nil)
|
||||||
(navigation/navigate-to-cofx (assoc-in cofx [:db :contacts/identity] profile-id) :profile nil)))
|
(if platform/desktop?
|
||||||
|
(desktop.events/show-profile-desktop profile-id cofx)
|
||||||
|
(navigation/navigate-to-cofx (assoc-in cofx [:db :contacts/identity] profile-id) :profile nil))))
|
||||||
|
|
||||||
(fx/defn handle-extension [cofx url]
|
(fx/defn handle-extension [cofx url]
|
||||||
(log/info "universal-links: handling url profile" url)
|
(log/info "universal-links: handling url profile" url)
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
(defrecord Notification [])
|
(defrecord Notification [])
|
||||||
(def react-native-firebase #js {:default #js {:notifications #js {:Notification Notification}}})
|
(def react-native-firebase #js {:default #js {:notifications #js {:Notification Notification}}})
|
||||||
|
|
||||||
|
(def desktop-linking #js {:addEventListener (fn [])})
|
||||||
|
|
||||||
(def snoopy #js {:default #js {}})
|
(def snoopy #js {:default #js {}})
|
||||||
(def snoopy-filter #js {:default #js {}})
|
(def snoopy-filter #js {:default #js {}})
|
||||||
(def snoopy-bars #js {:default #js {}})
|
(def snoopy-bars #js {:default #js {}})
|
||||||
|
|
Loading…
Reference in New Issue