From 92d00f425019a3ee174b3eb903daff1d33b8887e Mon Sep 17 00:00:00 2001 From: Vitaliy Vlasov Date: Sat, 27 Oct 2018 21:24:47 +0200 Subject: [PATCH] Use multiple app instances simultaneously Signed-off-by: Vitaliy Vlasov --- ci/Jenkinsfile.linux | 2 +- ci/Jenkinsfile.windows | 2 +- desktop/main.cpp | 61 ++++++++++++++++++- env/dev/env/android/main.cljs | 4 +- env/dev/env/desktop/main.cljs | 4 +- env/dev/env/ios/main.cljs | 4 +- figwheel-bridge.js | 2 +- .../react-native-status/desktop/rctstatus.cpp | 10 ++- scripts/build-desktop.sh | 8 +-- src/status_im/android/core.cljs | 5 +- src/status_im/data_store/realm/core.cljs | 44 +++++++------ src/status_im/desktop/core.cljs | 11 +++- src/status_im/events.cljs | 5 ++ src/status_im/init/core.cljs | 2 + src/status_im/ios/core.cljs | 5 +- src/status_im/node/core.cljs | 16 +++-- src/status_im/ui/screens/db.cljs | 3 + src/status_im/ui/screens/subs.cljs | 4 ++ ubuntu-server.js | 6 +- 19 files changed, 147 insertions(+), 51 deletions(-) diff --git a/ci/Jenkinsfile.linux b/ci/Jenkinsfile.linux index 5091cadb88..a9d5e7d209 100644 --- a/ci/Jenkinsfile.linux +++ b/ci/Jenkinsfile.linux @@ -9,7 +9,7 @@ pipeline { "-v /dev/fuse:/dev/fuse "+ "-v /var/tmp/lein:/var/tmp/lein:rw "+ "-v /var/tmp/npm:/var/tmp/npm:rw "+ - "-v /opt/StatusImAppImage.zip:/opt/StatusImAppImage.zip:ro" + "-v /opt/StatusImAppImage_20181113.zip:/opt/StatusImAppImage.zip:ro" ) } } diff --git a/ci/Jenkinsfile.windows b/ci/Jenkinsfile.windows index 344e71a8c7..1cf20fe65f 100644 --- a/ci/Jenkinsfile.windows +++ b/ci/Jenkinsfile.windows @@ -9,7 +9,7 @@ pipeline { "-v /dev/fuse:/dev/fuse "+ "-v /var/tmp/lein:/var/tmp/lein:rw "+ "-v /var/tmp/npm:/var/tmp/npm:rw "+ - "-v /opt/StatusIm-Windows-base-image.zip:/opt/StatusIm-Windows-base-image.zip:ro" + "-v /opt/StatusIm-Windows-base-image_20181113.zip:/opt/StatusIm-Windows-base-image.zip:ro" ) } } diff --git a/desktop/main.cpp b/desktop/main.cpp index 1a8f290d1f..a0726be934 100644 --- a/desktop/main.cpp +++ b/desktop/main.cpp @@ -67,6 +67,8 @@ class ReactNativeProperties : public QObject { QString pluginsPath READ pluginsPath WRITE setPluginsPath NOTIFY pluginsPathChanged) Q_PROPERTY( QString executor READ executor WRITE setExecutor NOTIFY executorChanged) + Q_PROPERTY( + QVariantMap initialProps READ initialProps WRITE setInitialProps NOTIFY initialPropsChanged) public: ReactNativeProperties(QObject *parent = nullptr) : QObject(parent) { m_codeLocation = m_packagerTemplate.arg(m_packagerHost).arg(m_packagerPort); @@ -99,6 +101,14 @@ public: m_executor = executor; Q_EMIT executorChanged(); } + QVariantMap initialProps() const { return m_initialProps; } + void setInitialProps(const QVariantMap &initialProps) { + if (m_initialProps == initialProps) + return; + m_initialProps = initialProps; + Q_EMIT initialPropsChanged(); + } + QString packagerHost() const { return m_packagerHost; } void setPackagerHost(const QString &packagerHost) { if (m_packagerHost == packagerHost) @@ -130,11 +140,13 @@ public: setLiveReload(false); } } + Q_SIGNALS: void liveReloadChanged(); void codeLocationChanged(); void pluginsPathChanged(); void executorChanged(); + void initialPropsChanged(); private: bool m_liveReload = false; @@ -150,6 +162,7 @@ private: #else QString m_executor = "LocalServerConnection"; #endif + QVariantMap m_initialProps; }; void saveMessage(QtMsgType type, const QMessageLogContext &context, @@ -197,8 +210,14 @@ bool redirectLogIntoFile() { } QString getDataStoragePath() { - QString dataStoragePath = - QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + QString statusDataDir = qgetenv("STATUS_DATA_DIR"); + QString dataStoragePath; + if (!statusDataDir.isEmpty()) { + dataStoragePath = statusDataDir; + } + else { + dataStoragePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + } QDir dir(dataStoragePath); if (!dir.exists()) { dir.mkpath("."); @@ -206,6 +225,21 @@ QString getDataStoragePath() { return dataStoragePath; } +void renameRealmDirs() { + QDir dataDir(getDataStoragePath()); + qCDebug(STATUS) << "### path: " << getDataStoragePath(); + + if (dataDir.exists("default.realmaccounts")) { + dataDir.mkdir("default.realm"); + dataDir.rename("default.realmaccounts", "default.realm/accounts"); + dataDir.rename("default.realmdefault.realm", "default.realm/default.realm"); + dataDir.rename("default.realmdefault.realm.lock", "default.realm/default.realm.lock"); + dataDir.rename("default.realmdefault.realm.management", "default.realm/default.realm.management"); + dataDir.rename("default.realmdefault.realm.note", "default.realm/default.realm.note"); + } +} + + int main(int argc, char **argv) { QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); @@ -215,12 +249,15 @@ int main(int argc, char **argv) { QString appPath = QCoreApplication::applicationDirPath(); QString dataStoragePath = getDataStoragePath(); #ifdef BUILD_FOR_BUNDLE - killZombieJsServer(); + if (qgetenv("STATUS_DATA_DIR").isEmpty()) { + killZombieJsServer(); + } #else appPath.append(CRASH_REPORT_EXECUTABLE_RELATIVE_PATH); dataStoragePath = ""; #endif + renameRealmDirs(); ExceptionGlobalHandler exceptionHandler( appPath + QDir::separator() + CRASH_REPORT_EXECUTABLE, exceptionPostHandledCallback, dataStoragePath); @@ -251,6 +288,19 @@ int main(int argc, char **argv) { QQuickView view; ReactNativeProperties *rnp = new ReactNativeProperties(&view); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QVariantMap initialProps; + QString statusNodePort = env.value("STATUS_NODE_PORT"); + QString statusDataDir = env.value("STATUS_DATA_DIR"); + if (!statusNodePort.isEmpty()) { + initialProps["STATUS_NODE_PORT"] = statusNodePort; + } + if (!statusDataDir.isEmpty()) { + initialProps["STATUS_DATA_DIR"] = statusDataDir; + } + + rnp->setInitialProps(initialProps); + #ifdef BUILD_FOR_BUNDLE rnp->setCodeLocation("file:" + QGuiApplication::applicationDirPath() + "/assets"); @@ -386,6 +436,11 @@ bool runNodeJsServer() { g_nodeJsServerProcess = new QProcess(); g_nodeJsServerProcess->setWorkingDirectory(getDataStoragePath()); g_nodeJsServerProcess->setProgram(QGuiApplication::applicationDirPath() + QDir::separator() + NODEJS_SERVER_NAME); + QString port = qgetenv("REACT_SERVER_PORT"); + if (!port.isEmpty()) { + QStringList arguments = (QStringList() << "--port" << port); + g_nodeJsServerProcess->setArguments(arguments); + } QObject::connect(g_nodeJsServerProcess, &QProcess::errorOccurred, [=](QProcess::ProcessError) { qCWarning(JSSERVER) << "process name: " diff --git a/env/dev/env/android/main.cljs b/env/dev/env/android/main.cljs index bb19159cf7..db9bab1360 100644 --- a/env/dev/env/android/main.cljs +++ b/env/dev/env/android/main.cljs @@ -13,10 +13,10 @@ (assert (exists? core/app-root) "Fatal Error - Your core.cljs file doesn't define an 'app-root' function!!! - Perhaps there was a compilation failure?") (def cnt (r/atom 0)) -(defn reloader [] @cnt [core/app-root]) +(defn reloader [props] @cnt [core/app-root props]) ;; Do not delete, root-el is used by the figwheel-bridge.js -(def root-el (r/as-element [reloader])) +(def root-el (r/reactify-component reloader)) (figwheel/start {:websocket-url (:android conf/figwheel-urls) :heads-up-display false diff --git a/env/dev/env/desktop/main.cljs b/env/dev/env/desktop/main.cljs index 72c5791446..687e69e776 100644 --- a/env/dev/env/desktop/main.cljs +++ b/env/dev/env/desktop/main.cljs @@ -13,10 +13,10 @@ (assert (exists? core/app-root) "Fatal Error - Your core.cljs file doesn't define an 'app-root' function!!! - Perhaps there was a compilation failure?") (def cnt (r/atom 0)) -(defn reloader [] @cnt [core/app-root]) +(defn reloader [props] @cnt [core/app-root props]) ;; Do not delete, root-el is used by the figwheel-bridge.js -(def root-el (r/as-element [reloader])) +(def root-el (r/reactify-component reloader)) (figwheel/start {:websocket-url (:desktop conf/figwheel-urls) :heads-up-display false diff --git a/env/dev/env/ios/main.cljs b/env/dev/env/ios/main.cljs index 887650871f..fb4a045ccf 100644 --- a/env/dev/env/ios/main.cljs +++ b/env/dev/env/ios/main.cljs @@ -13,10 +13,10 @@ (assert (exists? core/app-root) "Fatal Error - Your core.cljs file doesn't define an 'app-root' function!!! - Perhaps there was a compilation failure?") (def cnt (r/atom 0)) -(defn reloader [] @cnt [core/app-root]) +(defn reloader [props] @cnt [core/app-root props]) ;; Do not delete, root-el is used by the figwheel-bridge.js -(def root-el (r/as-element [reloader])) +(def root-el (r/reactify-component reloader)) (figwheel/start {:websocket-url (:ios conf/figwheel-urls) :heads-up-display false diff --git a/figwheel-bridge.js b/figwheel-bridge.js index ecbd394e92..6541d83bbc 100644 --- a/figwheel-bridge.js +++ b/figwheel-bridge.js @@ -78,7 +78,7 @@ var figwheelApp = function (platform, devHost) { ); } - return this.state.root; + return React.createElement(this.state.root, this.props); }, componentDidMount: function () { diff --git a/modules/react-native-status/desktop/rctstatus.cpp b/modules/react-native-status/desktop/rctstatus.cpp index acf3ca5304..bf865e60db 100644 --- a/modules/react-native-status/desktop/rctstatus.cpp +++ b/modules/react-native-status/desktop/rctstatus.cpp @@ -92,7 +92,15 @@ void RCTStatus::startNode(QString configString) { if (!relativeDataDirPath.startsWith("/")) relativeDataDirPath.prepend("/"); - QString rootDirPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + QString statusDataDir = qgetenv("STATUS_DATA_DIR"); + QString rootDirPath; + if (!statusDataDir.isEmpty()) { + rootDirPath = statusDataDir; + } + else { + rootDirPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + } + QDir rootDir(rootDirPath); QString absDataDirPath = rootDirPath + relativeDataDirPath; QDir dataDir(absDataDirPath); diff --git a/scripts/build-desktop.sh b/scripts/build-desktop.sh index 733df0a538..6062630ee3 100755 --- a/scripts/build-desktop.sh +++ b/scripts/build-desktop.sh @@ -227,7 +227,7 @@ function bundleWindows() { if [ -z $STATUSIM_WINDOWS_BASEIMAGE_ZIP ]; then STATUSIM_WINDOWS_BASEIMAGE_ZIP=./StatusIm-Windows-base-image.zip - [ -f $STATUSIM_WINDOWS_BASEIMAGE_ZIP ] || wget https://desktop-app-files.ams3.digitaloceanspaces.com/StatusIm-Windows-base-image.zip + [ -f $STATUSIM_WINDOWS_BASEIMAGE_ZIP ] || wget https://desktop-app-files.ams3.digitaloceanspaces.com/StatusIm-Windows-base-image_20181113.zip -O StatusIm-Windows-base-image.zip fi unzip "$STATUSIM_WINDOWS_BASEIMAGE_ZIP" -d Windows/ @@ -271,11 +271,11 @@ function bundleLinux() { echo "Creating AppImage..." pushd $WORKFOLDER - rm -rf StatusImAppImage + rm -rf StatusImAppImage* # TODO this needs to be fixed: status-react/issues/5378 if [ -z $STATUSIM_APPIMAGE ]; then STATUSIM_APPIMAGE=./StatusImAppImage.zip - [ -f $STATUSIM_APPIMAGE ] || wget https://desktop-app-files.ams3.digitaloceanspaces.com/StatusImAppImage.zip + [ -f $STATUSIM_APPIMAGE ] || wget https://desktop-app-files.ams3.digitaloceanspaces.com/StatusImAppImage_20181113.zip -O StatusImAppImage.zip fi unzip "$STATUSIM_APPIMAGE" -d . rm -rf AppDir @@ -351,7 +351,7 @@ function bundleMacOS() { pushd $WORKFOLDER rm -rf Status.app # TODO this needs to be fixed: status-react/issues/5378 - [ -f ./Status.app.zip ] || curl -L -o Status.app.zip https://desktop-app-files.ams3.digitaloceanspaces.com/Status.app.zip + [ -f ./Status.app.zip ] || curl -L -o Status.app.zip https://desktop-app-files.ams3.digitaloceanspaces.com/Status_20181113.app.zip echo -e "${GREEN}Downloading done.${NC}" echo "" unzip ./Status.app.zip diff --git a/src/status_im/android/core.cljs b/src/status_im/android/core.cljs index 7cd1ab694a..6cb0ca5885 100644 --- a/src/status_im/android/core.cljs +++ b/src/status_im/android/core.cljs @@ -17,7 +17,7 @@ (defn app-state-change-handler [state] (dispatch [:app-state-change state])) -(defn app-root [] +(defn app-root [props] (let [keyboard-height (subscribe [:get :keyboard-height])] (reagent/create-class {:component-will-mount @@ -39,7 +39,8 @@ (.hide react/splash-screen) (.addEventListener react/app-state "change" app-state-change-handler)) :component-did-mount - (fn [] + (fn [this] + (dispatch [:set-initial-props (reagent/props this)]) ;; TODO(oskarth): Background click_action handler (notifications/init)) :component-will-unmount diff --git a/src/status_im/data_store/realm/core.cljs b/src/status_im/data_store/realm/core.cljs index 7db27f1fd7..948e9d96ac 100644 --- a/src/status_im/data_store/realm/core.cljs +++ b/src/status_im/data_store/realm/core.cljs @@ -2,6 +2,7 @@ (:require [goog.object :as object] [goog.string :as gstr] [clojure.string :as string] + [re-frame.core :as re-frame] [status-im.data-store.realm.schemas.account.core :as account] [status-im.data-store.realm.schemas.base.core :as base] [taoensso.timbre :as log] @@ -65,29 +66,40 @@ (def old-base-realm-path (.-defaultPath rn-dependencies/realm)) -(def realm-dir +(defn realm-dir [] + "This has to be a fn because otherwise re-frame app-db is not + initialized yet" (if-let [path (utils.platform/no-backup-directory)] (str path "/realm/") - (.-defaultPath rn-dependencies/realm))) + (let [initial-props @(re-frame/subscribe [:initial-props]) + status-data-dir (get initial-props :STATUS_DATA_DIR)] + (cond-> (if status-data-dir + (str status-data-dir "/default.realm") + (.-defaultPath rn-dependencies/realm)) + utils.platform/desktop? + (str "/"))))) (def old-realm-dir (string/replace old-base-realm-path #"default\.realm$" "")) -(def accounts-realm-dir - (str realm-dir "accounts/")) +(defn accounts-realm-dir [] + (str (realm-dir) "accounts/")) -(def base-realm-path - (str realm-dir - "default.realm")) +(defn base-realm-path [] + (str (realm-dir) "default.realm")) + +(defn get-account-db-path + [address] + (str (accounts-realm-dir) (utils.ethereum/sha3 address))) (defn delete-realms [] (log/warn "realm: deleting all realms") - (fs/unlink realm-dir)) + (fs/unlink (realm-dir))) (defn delete-account-realm [address] (log/warn "realm: deleting account db " (utils.ethereum/sha3 address)) - (let [file (str accounts-realm-dir (utils.ethereum/sha3 address))] + (let [file (get-account-db-path address)] (.. (fs/unlink file) (then #(fs/unlink (str file ".lock"))) (then #(fs/unlink (str file ".management"))) @@ -95,14 +107,14 @@ (defn ensure-directories [] (.. - (fs/mkdir realm-dir) - (then #(fs/mkdir accounts-realm-dir)))) + (fs/mkdir (realm-dir)) + (then #(fs/mkdir (accounts-realm-dir))))) (defn- move-realm-to-library [path] (let [filename (last (string/split path "/")) new-path (if (is-account-file? path) - (str accounts-realm-dir (utils.ethereum/sha3 filename)) - (str realm-dir filename))] + (get-account-db-path filename) + (str (realm-dir) filename))] (log/debug "realm: moving " path " to " new-path) (if (realm-management-file? path) (fs/unlink path) @@ -177,7 +189,7 @@ (log/debug "Opening base realm... (first run)") (when @base-realm (close @base-realm)) - (reset! base-realm (migrate-realm base-realm-path base/schemas encryption-key)) + (reset! base-realm (migrate-realm (base-realm-path) base/schemas encryption-key)) (log/debug "Created @base-realm")) (defn re-encrypt-realm @@ -212,10 +224,6 @@ (on-error {:error :write-copy-to-failed :message message}))))))) -(defn get-account-db-path - [address] - (str accounts-realm-dir (utils.ethereum/sha3 address))) - (defn check-db-encryption [address password old-key] (let [file-name (get-account-db-path address) diff --git a/src/status_im/desktop/core.cljs b/src/status_im/desktop/core.cljs index 28a7a487dc..7a53916b48 100644 --- a/src/status_im/desktop/core.cljs +++ b/src/status_im/desktop/core.cljs @@ -1,18 +1,23 @@ (ns status-im.desktop.core (:require [reagent.core :as reagent] + [re-frame.core :as re-frame] status-im.utils.db status-im.ui.screens.db status-im.ui.screens.events status-im.ui.screens.subs status-im.data-store.core + [reagent.impl.component :as reagent.component] [status-im.ui.screens.desktop.views :as views] [status-im.core :as core] [status-im.desktop.deep-links :as deep-links])) -(defn app-root [] +(defn app-root [props] (reagent/create-class - {:component-did-mount deep-links/add-event-listener - :reagent-render views/main})) + {:component-did-mount (fn [this] + (re-frame/dispatch [:set-initial-props (reagent/props this)]) + (deep-links/add-event-listener)) + :reagent-render (fn [props] + views/main)})) (defn init [] (core/init app-root)) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index ee2e77411a..1e0211ad83 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -1169,6 +1169,11 @@ (fn [cofx _] (pairing/send-installation-messages cofx))) +(handlers/register-handler-fx + :set-initial-props + (fn [cofx [_ initial-props]] + {:db (assoc (:db cofx) :initial-props initial-props)})) + (handlers/register-handler-fx :pairing.ui/enable-installation-pressed (fn [cofx [_ installation-id]] diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index 92bbca456e..beb8efe1bf 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -78,11 +78,13 @@ (fx/defn initialize-app-db "Initialize db to initial state" [{{:keys [status-module-initialized? view-id hardwallet + initial-props network-status network peers-count peers-summary device-UUID] :node/keys [status] :or {network (get app-db :network)}} :db}] {:db (assoc app-db :contacts/contacts {} + :initial-props initial-props :network-status network-status :peers-count (or peers-count 0) :peers-summary (or peers-summary []) diff --git a/src/status_im/ios/core.cljs b/src/status_im/ios/core.cljs index 4d800c19c3..d2a65754c1 100644 --- a/src/status_im/ios/core.cljs +++ b/src/status_im/ios/core.cljs @@ -15,7 +15,7 @@ (defn app-state-change-handler [state] (dispatch [:app-state-change state])) -(defn app-root [] +(defn app-root [props] (let [keyboard-height (subscribe [:get :keyboard-height])] (reagent/create-class {:component-will-mount @@ -34,7 +34,8 @@ (.hide react/splash-screen) (.addEventListener react/app-state "change" app-state-change-handler)) :component-did-mount - (fn [] + (fn [this] + (dispatch [:set-initial-props (reagent/props this)]) (notifications/init)) :component-will-unmount (fn [] diff --git a/src/status_im/node/core.cljs b/src/status_im/node/core.cljs index c3284e8f20..6b2c598d75 100644 --- a/src/status_im/node/core.cljs +++ b/src/status_im/node/core.cljs @@ -54,12 +54,16 @@ (get-in db [:accounts/accounts address :network])) (defn- get-base-node-config [config] - (cond-> (assoc config - :Name "StatusIM" - :BackupDisabledDataDir (utils.platform/no-backup-directory)) - config/dev-build? - (assoc :ListenAddr ":30304" - :DataDir (str (:DataDir config) "_dev")))) + (let [initial-props @(re-frame/subscribe [:initial-props]) + status-node-port (get initial-props :STATUS_NODE_PORT)] + (cond-> (assoc config + :Name "StatusIM" + :BackupDisabledDataDir (utils.platform/no-backup-directory)) + config/dev-build? + (assoc :ListenAddr ":30304" + :DataDir (str (:DataDir config) "_dev")) + status-node-port + (assoc :ListenAddr (str ":" status-node-port))))) (defn- pick-nodes "Picks `limit` different nodes randomly from the list of nodes diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index f795c1e3dd..f81d2ed38a 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -57,6 +57,7 @@ :chat/last-outgoing-message-sent-at 0 :chat/spam-messages-frequency 0 :tooltips {} + :initial-props {} :desktop/desktop {:tab-view-id :home} :dimensions/window (dimensions/window) :push-notifications/stored {} @@ -148,6 +149,7 @@ (spec/def :desktop/desktop (spec/nilable any?)) (spec/def ::tooltips (spec/nilable any?)) +(spec/def ::initial-props (spec/nilable any?)) ;;;;NETWORK @@ -254,6 +256,7 @@ ::was-modal? ::rpc-url ::tooltips + ::initial-props ::web3 ::web3-node-version ::webview-bridge diff --git a/src/status_im/ui/screens/subs.cljs b/src/status_im/ui/screens/subs.cljs index 8236ec873e..12cbe056b1 100644 --- a/src/status_im/ui/screens/subs.cljs +++ b/src/status_im/ui/screens/subs.cljs @@ -91,3 +91,7 @@ (reg-sub :dimensions/window-width :<- [:dimensions/window] :width) + +(reg-sub :initial-props + (fn [db _] + (get db :initial-props))) diff --git a/ubuntu-server.js b/ubuntu-server.js index c7b3ba4228..12ad964ce8 100755 --- a/ubuntu-server.js +++ b/ubuntu-server.js @@ -119,9 +119,9 @@ if (process.argv.indexOf('--pipe') != -1) { rnUbuntuServer(process.stdin, process.stdout); } else { var port = process.env['REACT_SERVER_PORT'] || 5000; - process.argv.forEach((val) => { - if (val.indexOf('--port') != -1) { - port = val.substring(7); + process.argv.forEach((val, index) => { + if (val == '--port') { + port = process.argv[++index]; } });