Desktop branch merged into develop (#5266)

* Desktop branch merged into develop
* Fixed review notes by yenda
This commit is contained in:
Volodymyr Kozieiev 2018-07-23 18:21:31 +03:00 committed by GitHub
parent bdc7284308
commit 457f2a157a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
88 changed files with 17296 additions and 2561 deletions

1
.gitattributes vendored
View File

@ -1 +1,2 @@
*.pbxproj -text
*.patch eol=lf

5
.gitignore vendored
View File

@ -108,3 +108,8 @@ fastlane/README.md
# emacs
.dir-locals.el
#ignore platform-specific files in the root since they are only symlinks to files in folders 'desktop_files' and 'mobile_files'
/VERSION
/package-lock.json
/package.json
/.re-natal

1
Jenkinsfile vendored
View File

@ -9,6 +9,7 @@ def installJSDeps() {
def installed = false
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh mobile'
sh 'npm install'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1

197
Jenkinsfile.desktopbuild Normal file
View File

@ -0,0 +1,197 @@
env.LANG="en_US.UTF-8"
env.LANGUAGE="en_US.UTF-8"
env.LC_ALL="en_US.UTF-8"
def installJSDeps() {
def attempt = 1
def maxAttempts = 10
def installed = false
sh 'node -v'
sh 'npm -v'
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh desktop'
sh 'npm install --verbose'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1
}
}
def doGitRebase() {
try {
sh 'git rebase origin/develop'
} catch (e) {
sh 'git rebase --abort'
throw e
}
}
def cleanupBuild(packageFolder) {
sh 'rm -rf node_modules'
sh ( 'rm -rf ' + packageFolder )
sh 'rm -rf desktop/modules'
sh 'rm -rf desktop/node_modules'
}
parallel (
"MacOS parallel build stream" : {
timeout(90) {
node ('macos1') {
def apkUrl = ''
def ipaUrl = ''
def testPassed = true
def branch;
def scriptOutput = ''
def packageFolder = './StatusImPackage'
def scriptPath = sh(script: 'pwd -P', returnStdout: true).trim()
load "$HOME/env.groovy"
try {
stage('Git & Dependencies') {
slackSend channel: '#jenkins-desktop', color: 'good', message: BRANCH_NAME + '(' + env.CHANGE_BRANCH + ') MacOS build started. ' + env.BUILD_URL
sh ('echo ' + scriptPath)
checkout scm
doGitRebase()
cleanupBuild(packageFolder)
sh 'cp .env.jenkins .env'
sh 'lein deps'
installJSDeps()
}
stage('Build ClojureScript') {
sh 'rm -f index.desktop.js'
sh 'lein prod-build-desktop'
sh ( 'mkdir ' + packageFolder )
sh ( 'react-native bundle --entry-file index.desktop.js --bundle-output ' + packageFolder + '/StatusIm.jsbundle --dev false --platform desktop --assets-dest ' + packageFolder + '/assets' )
}
stage('Build MacOS binaries') {
sh 'cd desktop && rm -rf CMakeFiles CMakeCache.txt cmake_install.cmake Makefile'
sh 'export PATH=/Users/administrator/qt/5.9.1/clang_64/bin:$PATH && cd desktop && cmake -DCMAKE_BUILD_TYPE=Release -DEXTERNAL_MODULES_DIR="node_modules/react-native-i18n/desktop;node_modules/react-native-config/desktop;node_modules/react-native-fs/desktop;node_modules/react-native-http-bridge/desktop;node_modules/react-native-webview-bridge/desktop;node_modules/react-native-keychain/desktop;node_modules/react-native-securerandom/desktop;modules/react-native-status/desktop"\
-DJS_BUNDLE_PATH="' + scriptPath + '/' + packageFolder + '/StatusIm.jsbundle" -DCMAKE_CXX_FLAGS:="-DBUILD_FOR_BUNDLE=1" . && make'
}
stage('Prepare and create MacOS Bundle') {
sh ('cd ' + packageFolder + ' && ../scripts/download-package-files.sh "StatusIm.app.zip" "1Vkb6MD3nsmT02Az6rRRZywQSwCz1ZN9V" && unzip ./StatusIm.app.zip')
sh ('cp -r ' + packageFolder + '/assets/share/assets ' + packageFolder +'/StatusIm.app/Contents/MacOs')
sh ('chmod +x ' + packageFolder + '/StatusIm.app/Contents/MacOs/ubuntu-server')
sh ('cp ./desktop/bin/StatusIm ' + packageFolder +'/StatusIm.app/Contents/MacOs')
sh ('export PATH=/Users/administrator/qt/5.9.1/clang_64/bin:$PATH && cd ' + packageFolder + ' && macdeployqt StatusIm.app -verbose=1 -dmg -qmldir="' + scriptPath + '/node_modules/react-native/ReactQt/runtime/src/qml/"')
sh 'rm -f StatusIm.app.zip'
}
stage('Archive built artifact') {
archiveArtifacts "StatusImPackage/*.dmg"
}
cleanupBuild(packageFolder)
slackSend channel: '#jenkins-desktop', color: 'good', message: BRANCH_NAME + '(' + env.CHANGE_BRANCH + ') MacOS build finished successfully. ' + env.BUILD_URL
} catch (e) {
cleanupBuild(packageFolder)
slackSend channel: '#jenkins-desktop', color: 'bad', message: BRANCH_NAME + ' failed to build on MacOS. ' + env.BUILD_URL
throw e
}
}
}
},
"Linux parallel build stream" : {
timeout(90) {
node ('linux1') {
def apkUrl = ''
def ipaUrl = ''
def testPassed = true
def branch;
def scriptOutput = ''
def packageFolder = './StatusImPackage'
def scriptPath = sh(script: 'pwd -P', returnStdout: true).trim()
sh ('echo ' + scriptPath)
try {
stage('Git & Dependencies') {
slackSend channel: '#jenkins-desktop', color: 'good', message: BRANCH_NAME + '(' + env.CHANGE_BRANCH + ') Linux build started. ' + env.BUILD_URL
sh ('echo ' + scriptPath)
checkout scm
doGitRebase()
cleanupBuild(packageFolder)
sh 'cp .env.jenkins .env'
sh 'lein deps'
installJSDeps()
}
stage('Build ClojureScript') {
sh 'rm -f index.desktop.js'
sh 'lein prod-build-desktop'
sh ( 'mkdir ' + packageFolder )
sh ( 'react-native bundle --entry-file index.desktop.js --bundle-output ' + packageFolder + '/StatusIm.jsbundle --dev false --platform desktop --assets-dest ' + packageFolder + '/assets' )
}
stage('Build Linux binaries') {
sh 'cd desktop && rm -rf CMakeFiles CMakeCache.txt cmake_install.cmake Makefile'
sh 'export PATH=/home/maxr/Qt5.9.1/5.9.1/gcc_64/bin:/usr/local/go/bin:$PATH && cd desktop && cmake -DCMAKE_BUILD_TYPE=Release -DEXTERNAL_MODULES_DIR="node_modules/react-native-i18n/desktop;node_modules/react-native-config/desktop;node_modules/react-native-fs/desktop;node_modules/react-native-http-bridge/desktop;node_modules/react-native-webview-bridge/desktop;node_modules/react-native-keychain/desktop;node_modules/react-native-securerandom/desktop;modules/react-native-status/desktop"\
-DJS_BUNDLE_PATH="' + scriptPath + '/' + packageFolder + '/StatusIm.jsbundle" -DCMAKE_CXX_FLAGS:="-DBUILD_FOR_BUNDLE=1" . && make'
}
stage('Prepare and create Linux AppImage') {
sh ('rm -rf ' + packageFolder + '/StatusImAppImage')
sh ('cd ' + packageFolder + ' && cp /home/maxr/qttools/StatusImAppImage.zip ./ && unzip ./StatusImAppImage.zip')
sh ('rm -rf ' + packageFolder + '/AppDir && mkdir ' + packageFolder + '/AppDir')
sh ('cp -r ./deployment/linux/usr ' + packageFolder + '/AppDir')
sh ('cp ./deployment/linux/.env ' + packageFolder + '/AppDir')
sh ('cp ./desktop/bin/StatusIm ' + packageFolder+ '/AppDir/usr/bin')
sh ('cp -f /home/maxr/qttools/linuxdeployqt-continuous-x86_64.AppImage ./')
sh ('chmod a+x ./linuxdeployqt-continuous-x86_64.AppImage')
sh 'rm -f Application-x86_64.AppImage'
sh 'rm -f StatusIm-x86_64.AppImage'
sh 'ldd ' + packageFolder+ '/AppDir/usr/bin/StatusIm'
sh ('export PATH=/home/maxr/Qt5.9.1/5.9.1/gcc_64/bin:/usr/local/go/bin:$PATH && ./linuxdeployqt-continuous-x86_64.AppImage ' + packageFolder+ '/AppDir/usr/share/applications/StatusIm.desktop -verbose=3 -always-overwrite -no-strip -no-translations -bundle-non-qt-libs -qmake=/home/maxr/Qt5.9.1/5.9.1/gcc_64/bin/qmake -extra-plugins=imageformats/libqsvg.so -qmldir="' + scriptPath + '/node_modules/react-native"')
sh 'ldd ' + packageFolder+ '/AppDir/usr/bin/StatusIm'
sh ('cp -r ' + packageFolder + '/assets/share/assets ' + packageFolder +'/AppDir/usr/bin')
sh ('cp -rf ' + packageFolder + '/StatusImAppImage/* ' + packageFolder +'/AppDir/usr/bin')
sh ('rm -f ' + packageFolder +'/AppDir/usr/bin/StatusIm.AppImage')
sh ('export PATH=/home/maxr/Qt5.9.1/5.9.1/gcc_64/bin:/usr/local/go/bin:$PATH && ./linuxdeployqt-continuous-x86_64.AppImage ' + packageFolder+ '/AppDir/usr/share/applications/StatusIm.desktop -verbose=3 -appimage -qmake=/home/maxr/Qt5.9.1/5.9.1/gcc_64/bin/qmake')
sh 'ldd ' + packageFolder+ '/AppDir/usr/bin/StatusIm'
sh ('rm -rf ' + packageFolder +'/StatusIm.AppImage')
sh ('cp -f ./StatusIm-x86_64.AppImage ' + packageFolder + '/StatusIm.AppImage')
}
stage('Archive built artifact') {
archiveArtifacts "StatusImPackage/*.AppImage"
}
cleanupBuild(packageFolder)
slackSend channel: '#jenkins-desktop', color: 'good', message: BRANCH_NAME + '(' + env.CHANGE_BRANCH + ') Linux build finished successfully. ' + env.BUILD_URL
} catch (e) {
cleanupBuild(packageFolder)
slackSend channel: '#jenkins-desktop', color: 'bad', message: BRANCH_NAME + ' failed to build on Linux. ' + env.BUILD_URL
throw e
}
}
}
}
)

View File

@ -12,6 +12,7 @@ def installJSDeps() {
def installed = false
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh mobile'
sh 'npm install'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1

View File

@ -12,6 +12,7 @@ def installJSDeps() {
def installed = false
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh mobile'
sh 'npm install'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1

View File

@ -9,6 +9,7 @@ def installJSDeps() {
def installed = false
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh mobile'
sh 'npm install'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1

View File

@ -12,6 +12,7 @@ def installJSDeps() {
def installed = false
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh mobile'
sh 'npm install'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1

View File

@ -12,6 +12,7 @@ def installJSDeps() {
def installed = false
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh mobile'
sh 'npm install'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1

View File

@ -12,6 +12,7 @@ def installJSDeps() {
def installed = false
while (!installed && attempt <= maxAttempts) {
println "#${attempt} attempt to install npm deps"
sh 'scripts/prepare-for-platform.sh mobile'
sh 'npm install'
installed = fileExists('node_modules/web3/index.js')
attemp = attempt + 1

View File

@ -30,8 +30,10 @@ setup: ##@prepare Install all the requirements for status-react
./scripts/setup
prepare: ##@prepare Install dependencies and prepare workspace
scripts/prepare-for-platform.sh mobile
npm install
prepare-ios: prepare ##@prepare Install iOS specific dependencies
mvn -f modules/react-native-status/ios/RCTStatus dependency:unpack
cd ios && pod install && cd ..

View File

@ -8,7 +8,7 @@
(def cljsbuild-config
{:dev
{:ios
{:source-paths ["components/src" "react-native/src" "src"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src"]
:compiler {:output-to "target/ios/app.js"
:main "env.ios.main"
:output-dir "target/ios"
@ -16,7 +16,7 @@
:optimizations :none}
:warning-handlers '[status-im.utils.build/warning-handler]}
:android
{:source-paths ["components/src" "react-native/src" "src"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src"]
:compiler {:output-to "target/android/app.js"
:main "env.android.main"
:output-dir "target/android"
@ -26,7 +26,7 @@
:prod
{:ios
{:source-paths ["components/src" "react-native/src" "src" "env/prod"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src" "env/prod"]
:compiler {:output-to "index.ios.js"
:output-dir "target/ios-prod"
:static-fns true
@ -38,7 +38,7 @@
:language-in :ecmascript5}
:warning-handlers '[status-im.utils.build/warning-handler]}
:android
{:source-paths ["components/src" "react-native/src" "src" "env/prod"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src" "env/prod"]
:compiler {:output-to "index.android.js"
:output-dir "target/android-prod"
:static-fns true

View File

@ -36,7 +36,7 @@
(def view (get-class "View"))
(def safe-area-view (get-class "SafeAreaView"))
(def status-bar (get-class "StatusBar"))
(def status-bar (get-class (if platform/desktop? "View" "StatusBar")))
(def scroll-view (get-class "ScrollView"))
(def web-view (get-class "WebView"))

19
deployment/linux/.env Normal file
View File

@ -0,0 +1,19 @@
TESTFAIRY_ENABLED=0
STUB_STATUS_GO=0
ETHEREUM_DEV_CLUSTER=1
MAINNET_NETWORKS_ENABLED=1
OFFLINE_INBOX_ENABLED=1
OFFLINE_INBOX_MANY_ENABLED=1
LOG_LEVEL=debug
LOG_LEVEL_STATUS_GO=info
JSC_ENABLED=1
QUEUE_MESSAGE_ENABLED=1
MANY_WHISPER_TOPICS_ENABLED=0
RN_BRIDGE_THRESHOLD_WARNINGS=0
COMPILE_VIEWS_ENABLED=0
POW_TARGET=0.002
POW_TIME=1
MIXPANEL_TOKEN=3f2e1a8970f159aa2a3d5dc5d65eab38
DEFAULT_NETWORK=mainnet_rpc
TESTFAIRY_TOKEN=969f6c921cb435cea1d41d1ea3f5b247d6026d55
INSTABUG_TOKEN=758630ed52864cbad9c5eeeac596c60c

View File

View File

View File

@ -0,0 +1,7 @@
[Desktop Entry]
Type=Application
Name=StatusIm
Comment=StatusIm Desktop
Exec=StatusIm
Icon=StatusIm
Categories=Network;

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -1,4 +1,4 @@
{:paths ["components/src" "src" "react-native/src" "resources"]
{:paths ["components/src" "src" "react-native/src/cljsjs" "react-native/src/mobile" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.9.0"}
org.clojure/clojurescript {:mvn/version "1.10.238"}
org.clojure/core.async {:mvn/version "0.4.474"}

39
desktop/CMakeLists.txt Normal file
View File

@ -0,0 +1,39 @@
# Copyright (C) 2016, Canonical Ltd.
# All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
cmake_minimum_required(VERSION 2.8.11)
set(APP_NAME StatusIm)
set(REACT_BUILD_STATIC_LIB ON)
message(STATUS "EXTERNAL_MODULES_DIR: ${EXTERNAL_MODULES_DIR}")
foreach(external_module ${EXTERNAL_MODULES_DIR})
message(STATUS "external_module: ${external_module}")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../${external_module} ${CMAKE_CURRENT_BINARY_DIR}/${external_module})
endforeach(external_module)
# APPLICATION_MAIN_CPP_PATH contains absolute path to generated template copy of main.cpp for application executable
get_filename_component(APPLICATION_MAIN_CPP_PATH main.cpp ABSOLUTE)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../node_modules/react-native/React/Layout)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../node_modules/react-native/ReactQt/runtime/src ${CMAKE_CURRENT_BINARY_DIR}/lib)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../node_modules/react-native/ReactQt/application/src ${CMAKE_CURRENT_BINARY_DIR}/bin)
if (WIN32)
set(RUN_SCRIPT_FILE_NAME "run-app.bat")
else()
set(RUN_SCRIPT_FILE_NAME "run-app.sh")
endif()
configure_file(
${RUN_SCRIPT_FILE_NAME}.in
${CMAKE_CURRENT_BINARY_DIR}/${RUN_SCRIPT_FILE_NAME}
@ONLY
)

34
desktop/build.bat Normal file
View File

@ -0,0 +1,34 @@
@rem Copyright (c) 2017-present, Status Research and Development GmbH.
@rem All rights reserved.
@rem
@rem This source code is licensed under the BSD-style license found in the
@rem LICENSE file in the root directory of this source tree. An additional grant
@rem of patent rights can be found in the PATENTS file in the same directory.
@echo off
setlocal EnableDelayedExpansion
set "option="
for %%a in (%*) do (
if not defined option (
set arg=%%a
if "!arg:~0,1!" equ "-" set "option=!arg!"
) else (
set "option!option!=%%a"
set "option="
)
)
SET option
@echo on
echo "build.bat external modules paths: "%option-e%
echo "build.bat JS bundle path: "%option-j%
echo "build.bat cmake generator: "%option-g%
@rem Workaround
@rem rm -rf CMakeFiles CMakeCache.txt cmake_install.cmake Makefile
@rem Build project
echo %CD%
cmake -DCMAKE_BUILD_TYPE=Debug -G %option-g% -DEXTERNAL_MODULES_DIR=%option-e% -DJS_BUNDLE_PATH=%option-j% . && cmake --build .

32
desktop/build.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
# Copyright (C) 2016, Canonical Ltd.
# All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
# XXX: Don't move this script
cd $(dirname $0)
while (( "$#" )); do
if [[ $1 == "-e" ]]; then
shift
ExternalModulesPaths="$1"
fi
if [[ $1 == "-j" ]]; then
shift
JsBundlePath="$1"
fi
shift
done
echo "build.sh external modules paths: "$ExternalModulesPaths
echo "build.sh JS bundle path: "$JsBundlePath
# Workaround
rm -rf CMakeFiles CMakeCache.txt cmake_install.cmake Makefile
# Build project
cmake -DCMAKE_BUILD_TYPE=Debug -DEXTERNAL_MODULES_DIR="$ExternalModulesPaths" -DJS_BUNDLE_PATH="$JsBundlePath" . && make

284
desktop/main.cpp Normal file
View File

@ -0,0 +1,284 @@
/**
* Copyright (C) 2016, Canonical Ltd.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
// #define BUILD_FOR_BUNDLE
#include <QCommandLineParser>
#include <QFile>
#include <QGuiApplication>
#include <QProcess>
#include <QQuickView>
#include <QTimer>
#include <QUrl>
#include <QStandardPaths>
#include "attachedproperties.h"
#include "reactitem.h"
#include "rootview.h"
#include "utilities.h"
#ifdef BUILD_FOR_BUNDLE
QStringList consoleOutputStrings;
bool ubuntuServerStarted = false;
#endif
// TODO: some way to change while running
class ReactNativeProperties : public QObject {
Q_OBJECT
Q_PROPERTY(bool liveReload READ liveReload WRITE setLiveReload NOTIFY
liveReloadChanged)
Q_PROPERTY(QUrl codeLocation READ codeLocation WRITE setCodeLocation NOTIFY
codeLocationChanged)
Q_PROPERTY(QString pluginsPath READ pluginsPath WRITE setPluginsPath NOTIFY
pluginsPathChanged)
Q_PROPERTY(
QString executor READ executor WRITE setExecutor NOTIFY executorChanged)
public:
ReactNativeProperties(QObject *parent = 0) : QObject(parent) {
m_codeLocation = m_packagerTemplate.arg(m_packagerHost).arg(m_packagerPort);
}
bool liveReload() const { return m_liveReload; }
void setLiveReload(bool liveReload) {
if (m_liveReload == liveReload)
return;
m_liveReload = liveReload;
Q_EMIT liveReloadChanged();
}
QUrl codeLocation() const { return m_codeLocation; }
void setCodeLocation(const QUrl &codeLocation) {
if (m_codeLocation == codeLocation)
return;
m_codeLocation = codeLocation;
Q_EMIT codeLocationChanged();
}
QString pluginsPath() const { return m_pluginsPath; }
void setPluginsPath(const QString &pluginsPath) {
if (m_pluginsPath == pluginsPath)
return;
m_pluginsPath = pluginsPath;
Q_EMIT pluginsPathChanged();
}
QString executor() const { return m_executor; }
void setExecutor(const QString &executor) {
if (m_executor == executor)
return;
m_executor = executor;
Q_EMIT executorChanged();
}
QString packagerHost() const { return m_packagerHost; }
void setPackagerHost(const QString &packagerHost) {
if (m_packagerHost == packagerHost)
return;
m_packagerHost = packagerHost;
setCodeLocation(m_packagerTemplate.arg(m_packagerHost).arg(m_packagerPort));
}
QString packagerPort() const { return m_packagerPort; }
void setPackagerPort(const QString &packagerPort) {
if (m_packagerPort == packagerPort)
return;
m_packagerPort = packagerPort;
setCodeLocation(m_packagerTemplate.arg(m_packagerHost).arg(m_packagerPort));
}
void setLocalSource(const QString &source) {
if (m_localSource == source)
return;
// overrides packager*
if (source.startsWith("file:")) {
setCodeLocation(source);
} else {
QFileInfo fi(source);
if (!fi.exists()) {
qWarning() << "Attempt to set non-existent local source file";
return;
}
setCodeLocation(QUrl::fromLocalFile(fi.absoluteFilePath()));
setLiveReload(false);
}
}
Q_SIGNALS:
void liveReloadChanged();
void codeLocationChanged();
void pluginsPathChanged();
void executorChanged();
private:
bool m_liveReload = false;
QString m_packagerHost = "localhost";
QString m_packagerPort = "8081";
QString m_localSource;
QString m_packagerTemplate =
"http://%1:%2/index.desktop.bundle?platform=desktop&dev=true";
QUrl m_codeLocation;
QString m_pluginsPath;
#ifdef BUILD_FOR_BUNDLE
QString m_executor = "RemoteServerConnection";
#else
QString m_executor = "LocalServerConnection";
#endif
};
#ifdef BUILD_FOR_BUNDLE
void runUbuntuServer();
void saveMessage(QtMsgType type, const QMessageLogContext &context,
const QString &msg);
void writeLogsToFile();
#endif
int main(int argc, char **argv) {
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Q_INIT_RESOURCE(react_resources);
#ifdef BUILD_FOR_BUNDLE
QString dataFolder = QDir::homePath() + "/Library/StatusIm/";
qInstallMessageHandler(saveMessage);
QDir dir(dataFolder + "ethereum/mainnet_rpc");
if (!dir.exists()) {
dir.mkpath(".");
}
runUbuntuServer();
#endif
QQuickView view;
ReactNativeProperties *rnp = new ReactNativeProperties(&view);
#ifdef BUILD_FOR_BUNDLE
rnp->setCodeLocation("file:" + QGuiApplication::applicationDirPath() +
"/assets");
#endif
utilities::registerReactTypes();
QCommandLineParser p;
p.setApplicationDescription("React Native host application");
p.addHelpOption();
p.addOptions({
{{"R", "live-reload"}, "Enable live reload."},
{{"H", "host"}, "Set packager host address.", rnp->packagerHost()},
{{"P", "port"}, "Set packager port number.", rnp->packagerPort()},
{{"L", "local"}, "Set path to the local packaged source", "not set"},
{{"M", "plugins-path"}, "Set path to node modules", "./plugins"},
{{"E", "executor"}, "Set Javascript executor", rnp->executor()},
});
p.process(app);
rnp->setLiveReload(p.isSet("live-reload"));
if (p.isSet("host"))
rnp->setPackagerHost(p.value("host"));
if (p.isSet("port"))
rnp->setPackagerPort(p.value("port"));
if (p.isSet("local"))
rnp->setLocalSource(p.value("local"));
if (p.isSet("plugins-path"))
rnp->setPluginsPath(p.value("plugins-path"));
if (p.isSet("executor"))
rnp->setExecutor(p.value("executor"));
view.rootContext()->setContextProperty("ReactNativeProperties", rnp);
view.setSource(QUrl("qrc:///main.qml"));
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.show();
#ifdef BUILD_FOR_BUNDLE
QTimer t;
t.setInterval(500);
QObject::connect(&t, &QTimer::timeout, [=]() { writeLogsToFile(); });
t.start();
#endif
return app.exec();
}
#ifdef BUILD_FOR_BUNDLE
void writeLogsToFile() {
QFile logFile(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/StatusIm.log");
if (logFile.open(QIODevice::WriteOnly | QIODevice::Append)) {
for (QString message : consoleOutputStrings) {
logFile.write(message.toStdString().c_str());
}
consoleOutputStrings.clear();
logFile.flush();
logFile.close();
}
}
void runUbuntuServer() {
QProcess *process = new QProcess();
process->setProgram(QGuiApplication::applicationDirPath() + "/ubuntu-server");
QObject::connect(process, &QProcess::errorOccurred,
[=](QProcess::ProcessError) {
qDebug() << "process name: " << process->program();
qDebug() << "process error: " << process->errorString();
});
QObject::connect(process, &QProcess::readyReadStandardOutput, [=] {
qDebug() << "ubuntu-server std: "
<< process->readAllStandardOutput().trimmed();
});
QObject::connect(process, &QProcess::readyReadStandardError, [=] {
QString output = process->readAllStandardError().trimmed();
qDebug() << "ubuntu-server err: " << output;
if (output.contains("Server starting")) {
ubuntuServerStarted = true;
}
});
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit,
[=]() {
qDebug() << "Kill ubuntu server";
process->kill();
});
qDebug() << "starting ubuntu server...";
process->start();
qDebug() << "wait for started...";
while (!ubuntuServerStarted) {
QGuiApplication::processEvents();
}
qDebug() << "waiting finished";
}
void saveMessage(QtMsgType type, const QMessageLogContext &context,
const QString &msg) {
QByteArray localMsg = msg.toLocal8Bit();
QString message = localMsg + "\n";
switch (type) {
case QtDebugMsg:
consoleOutputStrings << "Debug: " << message << "\n";
break;
case QtInfoMsg:
consoleOutputStrings << "Info: " << message << "\n";
break;
case QtWarningMsg:
consoleOutputStrings << "Warning: " << message << "\n";
break;
case QtCriticalMsg:
consoleOutputStrings << "Critical: " << message << "\n";
break;
case QtFatalMsg:
consoleOutputStrings << "Fatal: " << message << "\n";
writeLogsToFile();
abort();
}
}
#endif
#include "main.moc"

11
desktop/run-app.bat.in Normal file
View File

@ -0,0 +1,11 @@
@rem Copyright (c) 2017-present, Status Research and Development GmbH.
@rem All rights reserved.
@rem
@rem This source code is licensed under the BSD-style license found in the
@rem LICENSE file in the root directory of this source tree. An additional grant
@rem of patent rights can be found in the PATENTS file in the same directory.
@rem Run app locally
@CMAKE_BINARY_DIR@/bin/@APP_NAME@

73
desktop/run-app.sh.in Executable file
View File

@ -0,0 +1,73 @@
#!/bin/bash
# Copyright (C) 2016, Canonical Ltd.
# All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
args=""
on_device=0
plugins_path=""
asset_path="share"
executor=""
react_host=`hostname -I`
# Parse args
for arg in "$@"
do
IFS="=" read -a parts <<< "$arg"
if [[ $parts == "--on-device" ]]; then
on_device=1
elif [[ $parts == "--plugins-path" ]]; then
plugins_path=${parts[1]}
args=$args" --plugins-path=./plugins"
elif [[ $parts == "--asset-path" ]]; then
asset_path=${parts[1]}
elif [[ $parts == "--executor" ]]; then
if [[ $on_device == 1 ]]; then
# Force net executor for now
executor="ReactNetExecutor"
else
executor=${parts[1]}
fi
args=$args" --executor=$executor"
else
args=$args" $parts"
fi
done
# Handle defaults
if [[ -z "$executor" ]]; then
if [[ $on_device == 1 ]]; then
executor="ReactNetExecutor"
args=$args" --executor=ReactNetExecutor"
fi
# The RN application selects pipe executor by default
fi
# For net case, try and run executor; it is probably OK if this fails - it's
# just running elsewhere
if [[ "$executor" == "ReactNetExecutor" ]]; then
(node @CMAKE_BINARY_DIR@/bin/ubuntu-server.js 2>&1 > /dev/null) &
fi
if [[ $on_device == 1 ]]; then
app_path="/home/phablet/@APP_NAME@"
# Push binaries
adb push @CMAKE_BINARY_DIR@/bin/@APP_NAME@ "$app_path/@APP_NAME@"
[ -d "$plugins_path" ] && adb push "$plugins_path" "$app_path/plugins/"
[ -d "$asset_path" ] && adb push "$asset_path" "$app_path/share/"
# adb reverse --no-rebind tcp:8081 tcp:808
# Run app on device
adb shell "cd $app_path && REACT_SERVER_HOST=$react_host ./@APP_NAME@ --host $react_host $args -- --desktop_file_hint=/usr/share/applications/webbrowser-app.desktop"
else
# Run app locally
@CMAKE_BINARY_DIR@/bin/@APP_NAME@ $args
fi

70
desktop_files/.re-natal Normal file
View File

@ -0,0 +1,70 @@
{
"name": "StatusIm",
"interface": "reagent",
"platforms": {
"ios": {
"host": "localhost",
"modules": [
"react-native-image-resizer",
"react-native-camera",
"instabug-reactnative",
"nfc-react-native",
"react-native-background-timer",
"react-native-testfairy"
]
},
"android": {
"host": "10.0.2.2",
"modules": [
"react-native-image-resizer",
"react-native-camera",
"instabug-reactnative",
"nfc-react-native",
"react-native-background-timer",
"react-native-testfairy"
]
},
"desktop": {
"host": "localhost",
"modules": []
}
},
"modules": [
"realm",
"react-native-i18n",
"realm/react-native",
"dismissKeyboard",
"react-native-splash-screen",
"react-native-status",
"react-native-qrcode",
"identicon.js",
"react-native-fs",
"react-native-dialogs",
"react-native-image-crop-picker",
"react-native-securerandom",
"react-native-webview-bridge",
"react-native-fcm",
"homoglyph-finder",
"web3",
"chance",
"react-native-http-bridge",
"emojilib",
"react-native-config",
"react-native-svg",
"react-native-keychain",
"rn-snoopy",
"rn-snoopy/stream/bars",
"rn-snoopy/stream/filter",
"rn-snoopy/stream/buffer",
"react-native/Libraries/vendor/emitter/EventEmitter",
"react-native-fetch-polyfill"
],
"imageDirs": [
"resources/images",
"resources/icons"
],
"envRoots": {
"dev": "env/dev",
"prod": "env/prod"
}
}

1
desktop_files/VERSION Normal file
View File

@ -0,0 +1 @@
0.0.1

12602
desktop_files/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
{
"name": "StatusIm",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"prepare": "patch-package"
},
"desktopExternalModules": [
"node_modules/react-native-i18n/desktop",
"node_modules/react-native-config/desktop",
"node_modules/react-native-fs/desktop",
"node_modules/react-native-http-bridge/desktop",
"node_modules/react-native-webview-bridge/desktop",
"node_modules/react-native-keychain/desktop",
"node_modules/react-native-securerandom/desktop",
"modules/react-native-status/desktop"
],
"dependencies": {
"assert": "1.4.1",
"asyncstorage-down": "4.0.1",
"babel-core": "6.24.1",
"babel-generator": "6.24.1",
"babel-helper-builder-react-jsx": "6.18.0",
"babel-plugin-transform-es2015-block-scoping": "6.15.0",
"babel-preset-react-native": "4.0.0",
"babel-register": "6.18.0",
"bignumber.js": "github:status-im/bignumber.js#master",
"buffer": "3.6.0",
"chance": "1.0.12",
"create-react-class": "15.6.2",
"dns.js": "1.0.1",
"emojilib": "2.2.9",
"events": "1.1.1",
"homoglyph-finder": "1.1.1",
"identicon.js": "github:status-im/identicon.js",
"instabug-reactnative": "2.12.0",
"level-filesystem": "1.2.0",
"metro": "^0.30.2",
"nfc-react-native": "github:status-im/nfc-react-native",
"process": "0.11.10",
"prop-types": "15.6.0",
"punycode": "1.4.1",
"querystring-es3": "0.2.1",
"re-natal": "git+https://github.com/status-im/re-natal.git#master",
"react": "16.3.1",
"react-dom": "16.3.1",
"react-native": "git+https://github.com/status-im/react-native-desktop.git",
"react-native-background-timer": "2.0.0",
"react-native-camera": "0.10.0",
"react-native-config": "git+https://github.com/status-im/react-native-config.git",
"react-native-crypto": "2.1.1",
"react-native-dialogs": "0.0.20",
"react-native-fcm": "10.0.3",
"react-native-fetch-polyfill": "1.1.2",
"react-native-fs": "git+https://github.com/status-im/react-native-fs.git",
"react-native-http": "github:tradle/react-native-http#834492d",
"react-native-http-bridge": "git+https://github.com/status-im/react-native-http-bridge.git#desktop",
"react-native-i18n": "git+https://github.com/status-im/react-native-i18n.git#version_0.0.8_desktop",
"react-native-image-crop-picker": "0.18.1",
"react-native-image-resizer": "1.0.0",
"react-native-invertible-scroll-view": "1.1.0",
"react-native-keychain": "git+https://github.com/status-im/react-native-keychain.git",
"react-native-level-fs": "3.0.0",
"react-native-os": "1.1.0",
"react-native-qrcode": "0.2.6",
"react-native-securerandom": "git+https://github.com/status-im/react-native-securerandom.git",
"react-native-splash-screen": "3.0.6",
"react-native-svg": "6.3.1",
"react-native-tcp": "3.3.0",
"react-native-testfairy": "2.10.0",
"react-native-udp": "2.2.1",
"react-native-webview-bridge": "github:status-im/react-native-webview-bridge#react-native-0.49-desktop",
"realm": "git+https://github.com/status-im/realm-js.git",
"rn-snoopy": "github:status-im/rn-snoopy",
"string_decoder": "0.10.31",
"url": "0.10.3",
"web3": "github:status-im/web3.js#feature/shhext"
},
"devDependencies": {
"patch-package": "^5.1.1"
}
}

View File

@ -0,0 +1,60 @@
# Prerequisites:
lein, node.js v.8 , cmake, Qt 5.9.1 (with QtWebEngine components installed), Qt's qmake available in PATH
Note: add qmake to PATH via
`export PATH=<QT_PATH>/clang_64/bin:$PATH`
Caveats:
- if npm hangs at some step, check the version. If it's 5.6.0, try downgrading to 5.5.1 via `npm install -g npm@5.5.1`
# To install react-native-cli with desktop commands support:
1. git clone https://github.com/status-im/react-native-desktop.git
2. cd react-native-desktop/react-native-cli
3. npm update
4. npm install -g
# To setup re-natal dev builds of status-react for Desktop:
1. git clone https://github.com/status-im/status-react.git
2. cd status-react
3. git checkout desktop
4. npm install
5. lein deps
6. ./re-natal use-figwheel
7. ./re-natal enable-source-maps
8. In separate terminal tab: `npm start` (note: it starts react-native packager )
9. In separate terminal tab: node ./ubuntu-server.js
10. In separate terminal tab: lein figwheel-repl desktop (note: wait until sources compiled)
11. In separate terminal tab: react-native run-desktop
# Editor setup
Running `lein figwheel-repl desktop` will run a REPL on port 7888 by default. Some additional steps might be needed to connect to it.
## emacs-cider
In order to get REPL working, use the below elisp code:
```
(defun custom-cider-jack-in ()
(interactive)
(let ((status-desktop-params "with-profile +figwheel repl"))
(set-variable 'cider-lein-parameters status-desktop-params)
(message "setting 'cider-lein-parameters")
(cider-jack-in)))
(defun start-figwheel-cljs-repl ()
(interactive)
(set-buffer "*cider-repl status-react*")
(goto-char (point-max))
(insert "(do (use 'figwheel-api)
(start [:desktop])
(start-cljs-repl))")
(cider-repl-return))
```
`custom-cider-jack-in` sets the correct profile for leiningen, and can be run as soon as emacs is open.
run `start-figwheel-cljs-repl` once you already have a cider repl session from the jack-in
## vim-fireplace
For some reason there is no `.nrepl-port` file in project root, so `vim-fireplace` will not be able to connect automatically. You can either:
- run `:Connect` and answer a couple of interactive prompts
- create `.nrepl-port` file manually and add a single line containing `7888` (or whatever port REPL is running on)
After Figwheel has connected to the app, run `:Piggieback (figwheel-sidecar.repl-api/repl-env)` inside Vim, and you should be all set.

View File

@ -0,0 +1,136 @@
These are some common issues you may run into while setting up React Native Qt.
## Initial setup issues
### `npm install` hangs
Downgrade to version 5.5.1: `npm install -g npm@5.5.1`.
### `re-natal` missing
Create a link:
`ln -sf node_modules/re-natal/index.js re-natal`
### `react-native run desktop` complaining about missing `qmldir`:
```Command failed: ./build.sh -e "node_modules/react-native-i18n/desktop;node_modules/react-native-config/desktop;node_modules/react-native-fs/desktop;node_modules/react-native-http-bridge/desktop;node_modules/react-native-webview-bridge/desktop;modules/react-native-status/desktop"
Error copying directory from "/path-to-status-react/node_modules/react-native/ReactQt/runtime/src/qmldir" to "/path-to-status-react/desktop/lib/React".
make[2]: *** [lib/CMakeFiles/copy-qmldir] Error 1
make[1]: *** [lib/CMakeFiles/copy-qmldir.dir/all] Error 2
make: *** [all] Error 2
```
Can be solved by re-running `npm install react-native` which put the `ReactQt/runtime/src/qmldir` file back.
### Missing web3 package issue
After last upgrade of react-native-desktop to the v.0.53.3 of original react-native appeared some incompatibility between `react-native` and `web3` packages on npm install. Initially it installed usually fine, but after `react-native desktop` command execution `web3` package is get removed from `node_modules`. Manual install of web3 by `npm install web3` installs `web3` package, but removes `react-native` package. Workaround or solution?
### Go problem
```
panic: runloop has just unexpectedly stopped
goroutine 50 [running]:
github.com/status-im/status-go/vendor/github.com/rjeczalik/notify.init.0.func1()
/path-to-status-react/desktop/modules/react-native-status/desktop/StatusGo/src/github.com/status-im/status-go/vendor/github.com/rjeczalik/notify/watcher_fsevents_cgo.go:69 +0x79
created by github.com/status-im/status-go/vendor/github.com/rjeczalik/notify.init.0
/path-to-status-react/desktop/modules/react-native-status/desktop/StatusGo/src/github.com/status-im/status-go/vendor/github.com/rjeczalik/notify/watcher_fsevents_cgo.go:65 +0x4e
events.js:183
throw er; // Unhandled 'error' event
```
Related to https://github.com/rjeczalik/notify/issues/139. Solution: re-run.
## App issues
### Eth node crashing
`node ./ubuntu_server.js` log:
```
DEBUG [status-im.utils.handlers:36] - Handling re-frame event: :signal-event {"type":"node.crashed","event":{"error":"node is already running"}}
DEBUG [status-im.ui.screens.events:350] - :event-str {"type":"node.crashed","event":{"error":"node is already running"}}
DEBUG [status-im.utils.instabug:8] - Signal event: {"type":"node.crashed","event":{"error":"node is already running"}}
DEBUG [status-im.ui.screens.events:362] - Event node.crashed not handled
```
Solution: prevent starting Ethereum local node when there is an instance already running.
### Reload JS - blank screen
Console log for `react-native run-desktop` shows error 533.
Solution: reload again. Still, might hang at `Signing you in...` step (due to node attempted to be restarted). Re-run Figwheel and `react-native run-desktop`
### ReactButton.qml non-existent property "elide" error upon startup
```
qrc:/qml/ReactButton.qml:33: Error: Cannot assign to non-existent property "elide"
"Component for qrc:/qml/ReactWebView.qml is not loaded"
QQmlComponent: Component is not ready
"Unable to construct item from component qrc:/qml/ReactWebView.qml"
"Can't create QML item for componenet qrc:/qml/ReactWebView.qml"
"RCTWebViewView" has no view for inspecting!
```
Reload JS does not help, restarting Figwheel/react-native might not as well. Restarting Metro bundler solved it for me.
### After login when several contacts are available: realm errors
1. `attempting to create an object of type 'chat'...`
2. `attempting to create an object of type 'transport'...`
3. Error text containing only the public key.
The realm stack trace follows.
### Error: spawn gnome-terminal ENOENT
In node server log:
```
ignoring exception: Error: read ECONNRESET
```
In react-native log:
```
./run-app.sh: line 72: 56660 Segmentation fault: 11 /path-to-status-react/desktop/bin/StatusIm $args
events.js:183
throw er; // Unhandled 'error' event
^
Error: spawn gnome-terminal ENOENT
at _errnoException (util.js:992:11)
at Process.ChildProcess._handle.onexit (internal/child_process.js:190:19)
at onErrorNT (internal/child_process.js:372:16)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
```
or
```
StatusIm(7924,0x70000c1cd000) malloc: *** error for object 0x7f8b1539bd10: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
./run-app.sh: line 72: 7924 Abort trap: 6 /path-to-status-react/desktop/bin/StatusIm $args
events.js:183
throw er; // Unhandled 'error' event
^
Error: spawn gnome-terminal ENOENT
at _errnoException (util.js:992:11)
at Process.ChildProcess._handle.onexit (internal/child_process.js:190:19)
at onErrorNT (internal/child_process.js:372:16)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
```
### statusgo error during `react-native run-desktop`
```
Command failed: build(.)sh -e "node_modules/react-native-i18n/desktop;node_modules/react-native-config/desktop;node_modules/react-native-fs/desktop;node_modules/react-native-http-bridge/desktop;node_modules/react-native-webview-bridge/desktop;modules/react-native-status/desktop"
# github.com/status-im/status-go/vendor/github.com/ethereum/go-ethereum/crypto/bn256
../vendor/github.com/ethereum/go-ethereum/crypto/bn256/bn256_fast.go:26: syntax error: unexpected = in type declaration
../vendor/github.com/ethereum/go-ethereum/crypto/bn256/bn256_fast.go:30: syntax error: unexpected = in type declaration
# github.com/status-im/status-go/vendor/github.com/ethereum/go-ethereum/crypto/bn256
vendor/github.com/ethereum/go-ethereum/crypto/bn256/bn256_fast.go:26: syntax error: unexpected = in type declaration
vendor/github.com/ethereum/go-ethereum/crypto/bn256/bn256_fast.go:30: syntax error: unexpected = in type declaration
make[3]: *** [statusgo-library] Error 2
make[2]: *** [modules/react-native-status/desktop/StatusGo/src/github.com/status-im/src/StatusGo_ep-stamp/StatusGo_ep-configure] Error 2
make[1]: *** [modules/react-native-status/desktop/CMakeFiles/StatusGo_ep(.)dir/all] Error 2
make: *** [all] Error 2
```
### inotify errors
upon running `npm start` on linux, watchman may indicate: "The user limit on the total number of inotify watches was reached"
This can be fixed by running the below command. Note, changes will only be as valid as the current terminal session.
```
echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_user_watches && echo 999999 | sudo tee -a
/proc/sys/fs/inotify/max_queued_events && echo 999999 | sudo tee -a /proc/sys/fs/inotify/max_user_instances &&
watchman shutdown-server && sudo sysctl -p
```

View File

@ -1,5 +1,6 @@
(ns env.config)
(def figwheel-urls {:android "ws://192.168.10.203:3449/figwheel-ws",
:ios "ws://localhost:3449/figwheel-ws"}
)
:ios "ws://localhost:3449/figwheel-ws",
:desktop "ws://localhost:3449/figwheel-ws"})

View File

@ -18,11 +18,11 @@
;; Do not delete, root-el is used by the figwheel-bridge.js
(def root-el (r/as-element [reloader]))
(figwheel/start {:websocket-url (:ios conf/figwheel-urls)
(figwheel/start {:websocket-url (:desktop conf/figwheel-urls)
:heads-up-display false
:jsload-callback #(swap! cnt inc)})
(utils.handlers/add-pre-event-callback rr/pre-event-callback)
(rr/enable-re-frisk-remote! {:host (env.utils/re-frisk-url (:ios conf/figwheel-urls))
(rr/enable-re-frisk-remote! {:host (env.utils/re-frisk-url (:desktop conf/figwheel-urls))
:on-init core/init})

View File

@ -4,15 +4,15 @@
(defn system-options [builds-to-start]
{:nrepl-port 7888
:builds [{:id :desktop
:source-paths ["react-native/src" "src" "env/dev"]
:compiler {:output-to "target/ios/desktop.js"
:source-paths ["react-native/src/cljsjs" "react-native/src/desktop" "src" "env/dev"]
:compiler {:output-to "target/desktop/app.js"
:main "env.desktop.main"
:output-dir "target/desktop"
:npm-deps false
:optimizations :none}
:figwheel true}
{:id :ios
:source-paths ["react-native/src" "src" "env/dev"]
:source-paths ["react-native/src/cljsjs" "react-native/src/mobile" "src" "env/dev"]
:compiler {:output-to "target/ios/app.js"
:main "env.ios.main"
:output-dir "target/ios"
@ -20,7 +20,7 @@
:optimizations :none}
:figwheel true}
{:id :android
:source-paths ["react-native/src" "src" "env/dev"]
:source-paths ["react-native/src/cljsjs" "react-native/src/mobile" "src" "env/dev"]
:compiler {:output-to "target/android/app.js"
:main "env.android.main"
:output-dir "target/android"

4
env/prod/env/desktop/main.cljs vendored Normal file
View File

@ -0,0 +1,4 @@
(ns env.desktop.main
(:require [status-im.desktop.core :as core]))
(core/init)

View File

@ -5,7 +5,7 @@
*/
var CLOSURE_UNCOMPILED_DEFINES = null;
var debugEnabled = false;
var debugEnabled = true;
var config = {
basePath: "target/",
@ -103,27 +103,35 @@ var isChrome = function () {
return typeof importScripts === "function"
};
function asyncImportScripts(url, success, error) {
logDebug('(asyncImportScripts) Importing: ' + url);
asyncImportChain =
asyncImportChain
.then(function (v) {return fetch(url);})
.then(function (response) {
if(response.ok)
return response.text();
throw new Error("Failed to Fetch: " + url + " - Perhaps your project was cleaned and you haven't recompiled?")
})
.then(function (responseText) {
evaluate(responseText);
async function getUrlText(url) {
const text = await fetch(url).then(response => {
if(!response.ok) {
throw new Error("Failed to Fetch: " + url + " - Perhaps your project was cleaned and you haven't recompiled?");
}
return response.text()
});
return text;
}
var ATTEMPTS_COUNT = 3;
async function asyncImportScripts(url, success, error) {
var attempt = 0;
var text = await getUrlText(url);
while(attempt < ATTEMPTS_COUNT && text.length === 0)
{
text = await getUrlText(url);
++attempt;
}
if(!text || 0 === text.length) {
console.log("Can't fetch file: ", url)
return;
}
evaluate(text);
fireEvalListenters(url);
success();
return true;
})
.catch(function (e) {
console.error(e);
error();
return true;
});
}
function syncImportScripts(url, success, error) {
@ -147,7 +155,7 @@ function importJs(src, success, error) {
if (isChrome()) {
syncImportScripts(src, success, error);
} else {
asyncImportScripts(src, success, error);
asyncImportChain = asyncImportChain.then(function (v) {return asyncImportScripts(src, success, error);})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR})
find_package(Go REQUIRED)
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_TYPE_NAMES}
\"RCTStatus\" PARENT_SCOPE)
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_SRC}
${CMAKE_CURRENT_SOURCE_DIR}/rctstatus.cpp PARENT_SCOPE)
include(${CMAKE_ROOT}/Modules/ExternalProject.cmake)
if (WIN32 AND NOT CUSTOM_STATUSGO_BUILD_DIR_PATH)
set(CUSTOM_STATUSGO_BUILD_DIR_PATH "C:/srd-build/StatusGo")
endif()
if (CUSTOM_STATUSGO_BUILD_DIR_PATH)
set(StatusGo_ROOT ${CUSTOM_STATUSGO_BUILD_DIR_PATH})
else()
set(StatusGo_ROOT "${CMAKE_CURRENT_BINARY_DIR}/StatusGo")
endif()
set(StatusGo_PREFIX "${StatusGo_ROOT}/src/github.com/status-im")
set(StatusGo_SOURCE_DIR "${StatusGo_PREFIX}/status-go")
set(StatusGo_INCLUDE_DIR "${StatusGo_SOURCE_DIR}/build/bin")
set(StatusGo_STATIC_LIB
"${StatusGo_SOURCE_DIR}/build/bin/${CMAKE_STATIC_LIBRARY_PREFIX}status${CMAKE_STATIC_LIBRARY_SUFFIX}")
include_directories(${StatusGo_INCLUDE_DIR})
if (WIN32)
set(CONFIGURE_SCRIPT build-status-go.bat)
else()
set(CONFIGURE_SCRIPT build-status-go.sh)
endif()
ExternalProject_Add(StatusGo_ep
PREFIX ${StatusGo_PREFIX}
SOURCE_DIR ${StatusGo_SOURCE_DIR}
GIT_REPOSITORY https://github.com/status-im/status-go.git
GIT_TAG origin/develop
BUILD_BYPRODUCTS ${StatusGo_STATIC_LIB}
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/${CONFIGURE_SCRIPT} ${GO_ROOT_PATH} ${StatusGo_ROOT} ${StatusGo_SOURCE_DIR}
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
set(REACT_NATIVE_DESKTOP_EXTERNAL_PROJECT_DEPS ${REACT_NATIVE_DESKTOP_EXTERNAL_PROJECT_DEPS} StatusGo_ep PARENT_SCOPE)
if (APPLE)
set(STATUSGO_DEPS_LIBS "-framework Foundation"
"-framework CoreServices"
"-framework IOKit"
"-framework Security" pthread)
elseif (WIN32)
set(STATUSGO_DEPS_LIBS -lWinMM -lWS2_32 -lsetupapi)
else()
set(STATUSGO_DEPS_LIBS pthread)
endif()
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_LIBS ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_LIBS}
${StatusGo_STATIC_LIB} ${STATUSGO_DEPS_LIBS} PARENT_SCOPE)
set(REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_INCLUDE_DIRS ${REACT_NATIVE_DESKTOP_EXTERNAL_MODULES_INCLUDE_DIRS}
${StatusGo_INCLUDE_DIR} PARENT_SCOPE)

View File

@ -0,0 +1,35 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# The module defines the following variables:
# GO_FOUND - true if the Go was found
# GO_EXECUTABLE - path to the executable
# GO_VERSION - Go version number
# GO_PLATFORM - i.e. linux
# GO_ARCH - i.e. amd64
# Example usage:
# find_package(Go 1.2 REQUIRED)
find_program(GO_EXECUTABLE go PATHS ENV GOROOT GOPATH GOBIN PATH_SUFFIXES bin)
if (GO_EXECUTABLE)
get_filename_component(GO_ROOT_PATH ${GO_EXECUTABLE} REALPATH)
get_filename_component(GO_ROOT_PATH ${GO_ROOT_PATH}/../.. REALPATH)
message(STATUS "GO_ROOT_PATH is set to: ${GO_ROOT_PATH}")
execute_process(COMMAND ${GO_EXECUTABLE} version OUTPUT_VARIABLE GO_VERSION_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE)
if(GO_VERSION_OUTPUT MATCHES "go([0-9]+\\.[0-9]+\\.?[0-9]*)[a-zA-Z0-9]* ([^/]+)/(.*)")
set(GO_VERSION ${CMAKE_MATCH_1})
set(GO_PLATFORM ${CMAKE_MATCH_2})
set(GO_ARCH ${CMAKE_MATCH_3})
elseif(GO_VERSION_OUTPUT MATCHES "go version devel .* ([^/]+)/(.*)$")
set(GO_VERSION "99-devel")
set(GO_PLATFORM ${CMAKE_MATCH_1})
set(GO_ARCH ${CMAKE_MATCH_2})
message("WARNING: Development version of Go being used, can't determine compatibility.")
endif()
endif()
mark_as_advanced(GO_EXECUTABLE)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Go REQUIRED_VARS GO_EXECUTABLE GO_VERSION GO_PLATFORM GO_ARCH VERSION_VAR GO_VERSION)

View File

@ -0,0 +1,8 @@
set GOROOT=%1
set GOPATH=%2
set PATH=%GOROOT%/bin;%GOROOT%;%GOPATH%;%PATH%
cd %3/lib
go get .
cd ..
mingw32-make statusgo-library

View File

@ -0,0 +1,10 @@
#!/bin/bash
export GOROOT=$1
export GOPATH=$2
export PATH=$GOROOT/bin:$GOROOT:$GOPATH:$PATH
cd $3/lib
go get ./
cd ..
make statusgo-library

View File

@ -0,0 +1,286 @@
/**
* Copyright (c) 2017-present, Status Research and Development GmbH.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "rctstatus.h"
#include "bridge.h"
#include "eventdispatcher.h"
#include <QDebug>
#include <QJsonDocument>
#include <QByteArray>
#include <QVariantMap>
#include <QDir>
#include <QStandardPaths>
#include "libstatus.h"
namespace {
struct RegisterQMLMetaType {
RegisterQMLMetaType() {
qRegisterMetaType<RCTStatus*>();
}
} registerMetaType;
} // namespace
class RCTStatusPrivate {
public:
static Bridge* bridge;
static RCTStatus* rctStatus;
};
Bridge* RCTStatusPrivate::bridge = nullptr;
RCTStatus* RCTStatusPrivate::rctStatus = nullptr;
RCTStatus::RCTStatus(QObject* parent) : QObject(parent), d_ptr(new RCTStatusPrivate) {
RCTStatusPrivate::rctStatus = this;
SetSignalEventCallback((void*)&RCTStatus::jailSignalEventCallback);
connect(this, &RCTStatus::jailSignalEvent, this, &RCTStatus::onJailSignalEvent);
}
RCTStatus::~RCTStatus() {}
void RCTStatus::setBridge(Bridge* bridge) {
Q_D(RCTStatus);
d->bridge = bridge;
}
QString RCTStatus::moduleName() {
return "Status";
}
QList<ModuleMethod*> RCTStatus::methodsToExport() {
return QList<ModuleMethod*>{};
}
QVariantMap RCTStatus::constantsToExport() {
return QVariantMap();
}
void RCTStatus::initJail(QString js, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::initJail with param js:" << " and callback id: " << callbackId;
InitJail(js.toUtf8().data());
d->bridge->invokePromiseCallback(callbackId, QVariantList{ "{\"result\":\"\"}" });
}
void RCTStatus::parseJail(QString chatId, QString js, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::parseJail with param chatId: " << chatId << " js:" << " and callback id: " << callbackId;
const char* result = Parse(chatId.toUtf8().data(), js.toUtf8().data());
qDebug() << "RCTStatus::parseJail parseJail result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::callJail(QString chatId, QString path, QString params, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::callJail with param chatId: " << chatId << " path: " << path << " params: " << params << " and callback id: " << callbackId;
const char* result = Call(chatId.toUtf8().data(), path.toUtf8().data(), params.toUtf8().data());
qDebug() << "RCTStatus::callJail callJail result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::getDeviceUUID(double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::getDeviceUUID";
d->bridge->invokePromiseCallback(callbackId, QVariantList{"com.status.StatusIm"});
}
void RCTStatus::startNode(QString configString) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::startNode with param configString:" << configString;
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(configString.toUtf8(), &jsonError);
if (jsonError.error != QJsonParseError::NoError){
qDebug() << jsonError.errorString();
}
qDebug() << " RCTStatus::startNode configString: " << jsonDoc.toVariant().toMap();
QVariantMap configJSON = jsonDoc.toVariant().toMap();
QString newKeystoreUrl = "keystore";
int networkId = configJSON["NetworkId"].toInt();
QString dataDir = configJSON["DataDir"].toString();
QString networkDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/" + dataDir;
QDir dir(networkDir);
if (!dir.exists()) {
dir.mkpath(".");
}
qDebug()<<"RCTStatus::startNode networkDir: "<<networkDir;
char *configChars = GenerateConfig(networkDir.toUtf8().data(), networkId);
qDebug() << "RCTStatus::startNode GenerateConfig result: " << configChars;
jsonDoc = QJsonDocument::fromJson(QString(configChars).toUtf8(), &jsonError);
if (jsonError.error != QJsonParseError::NoError){
qDebug() << jsonError.errorString();
}
qDebug() << " RCTStatus::startNode GenerateConfig configString: " << jsonDoc.toVariant().toMap();
QVariantMap generatedConfig = jsonDoc.toVariant().toMap();
generatedConfig["KeyStoreDir"] = newKeystoreUrl;
generatedConfig["LogEnabled"] = true;
generatedConfig["LogFile"] = networkDir + "/geth.log";
//generatedConfig["LogLevel"] = "DEBUG";
const char* result = StartNode(QString(QJsonDocument::fromVariant(generatedConfig).toJson(QJsonDocument::Compact)).toUtf8().data());
qDebug() << "RCTStatus::startNode StartNode result: " << result;
}
void RCTStatus::shouldMoveToInternalStorage(double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::shouldMoveToInternalStorage with param callbackId: " << callbackId;
d->bridge->invokePromiseCallback(callbackId, QVariantList{});
}
void RCTStatus::moveToInternalStorage(double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::moveToInternalStorage with param callbackId: " << callbackId;
d->bridge->invokePromiseCallback(callbackId, QVariantList{ "{\"result\":\"\"}" });
}
void RCTStatus::stopNode() {
qDebug() << "call of RCTStatus::stopNode";
const char* result = StopNode();
qDebug() << "RCTStatus::stopNode StopNode result: " << result;
}
void RCTStatus::createAccount(QString password, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::createAccount with param callbackId: " << callbackId;
const char* result = CreateAccount(password.toUtf8().data());
qDebug() << "RCTStatus::createAccount CreateAccount result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::notifyUsers(QString token, QString payloadJSON, QString tokensJSON, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::notifyUsers with param callbackId: " << callbackId;
const char* result = NotifyUsers(token.toUtf8().data(), payloadJSON.toUtf8().data(), tokensJSON.toUtf8().data());
qDebug() << "RCTStatus::notifyUsers Notify result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::addPeer(QString enode, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::addPeer with param callbackId: " << callbackId;
const char* result = AddPeer(enode.toUtf8().data());
qDebug() << "RCTStatus::addPeer AddPeer result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::recoverAccount(QString passphrase, QString password, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::recoverAccount with param callbackId: " << callbackId;
const char* result = RecoverAccount(password.toUtf8().data(), passphrase.toUtf8().data());
qDebug() << "RCTStatus::recoverAccount RecoverAccount result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::login(QString address, QString password, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::login with param callbackId: " << callbackId;
const char* result = Login(address.toUtf8().data(), password.toUtf8().data());
qDebug() << "RCTStatus::login Login result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::approveSignRequests(QString hashes, QString password, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::approveSignRequests with param callbackId: " << callbackId;
const char* result = ApproveSignRequests(hashes.toUtf8().data(), password.toUtf8().data());
qDebug() << "RCTStatus::approveSignRequests CompleteTransactions result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::discardSignRequest(QString id) {
qDebug() << "call of RCTStatus::discardSignRequest with id: " << id;
DiscardSignRequest(id.toUtf8().data());
}
void RCTStatus::setAdjustResize() {
}
void RCTStatus::setAdjustPan() {
}
void RCTStatus::setSoftInputMode(int i) {
}
void RCTStatus::clearCookies() {
}
void RCTStatus::clearStorageAPIs() {
}
void RCTStatus::sendWeb3Request(QString payload, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::sendWeb3Request with param callbackId: " << callbackId;
const char* result = CallRPC(payload.toUtf8().data());
qDebug() << "RCTStatus::sendWeb3Request CallRPC result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::sendWeb3PrivateRequest(QString payload, double callbackId) {
Q_D(RCTStatus);
qDebug() << "call of RCTStatus::sendWeb3PrivateRequest with param callbackId: " << callbackId;
const char* result = CallPrivateRPC(payload.toUtf8().data());
qDebug() << "RCTStatus::sendWeb3PrivateRequest CallPrivateRPC result: " << result;
d->bridge->invokePromiseCallback(callbackId, QVariantList{result});
}
void RCTStatus::closeApplication() {
}
bool RCTStatus::JSCEnabled() {
qDebug() << "call of RCTStatus::JSCEnabled";
return false;
}
void RCTStatus::jailSignalEventCallback(const char* signal) {
qDebug() << "call of RCTStatus::jailSignalEventCallback ... signal: " << signal;
RCTStatusPrivate::rctStatus->emitSignalEvent(signal);
}
void RCTStatus::emitSignalEvent(const char* signal) {
qDebug() << "call of RCTStatus::emitSignalEvent ... signal: " << signal;
Q_EMIT jailSignalEvent(signal);
}
void RCTStatus::onJailSignalEvent(const char* signal) {
qDebug() << "call of RCTStatus::onJailSignalEvent ... signal: " << signal;
RCTStatusPrivate::bridge->eventDispatcher()->sendDeviceEvent("gethEvent", QVariantMap{{"jsonEvent", signal}});
}

View File

@ -0,0 +1,76 @@
/**
* Copyright (c) 2017-present, Status Research and Development GmbH.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#ifndef RCTSTATUS_H
#define RCTSTATUS_H
#include "moduleinterface.h"
#include <QVariantMap>
class RCTStatusPrivate;
class RCTStatus : public QObject, public ModuleInterface {
Q_OBJECT
Q_INTERFACES(ModuleInterface)
Q_DECLARE_PRIVATE(RCTStatus)
public:
Q_INVOKABLE RCTStatus(QObject* parent = 0);
~RCTStatus();
void setBridge(Bridge* bridge) override;
QString moduleName() override;
QList<ModuleMethod*> methodsToExport() override;
QVariantMap constantsToExport() override;
Q_INVOKABLE void initJail(QString js, double callbackId);
Q_INVOKABLE void parseJail(QString chatId, QString js, double callbackId);
Q_INVOKABLE void callJail(QString chatId, QString path, QString params, double callbackId);
Q_INVOKABLE void startNode(QString configString);
Q_INVOKABLE void shouldMoveToInternalStorage(double callbackId);
Q_INVOKABLE void moveToInternalStorage(double callbackId);
Q_INVOKABLE void stopNode();
Q_INVOKABLE void createAccount(QString password, double callbackId);
Q_INVOKABLE void notifyUsers(QString token, QString payloadJSON, QString tokensJSON, double callbackId);
Q_INVOKABLE void addPeer(QString enode, double callbackId);
Q_INVOKABLE void recoverAccount(QString passphrase, QString password, double callbackId);
Q_INVOKABLE void login(QString address, QString password, double callbackId);
Q_INVOKABLE void approveSignRequests(QString hashes, QString password, double callbackId);
Q_INVOKABLE void discardSignRequest(QString id);
Q_INVOKABLE void setAdjustResize();
Q_INVOKABLE void setAdjustPan();
Q_INVOKABLE void setSoftInputMode(int i);
Q_INVOKABLE void clearCookies();
Q_INVOKABLE void clearStorageAPIs();
Q_INVOKABLE void sendWeb3Request(QString payload, double callbackId);
Q_INVOKABLE void sendWeb3PrivateRequest(QString payload, double callbackId);
Q_INVOKABLE void closeApplication();
Q_INVOKABLE void getDeviceUUID(double callbackId);
Q_INVOKABLE static bool JSCEnabled();
Q_INVOKABLE static void jailSignalEventCallback(const char* signal);
void emitSignalEvent(const char* signal);
Q_SIGNALS:
void jailSignalEvent(const char* signal);
private Q_SLOTS:
void onJailSignalEvent(const char* signal);
private:
QScopedPointer<RCTStatusPrivate> d_ptr;
};
#endif // RCTSTATUS_H

View File

@ -0,0 +1,23 @@
patch-package
--- a/node_modules/metro/src/JSTransformer/index.js
+++ b/node_modules/metro/src/JSTransformer/index.js
@@ -151,6 +151,8 @@ module.exports = class Transformer {
/^--heap[_-]growing[_-]percent=[0-9]+$/.test(arg) ||
/^--max[_-]old[_-]space[_-]size=[0-9]+$/.test(arg));
+ execArgv.push("--max-old-space-size=8192");
+
const env = _extends({},
process.env, {
// Force color to print syntax highlighted code frames.
--- a/node_modules/metro/src/defaults.js
+++ b/node_modules/metro/src/defaults.js
@@ -45,7 +45,7 @@ exports.sourceExts = ['js', 'json'];
exports.moduleSystem = require.resolve('./lib/polyfills/require.js');
-exports.platforms = ['ios', 'android', 'windows', 'web'];
+exports.platforms = ['ios', 'android', 'windows', 'web', 'desktop'];
exports.providesModuleNodeModules = ['react-native', 'react-native-windows'];

View File

@ -22,13 +22,17 @@
:aliases {"prod-build" ^{:doc "Recompile code with prod profile."}
["do" "clean"
["with-profile" "prod" "cljsbuild" "once" "ios"]
["with-profile" "prod" "cljsbuild" "once" "android"]]
["with-profile" "prod" "cljsbuild" "once" "android"]
["with-profile" "prod" "cljsbuild" "once" "desktop"]]
"prod-build-android" ^{:doc "Recompile code for Android with prod profile."}
["do" "clean"
["with-profile" "prod" "cljsbuild" "once" "android"]]
"prod-build-ios" ^{:doc "Recompile code for iOS with prod profile."}
["do" "clean"
["with-profile" "prod" "cljsbuild" "once" "ios"]]
"prod-build-desktop" ^{:doc "Recompile code for desktop with prod profile."}
["do" "clean"
["with-profile" "prod" "cljsbuild" "once" "desktop"]]
"figwheel-repl" ["with-profile" "+figwheel" "run" "-m" "clojure.main" "env/dev/run.clj"]
"test-cljs" ["with-profile" "test" "doo" "node" "test" "once"]
"test-protocol" ["with-profile" "test" "doo" "node" "protocol" "once"]
@ -36,20 +40,20 @@
:profiles {:dev {:dependencies [[com.cemerick/piggieback "0.2.2"]]
:cljsbuild {:builds
{:ios
{:source-paths ["components/src" "react-native/src" "src"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src"]
:compiler {:output-to "target/ios/app.js"
:main "env.ios.main"
:output-dir "target/ios"
:optimizations :none}}
:android
{:source-paths ["components/src" "react-native/src" "src"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src"]
:compiler {:output-to "target/android/app.js"
:main "env.android.main"
:output-dir "target/android"
:optimizations :none}
:warning-handlers [status-im.utils.build/warning-handler]}
:desktop
{:source-paths ["components/src" "react-native/src" "src"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/desktop" "src"]
:compiler {:output-to "target/desktop/app.js"
:main "env.desktop.main"
:output-dir "target/desktop"
@ -62,7 +66,7 @@
[re-frisk-sidecar "0.5.7"]
[day8.re-frame/tracing "0.5.0"]
[hawk "0.2.11"]]
:source-paths ["src" "env/dev" "react-native/src" "components/src"]}]
:source-paths ["src" "env/dev" "react-native/src/cljsjs" "components/src"]}]
:test {:dependencies [[day8.re-frame/test "0.1.5"]]
:plugins [[lein-doo "0.1.9"]]
:cljsbuild {:builds
@ -91,7 +95,7 @@
:target :nodejs}}]}}
:prod {:cljsbuild {:builds
{:ios
{:source-paths ["components/src" "react-native/src" "src" "env/prod"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src" "env/prod"]
:compiler {:output-to "index.ios.js"
:main "env.ios.main"
:output-dir "target/ios-prod"
@ -104,7 +108,7 @@
:language-in :ecmascript5}
:warning-handlers [status-im.utils.build/warning-handler]}
:android
{:source-paths ["components/src" "react-native/src" "src" "env/prod"]
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/mobile" "src" "env/prod"]
:compiler {:output-to "index.android.js"
:main "env.android.main"
:output-dir "target/android-prod"
@ -115,4 +119,17 @@
:parallel-build false
:elide-asserts true
:language-in :ecmascript5}
:warning-handlers [status-im.utils.build/warning-handler]}
:desktop
{:source-paths ["components/src" "react-native/src/cljsjs" "react-native/src/desktop" "src" "env/prod"]
:compiler {:output-to "index.desktop.js"
:main "env.desktop.main"
:output-dir "target/desktop-prod"
:static-fns true
:optimize-constants true
:optimizations :simple
:closure-defines {"goog.DEBUG" false}
:parallel-build false
:elide-asserts true
:language-in :ecmascript5}
:warning-handlers [status-im.utils.build/warning-handler]}}}}})

View File

@ -0,0 +1,31 @@
(ns status-im.react-native.js-dependencies)
(def config (js/require "react-native-config"))
(def fs (js/require "react-native-fs"))
(def http-bridge (js/require "react-native-http-bridge"))
(def keychain (js/require "react-native-keychain"))
(def qr-code (js/require "react-native-qrcode"))
(def react-native (js/require "react-native"))
(def realm (js/require "realm"))
(def webview-bridge (js/require "react-native-webview-bridge"))
(def secure-random (.-generateSecureRandom (js/require "react-native-securerandom")))
(def EventEmmiter (js/require "react-native/Libraries/vendor/emitter/EventEmitter"))
(def fetch (.-default (js/require "react-native-fetch-polyfill")))
(def i18n (js/require "react-native-i18n"))
(def camera #js {:constants {:Aspect "Portrait"}})
(def dialogs #js {})
(def dismiss-keyboard #js {})
(def image-crop-picker #js {})
(def image-resizer #js {})
(def instabug #js {:IBGLog ( fn [])})
(def nfc #js {})
(def svg #js {})
(def react-native-fcm #js {:default #js {:getFCMToken (fn [])
:requestPermissions (fn [])}})
(def snoopy #js {})
(def snoopy-filter #js {})
(def snoopy-bars #js {})
(def snoopy-buffer #js {})
(def background-timer #js {:setTimeout (fn [])})
(def testfairy #js {})

View File

@ -1,31 +1,29 @@
(ns status-im.react-native.js-dependencies)
(def camera (js/require "react-native-camera"))
(def config (js/require "react-native-config"))
(def dialogs (js/require "react-native-dialogs"))
(def dismiss-keyboard (js/require "dismissKeyboard"))
(def fs (js/require "react-native-fs"))
(def http-bridge (js/require "react-native-http-bridge"))
;; i18n is now exported in default object of the module
;; https://github.com/AlexanderZaytsev/react-native-i18n/blob/master/index.js
(def i18n (.-default (js/require "react-native-i18n")))
(def image-crop-picker (js/require "react-native-image-crop-picker"))
(def image-resizer (js/require "react-native-image-resizer"))
(def instabug (js/require "instabug-reactnative"))
(def keychain (js/require "react-native-keychain"))
(def nfc (js/require "nfc-react-native"))
(def qr-code (js/require "react-native-qrcode"))
(def react-native (js/require "react-native"))
(def realm (js/require "realm"))
(def webview-bridge (js/require "react-native-webview-bridge"))
(def secure-random (.-generateSecureRandom (js/require "react-native-securerandom")))
(def EventEmmiter (js/require "react-native/Libraries/vendor/emitter/EventEmitter"))
(def fetch (.-default (js/require "react-native-fetch-polyfill")))
(def i18n (.-default (js/require "react-native-i18n")))
(def camera (js/require "react-native-camera"))
(def dialogs (js/require "react-native-dialogs"))
(def dismiss-keyboard (js/require "dismissKeyboard"))
(def image-crop-picker (js/require "react-native-image-crop-picker"))
(def image-resizer (js/require "react-native-image-resizer"))
(def instabug (js/require "instabug-reactnative"))
(def nfc (js/require "nfc-react-native"))
(def svg (js/require "react-native-svg"))
(def react-native-fcm (js/require "react-native-fcm"))
(def secure-random (.-generateSecureRandom (js/require "react-native-securerandom")))
(def snoopy (js/require "rn-snoopy"))
(def snoopy-filter (js/require "rn-snoopy/stream/filter"))
(def snoopy-bars (js/require "rn-snoopy/stream/bars"))
(def snoopy-buffer (js/require "rn-snoopy/stream/buffer"))
(def EventEmmiter (js/require "react-native/Libraries/vendor/emitter/EventEmitter"))
(def background-timer (.-default (js/require "react-native-background-timer")))
(def fetch (.-default (js/require "react-native-fetch-polyfill")))
(def testfairy (js/require "react-native-testfairy"))

View File

@ -0,0 +1,89 @@
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
STATUSREACTPATH="$SCRIPTPATH/.."
WORKFOLDER="$SCRIPTPATH/../mac_bundle"
MACDEPLOYQT=""
#if no arguments passed, inform user about possible ones (one for making script interactive, one for path to macdeployqt)
if [ $# -eq 0 ]
then
echo -e "${RED}You need to specify path to macdeployqt binary as an argument${NC}"
echo "Example: scripts/create-desktop-mac-bundle.sh /usr/bin/macdeployqt"
exit 1
else
MACDEPLOYQT=$1
fi
# check if gdrive installed
command -v gdrive >/dev/null 2>&1 || { echo -e "${RED}gdrive tool need to be installed. (brew install gdrive). Aborting.${NC}" >&2; exit 1; }
# inform user that define should be changed in "desktop/main.cpp"
echo ""
echo -e "${YELLOW}In desktop/main.cpp file please uncomment #define BULID_FOR_BUNDLE line.${NC}"
read -p "When ready, plese press enter to continue"
echo ""
# create directory for all work related to bundling
mkdir -p $WORKFOLDER
echo -e "${GREEN}Work folder created: $WORKFOLDER${NC}"
echo ""
# from index.desktop.js create javascript bundle and resources folder
echo "Generating StatusIm.bundle and assets folder..."
react-native bundle --entry-file index.desktop.js --bundle-output $WORKFOLDER/StatusIm.jsbundle --dev false --platform desktop --assets-dest $WORKFOLDER/assets
echo -e "${GREEN}Generating done.${NC}"
echo ""
# show path to javascript bundle and line that should be added to package.json
echo -e "${YELLOW}Please add the following line to package.json:${NC}"
echo "\"desktopJSBundlePath\": \"$WORKFOLDER/StatusIm.jsbundle\""
echo ""
read -p "When ready, plese press enter to continue"
echo ""
# build desktop app
echo "Building StatusIm desktop..."
react-native build-desktop
echo -e "${GREEN}Building done.${NC}"
echo ""
# download prepared package with mac bundle files (it contains qt libraries, icon)
echo "Downloading skeleton of mac bundle..."
echo -e "${YELLOW}First time gdrive can ask you for permissions to google drive${NC}"
gdrive download --path $WORKFOLDER 1fJbW9FzGGPvYkuJcSH5mCAcGdnyUeDSY
echo -e "${GREEN}Downloading done.${NC}"
echo ""
# Unpacking downloaded archive
echo "Unpacking bundle skeleton"
unzip $WORKFOLDER/StatusIm.app.zip -d $WORKFOLDER
chmod +x $WORKFOLDER/StatusIm.app/Contents/MacOs/ubuntu-server
echo -e "${GREEN}Unzipping done.${NC}"
echo ""
# copy binary and resources to mac bundle
echo "Copying resources and binary..."
cp -r $WORKFOLDER/assets/share/assets $WORKFOLDER/StatusIm.app/Contents/MacOs
cp $STATUSREACTPATH/desktop/bin/StatusIm $WORKFOLDER/StatusIm.app/Contents/MacOs
echo -e "${GREEN}Copying done.${NC}"
echo ""
# invoke macdeployqt to create StatusIm.dmg
echo "Creating bundle dmg..."
$MACDEPLOYQT $WORKFOLDER/StatusIm.app -verbose=3 -qmldir="$STATUSREACTPATH/node_modules/react-native/ReactQt/application/src/" -qmldir="$STATUSREACTPATH/node_modules/react-native/ReactQt/runtime/src/qml/" -dmg
echo -e "${GREEN}Bundle ready!${NC}"
echo ""

View File

@ -0,0 +1,8 @@
#!/bin/bash
filename=$1
#fileid="1yPTGcPe5DZd3ubzAgUBp3aAQRAOK9eKQ"
fileid=$2
rm -rf ./cookie
curl -c ./cookie -s -L "https://drive.google.com/uc?export=download&id=${fileid}" > /dev/null
curl -Lb ./cookie "https://drive.google.com/uc?export=download&confirm=`awk '/download/ {print $NF}' ./cookie`&id=${fileid}" -o ${filename}

48
scripts/prepare-for-platform.sh Executable file
View File

@ -0,0 +1,48 @@
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
PLATFORM=""
PLATFORM_FOLDER=""
#if no arguments passed, inform user about possible ones
if [ $# -eq 0 ]
then
echo -e "${GREEN}This script should be invoked with platform argument: 'mobile' or 'desktop'${NC}"
echo "When called it links"
# echo "If invoked with 'mobile' argument it will make a copying: "
# echo "package.json.mobile -> package.json"
# echo "etc.."
exit 1
else
PLATFORM=$1
PLATFORM_FOLDER="${PLATFORM}_files"
fi
echo "Removing node_modules"
rm -rf node_modules
echo "Creating link: package.json -> ${PLATFORM_FOLDER}/package.json "
ln -sf ${PLATFORM_FOLDER}/package.json package.json
echo "Creating link: package-lock.json -> ${PLATFORM_FOLDER}/package-lock.json"
ln -sf ${PLATFORM_FOLDER}/package-lock.json package-lock.json
echo "Creating link: VERSION -> ${PLATFORM_FOLDER}/VERSION"
ln -sf ${PLATFORM_FOLDER}/VERSION VERSION
if [ "${PLATFORM}" == "mobile" ]
then
echo -e "Removing .re-natal symlink from root"
rm -rf .re-natal
else
echo "Creating link: .re-natal -> ${PLATFORM_FOLDER}/.re-natal"
ln -sf ${PLATFORM_FOLDER}/.re-natal .re-natal
fi
echo -e "${GREEN}Finished!${NC}"

View File

@ -11,10 +11,7 @@
(defn app-root []
(reagent/create-class
{:component-will-mount
(fn []
(.hide react/splash-screen))
:reagent-render views/main}))
{:reagent-render views/main}))
(defn init []
(core/init app-root))

View File

@ -17,7 +17,7 @@
;; if StatusModule is not initialized better to store
;; calls and make them only when StatusModule is ready
;; this flag helps to handle this
(defonce module-initialized? (atom (or p/ios? js/goog.DEBUG)))
(defonce module-initialized? (atom (or p/ios? js/goog.DEBUG p/desktop?)))
;; array of calls to StatusModule
(defonce calls (atom []))

View File

@ -81,6 +81,7 @@
:active-unknown "Unknown"
:available "Available"
:no-messages "No messages"
:no-messages-yet "No messages yet"
:suggestions-requests "Requests"
:suggestions-commands "Commands"
:faucet-success "Faucet request has been received"
@ -187,6 +188,7 @@
;;sharing
:sharing-copy-to-clipboard "Copy to clipboard"
:sharing-copied-to-clipboard "Copied to clipboard"
:sharing-share "Share..."
:sharing-cancel "Cancel"
@ -220,6 +222,7 @@
;;chats
:new "New"
:new-chat "New chat"
:start-chat "Start chat"
:start-new-chat "Start new chat"
:start-group-chat "Start group chat"
:invite-friends "Invite friends"
@ -232,6 +235,7 @@
:delete-group-chat-confirmation "Are you sure you want to delete this group chat?"
:new-group-chat "New group chat"
:new-public-group-chat "Join public chat"
:selected-for-you "Selected for you"
:public-chat "Public chat"
:edit-chats "Edit chats"
:search-chats "Search chats"
@ -281,8 +285,10 @@
:search-contacts "Search contacts"
:contacts-group-new-chat "Start new chat"
:choose-from-contacts "Choose from contacts"
:or-choose-a-contact "Or choose a contact"
:no-contacts "No contacts yet"
:show-qr "Show QR code"
:copy-qr "Copy code"
:qr-code-public-key-hint "Share this code to \nstart chatting"
:enter-address "Enter address"
:enter-contact-code "Enter contact code"

View File

@ -2,6 +2,7 @@
(:require [clojure.string :as string]))
(def white "#ffffff")
(def transparent "transparent")
(def white-light-transparent "rgba(255, 255, 255, 0.1)") ;; Used as icon background color for a dark foreground
(def white-transparent "rgba(255, 255, 255, 0.2)") ;; Used as icon color on dark background
(def white-lighter-transparent "rgba(255, 255, 255, 0.6)") ;; Used for input placeholder color
@ -28,6 +29,8 @@
(def cyan "#7adcfb") ;; Used by wallet transaction filtering icon
(def photo-border-color "#ccd3d6")
(def green "#44d058") ;; icon for successful inboud transaction
(def tooltip-green-text "#66bf50") ;; fading tooltip text color
(def tooltip-green "#e9f6e6") ;; fading tooltip background color
(def chat-colors ["#fa6565"
"#7cda00"

View File

@ -1,6 +1,8 @@
(ns status-im.ui.components.desktop.tabs
(:require [re-frame.core :as re-frame]
[status-im.ui.components.icons.vector-icons :as icons]
[taoensso.timbre :as log]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.react :as react]
[status-im.ui.screens.main-tabs.styles :as tabs.styles])
(:require-macros [status-im.utils.views :as views]))
@ -26,7 +28,7 @@
[react/view {:style tabs.styles/tab-container}
(let [icon (if active? icon-active icon-inactive)]
[react/view
[icons/icon icon {:color (:color (tabs.styles/tab-icon active?))}]])
[icons/icon icon {:style {:tint-color (if active? colors/blue colors/gray-icon)}}]])
[react/view
[react/text {:style (tabs.styles/tab-title active?)}
title]]]))
@ -36,12 +38,14 @@
(defn tab [index content view-id active?]
[react/touchable-highlight {:style (merge tabs.styles/tab-container {:flex 1})
:disabled active?
:on-press #(re-frame/dispatch [:set-in [:desktop/desktop :tab-view-id] view-id])}
:on-press #(do
(re-frame/dispatch [:navigate-to :home])
(re-frame/dispatch [:set-in [:desktop/desktop :tab-view-id] view-id]))}
[react/view
[content active?]]])
(views/defview main-tabs []
(views/letsubs [current-tab [:get :left-view-id]]
(views/letsubs [current-tab [:get-in [:desktop/desktop :tab-view-id]]]
[react/view
[react/view {:style tabs.styles/tabs-container}
(for [[index {:keys [content view-id]}] tabs-list-indexed]

View File

@ -2,31 +2,94 @@
(:require-macros [status-im.ui.components.svg :as components.svg])
(:require [goog.object :as object]
[reagent.core :as reagent]
[status-im.ui.components.react :as react]
[status-im.utils.platform :as platform]
[status-im.ui.components.styles :as styles]
[status-im.ui.components.react :as react]
[status-im.react-native.js-dependencies :as js-dependencies])
(:refer-clojure :exclude [use]))
(defn get-property [name]
(when-not platform/desktop?
(defn get-property [name]
(object/get js-dependencies/svg name))
(defn adapt-class [class]
(defn adapt-class [class]
(when class
(reagent/adapt-react-class class)))
(defn get-class [name]
(defn get-class [name]
(adapt-class (get-property name)))
(def svg (get-class "Svg"))
(def g (get-class "G"))
(def rect (get-class "Rect"))
(def path (get-class "Path"))
(def use (get-class "Use"))
(def defs (get-class "Defs"))
(def circle (get-class "Circle"))
(def svg (get-class "Svg"))
(def g (get-class "G"))
(def rect (get-class "Rect"))
(def path (get-class "Path"))
(def use (get-class "Use"))
(def defs (get-class "Defs"))
(def circle (get-class "Circle")))
(def icons {:icons/discover (components.svg/slurp-svg "./resources/icons/bottom/discover_gray.svg")
(if platform/desktop?
(def icons {:icons/discover (js/require "./resources/icons/bottom/discover_gray.svg")
:icons/contacts (js/require "./resources/icons/bottom/contacts_gray.svg")
:icons/home (js/require "./resources/icons/bottom/home_gray.svg")
:icons/home-active (js/require "./resources/icons/bottom/home_blue.svg")
:icons/profile (js/require "./resources/icons/bottom/profile_gray.svg")
:icons/profile-active (js/require "./resources/icons/bottom/profile_blue.svg")
:icons/wallet (js/require "./resources/icons/bottom/wallet_gray.svg")
:icons/wallet-active (js/require "./resources/icons/bottom/wallet_active.svg")
:icons/speaker (js/require "./resources/icons/speaker.svg")
:icons/speaker-off (js/require "./resources/icons/speaker_off.svg")
:icons/transaction-history (js/require "./resources/icons/transaction_history.svg")
:icons/add (js/require "./resources/icons/add.svg")
:icons/add-contact (js/require "./resources/icons/add_contact.svg")
:icons/add-wallet (js/require "./resources/icons/add_wallet.svg")
:icons/address (js/require "./resources/icons/address.svg")
:icons/arrow-left (js/require "./resources/icons/arrow_left.svg")
:icons/arrow-right (js/require "./resources/icons/arrow_right.svg")
:icons/flash-active (js/require "./resources/icons/flash_active.svg")
:icons/flash-inactive (js/require "./resources/icons/flash_inactive.svg")
:icons/attach (js/require "./resources/icons/attach.svg")
:icons/browse (js/require "./resources/icons/browse.svg")
:icons/close (js/require "./resources/icons/close.svg")
:icons/copy-from (js/require "./resources/icons/copy_from.svg")
:icons/delete (js/require "./resources/icons/delete.svg")
:icons/dots-horizontal (js/require "./resources/icons/dots_horizontal.svg")
:icons/dots-vertical (js/require "./resources/icons/dots_vertical.svg")
:icons/exclamation-mark (js/require "./resources/icons/exclamation_mark.svg")
:icons/filter (js/require "./resources/icons/filter.svg")
:icons/fullscreen (js/require "./resources/icons/fullscreen.svg")
:icons/group-big (js/require "./resources/icons/group_big.svg")
:icons/group-chat (js/require "./resources/icons/group_chat.svg")
:icons/chats (js/require "./resources/icons/chats.svg")
:icons/hamburger (js/require "./resources/icons/hamburger.svg")
:icons/hidden (js/require "./resources/icons/hidden.svg")
:icons/in-contacts (js/require "./resources/icons/in_contacts.svg")
:icons/lock (js/require "./resources/icons/lock.svg")
:icons/mic (js/require "./resources/icons/mic.svg")
:icons/ok (js/require "./resources/icons/ok.svg")
:icons/public (js/require "./resources/icons/public.svg")
:icons/public-chat (js/require "./resources/icons/public_chat.svg")
:icons/qr (js/require "./resources/icons/QR.svg")
:icons/input-commands (js/require "./resources/icons/input_commands.svg")
:icons/input-send (js/require "./resources/icons/input_send.svg")
:icons/back (js/require "./resources/icons/back.svg")
:icons/forward (js/require "./resources/icons/forward.svg")
:icons/dropdown-up (js/require "./resources/icons/dropdown_up.svg")
:icons/up (js/require "./resources/icons/up.svg")
:icons/down (js/require "./resources/icons/down.svg")
:icons/grab (js/require "./resources/icons/grab.svg")
:icons/share (js/require "./resources/icons/share.svg")
:icons/tooltip-triangle (js/require "./resources/icons/tooltip-triangle.svg")
:icons/open (js/require "./resources/icons/open.svg")
:icons/network (js/require "./resources/icons/network.svg")
:icons/wnode (js/require "./resources/icons/wnode.svg")
:icons/refresh (js/require "./resources/icons/refresh.svg")
:icons/newchat (js/require "./resources/icons/newchat.svg")
:icons/logo (js/require "./resources/icons/logo.svg")
:icons/camera (js/require "./resources/icons/camera.svg")
:icons/check (js/require "./resources/icons/check.svg")
:icons/dots (js/require "./resources/icons/dots.svg")
:icons/warning (js/require "./resources/icons/warning.svg")})
(def icons {:icons/discover (components.svg/slurp-svg "./resources/icons/bottom/discover_gray.svg")
:icons/contacts (components.svg/slurp-svg "./resources/icons/bottom/contacts_gray.svg")
:icons/home (components.svg/slurp-svg "./resources/icons/bottom/home_gray.svg")
:icons/home-active (components.svg/slurp-svg "./resources/icons/bottom/home_blue.svg")
@ -86,14 +149,28 @@
:icons/camera (components.svg/slurp-svg "./resources/icons/camera.svg")
:icons/check (components.svg/slurp-svg "./resources/icons/check.svg")
:icons/dots (components.svg/slurp-svg "./resources/icons/dots.svg")
:icons/warning (components.svg/slurp-svg "./resources/icons/warning.svg")})
:icons/warning (components.svg/slurp-svg "./resources/icons/warning.svg")}))
(defn normalize-property-name [n]
(if (= n :icons/options)
(if platform/ios? :icons/dots-horizontal :icons/dots-vertical)
n))
(defn icon
(if platform/desktop?
(do (def default-viewbox {:width 24 :height 24})
(defn icon
([name] (icon name nil))
([name {:keys [color container-style style accessibility-label width height]
:or {accessibility-label :icon}}]
(let [icon-style (if width
(assoc default-viewbox :width width :height height)
default-viewbox)]
[react/view {:style container-style
:accessibility-label accessibility-label}
[react/image {:source (get icons (normalize-property-name name))
:style (merge icon-style style)}]]))))
(do (defn icon
([name] (icon name nil))
([name {:keys [color container-style style accessibility-label width height]
:or {accessibility-label :icon}}]
@ -119,4 +196,4 @@
(if width
(update icon-vec 1 assoc :width width :height height)
icon-vec))
(throw (js/Error. (str "Unknown icon: " name))))]))
(throw (js/Error. (str "Unknown icon: " name))))]))))

View File

@ -1,6 +1,7 @@
(ns status-im.ui.components.text-input.styles
(:require-macros [status-im.utils.styles :refer [defstyle defnstyle]])
(:require [status-im.ui.components.colors :as colors]))
(:require [status-im.ui.components.colors :as colors]
[status-im.utils.platform :as p]))
(def label
{:font-size 14
@ -15,11 +16,12 @@
:border-radius 8
:background-color colors/gray-lighter})
(def input
(defstyle input
{:font-size 15
:letter-spacing -0.2
:color colors/black
:padding 0})
:padding 0
:desktop {:height 52}})
(defn error [label?]
{:bottom-value (if label? -20 0)

View File

@ -0,0 +1,12 @@
(ns status-im.ui.screens.add-new.new-public-chat.subs
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.screens.add-new.new-public-chat.db :as db]
[cljs.spec.alpha :as spec]))
(re-frame/reg-sub
:new-topic-error-message
:<- [:get :public-group-topic]
(fn [topic]
(when-not (spec/valid? ::db/topic topic)
(i18n/label :topic-name-error))))

View File

@ -19,7 +19,7 @@
;; initial state of app-db
(def app-db {:current-public-key nil
:status-module-initialized? (or platform/ios? js/goog.DEBUG)
:status-module-initialized? (or platform/ios? js/goog.DEBUG platform/desktop?)
:keyboard-height 0
:tab-bar-visible? true
:navigation-stack '()
@ -51,6 +51,7 @@
:chat/cooldown-enabled? false
:chat/last-outgoing-message-sent-at 0
:chat/spam-messages-frequency 0
:tooltips {}
:desktop/desktop {:tab-view-id :home}
:dimensions/window (dimensions/window)})
@ -128,6 +129,7 @@
:navigation.screen-params/collectibles-list])))
(spec/def :desktop/desktop (spec/nilable any?))
(spec/def ::tooltips (spec/nilable any?))
;;;;NETWORK
@ -216,6 +218,7 @@
::modal
::was-modal?
::rpc-url
::tooltips
::web3
::web3-node-version
::webview-bridge

View File

@ -0,0 +1,75 @@
(ns status-im.ui.screens.desktop.main.add-new.styles
(:require [status-im.ui.components.colors :as colors]))
(def new-contact-view
{:flex 1
:background-color colors/white
:margin-left 24
:margin-right 37})
(def new-contact-title
{:height 64
:align-items :flex-start
:justify-content :center})
(def new-contact-title-text
{:font-size 20
:color :black
:font-weight "600"})
(def new-contact-subtitle
{:font-size 14})
(def new-contact-separator
{:height 1
:background-color colors/gray-light})
(def add-contact-edit-view
{:height 45
:margin-bottom 32
:background-color colors/white
:border-radius 12
:flex-direction :row
:margin-top 16})
(def add-contact-input
{:font-size 14
:background-color colors/gray-lighter
:margin-right 12
:border-radius 8})
(defn add-contact-button [error?]
{:width 140
:height 45
:border-radius 8
:background-color (if error? colors/gray-lighter colors/blue)
:align-items :center
:justify-content :center})
(defn add-contact-button-text [error?]
{:font-size 16
:color (if error? colors/gray colors/white)})
(def suggested-contact-view
{:flex-direction "row"
:align-items :center
:margin-bottom 16})
(def suggested-contacts
{:margin-top 12})
(def suggested-contact-image
{:width 46
:height 46
:border-radius 46
:margin-right 16})
(def suggested-topic-image
(merge suggested-contact-image
{:background-color colors/blue
:align-items :center
:justify-content :center}))
(def suggested-topic-text
{:font-size 25.6
:color colors/white})

View File

@ -1,47 +1,97 @@
(ns status-im.ui.screens.desktop.main.add-new.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.screens.add-new.new-public-chat.view :as public-chat]
[status-im.ui.components.list.views :as list]
[clojure.string :as string]
[status-im.i18n :as i18n]
[re-frame.core :as re-frame]
[status-im.ui.screens.desktop.main.add-new.styles :as styles]
[status-im.ui.screens.add-new.new-public-chat.view :refer [default-public-chats]]
[status-im.ui.screens.add-new.new-public-chat.db :as public-chat-db]
[taoensso.timbre :as log]
[status-im.ui.components.react :as react]))
(views/defview new-contact []
(views/letsubs [new-contact-identity [:get :contacts/new-identity]
topic [:get :public-group-topic]]
[react/view {:style {:flex 1 :background-color "#eef2f5"}}
contacts [:all-added-people-contacts]
chat-error [:new-contact-error-message]
topic [:get :public-group-topic]
topic-error [:new-topic-error-message]
account [:get-current-account]
topic-input-ref (atom nil)]
[react/scroll-view
[react/view {:style styles/new-contact-view}
^{:key "newcontact"}
[react/view {:style {:height 64 :align-items :center :padding-horizontal 11 :justify-content :center}}
[react/text {:style {:font-size 16 :color :black :font-weight "600"}}
"Add new contact"]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/view {:style {:height 90 :margin-horizontal 16 :margin-bottom 16 :background-color :white :border-radius 12}}
;:box-shadow "0 0.5px 4.5px 0 rgba(0, 0, 0, 0.04)"}}
[react/view {:style {:flex-direction :row :margin-horizontal 16 :margin-top 16}}
[react/view {:style {:flex 1}}
[react/text-input {:placeholder "Contact key"
[react/view {:style styles/new-contact-title}
[react/text {:style styles/new-contact-title-text}
(i18n/label :new-chat)]]
[react/text {:style styles/new-contact-subtitle} (i18n/label :contact-code)]
[react/view {:style styles/new-contact-separator}]
[react/view {:style styles/add-contact-edit-view}
[react/text-input {:placeholder "0x..."
:flex 1
:style styles/add-contact-input
:on-change (fn [e]
(let [native-event (.-nativeEvent e)
text (.-text native-event)]
(re-frame/dispatch [:set :contacts/new-identity text])))}]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:add-contact-handler new-contact-identity])}
[react/view {:style {:margin-left 16 :width 30 :height 30 :border-radius 15 :background-color "#eef2f5" :align-items :center
:justify-content :center}}
[icons/icon :icons/ok]]]]]
(re-frame/dispatch [:set :contacts/new-identity text])))}]
[react/touchable-highlight {:disabled chat-error :on-press #(when-not chat-error (re-frame/dispatch [:add-contact-handler new-contact-identity]))}
[react/view
{:style (styles/add-contact-button chat-error)}
[react/text
{:style (styles/add-contact-button-text chat-error)}
(i18n/label :start-chat)]]]]
^{:key "choosecontact"}
[react/view
(when (seq contacts) [react/text {:style styles/new-contact-subtitle} (i18n/label :or-choose-a-contact)])
[react/view {:style styles/suggested-contacts}
(doall
(for [c contacts]
^{:key (:whisper-identity c)}
[react/touchable-highlight {:on-press #(do
(re-frame/dispatch [:set :contacts/new-identity (:whisper-identity c)])
(re-frame/dispatch [:add-contact-handler (:whisper-identity c)]))}
[react/view {:style styles/suggested-contact-view}
[react/image {:style styles/suggested-contact-image
:source {:uri (:photo-path c)}}]
[react/text {:style styles/new-contact-subtitle} (:name c)]]]))]]
^{:key "publicchat"}
[react/view {:style {:height 64 :align-items :center :padding-horizontal 11 :justify-content :center}}
[react/text {:style {:font-size 16 :color :black :font-weight "600"}}
"Join to public chat"]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/view {:style {:height 90 :margin-horizontal 16 :margin-bottom 16 :background-color :white :border-radius 12}}
;:box-shadow "0 0.5px 4.5px 0 rgba(0, 0, 0, 0.04)"}}
[react/view {:style {:flex-direction :row :margin-horizontal 16 :margin-top 16}}
[react/text "#"]
[react/view {:style styles/new-contact-title}
[react/text {:style styles/new-contact-title-text}
(i18n/label :new-public-group-chat)]]
[react/text {:style styles/new-contact-subtitle} (i18n/label :public-group-topic)]
[react/view {:style styles/new-contact-separator}]
[react/view {:style styles/add-contact-edit-view}
[react/view {:style {:flex 1}}
[react/text-input {:placeholder "topic"
[react/text-input {:flex 1
:ref #(when (and (nil? @topic-input-ref) %)
(.setNativeProps % (js-obj "text" "#"))
(reset! topic-input-ref %))
:style styles/add-contact-input
:on-change (fn [e]
(let [native-event (.-nativeEvent e)
text (.-text native-event)]
(re-frame/dispatch [:set :public-group-topic text])))}]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:create-new-public-chat topic])}
[react/view {:style {:margin-left 16 :width 30 :height 30 :border-radius 15 :background-color "#eef2f5" :align-items :center
:justify-content :center}}
[icons/icon :icons/ok]]]]]]))
text (.-text native-event)
[_ before after] (first (re-seq #"(.*)\#(.*)" text))]
(.setNativeProps @topic-input-ref (js-obj "text" (str "#" before after)))
(re-frame/dispatch [:set :public-group-topic (subs text 1)])))}]]
[react/touchable-highlight {:disabled topic-error
:on-press #(when-not topic-error
(do
(re-frame/dispatch [:set :public-group-topic nil])
(re-frame/dispatch [:create-new-public-chat topic])))}
[react/view {:style (styles/add-contact-button topic-error)}
[react/text {:style (styles/add-contact-button-text topic-error)}
(i18n/label :new-public-group-chat)]]]]
[react/text {:style styles/new-contact-subtitle} (i18n/label :selected-for-you)]
[react/view {:style styles/suggested-contacts}
(doall
(for [topic public-chat/default-public-chats]
^{:key topic}
[react/touchable-highlight {:on-press #(do
(re-frame/dispatch [:set :public-group-topic nil])
(re-frame/dispatch [:create-new-public-chat topic]))}
[react/view {:style styles/suggested-contact-view}
[react/view {:style styles/suggested-topic-image}
[react/text {:style styles/suggested-topic-text} (string/capitalize (first topic))]]
[react/text {:style styles/new-contact-subtitle} topic]]]))]]]))

View File

@ -0,0 +1,116 @@
(ns status-im.ui.screens.desktop.main.chat.styles
(:require [status-im.ui.components.colors :as colors]))
(defn message-box [{:keys [outgoing] :as message}]
(let [align (if outgoing :flex-end :flex-start)
color (if outgoing colors/hawkes-blue colors/white)]
{:align-self align
:background-color color
:border-radius 8
:padding-left 12
:padding-right 12
:padding-top 10
:padding-bottom 10
:max-width 340}))
(defn message-row [{:keys [outgoing first-in-group?] :as message}]
(let [padding-horizontal (if outgoing :padding-right :padding-left)
padding-top-value (if first-in-group? 16 8)]
{padding-horizontal 24
:padding-top padding-top-value}))
(def message-row-column
{:flex-direction :column})
(defn message-timestamp-placeholder []
{:color colors/transparent
:font-size 10
:align-self :flex-end
:opacity 0.5
:text-align :right
:text-align-vertical :center
:width 60})
(defn message-timestamp []
(merge (message-timestamp-placeholder)
{:color colors/gray
:position :absolute
:bottom 0
:right -5}))
(def author
{:color colors/gray
:font-size 12
:margin-left 48
:margin-bottom 4})
(def chat-box
{:height 68
:background-color :white
:border-radius 12
:margin-horizontal 24
:padding-vertical 15})
(def chat-box-inner
{:flex-direction :row
:flex 1})
(def chat-text-input
{:flex 1})
(def messages-view
{:flex 1
:background-color colors/gray-lighter})
(def messages-scrollview-inner
{:padding-vertical 46})
(def photo-style
{:borderRadius 20
:width 40
:height 40
:margin-right 8})
(def toolbar-chat-view
{:align-items :center
:padding 11
:justify-content :center})
(def toolbar-chat-name
{:font-size 16
:color :black
:font-weight "600"})
(def add-contact
{:background-color :white
:border-radius 6
:margin-top 3
:padding 4})
(def add-contact-text
{:font-size 14
:color colors/gray})
(def message-text
{:font-size 14})
(def message-wrapper
{:flex-direction :row
:flex-wrap :wrap})
(def not-first-in-group-wrapper
{:flex-direction :row})
(def send-icon
{:margin-left 16
:width 30
:height 30
:border-radius 15
:background-color colors/gray-lighter
:align-items :center
:justify-content :center
:transform [{:rotate "90deg"}]})
(def chat-view
{:flex 1
:background-color :white})

View File

@ -5,12 +5,16 @@
[clojure.string :as string]
[status-im.chat.styles.message.message :as message.style]
[status-im.utils.gfycat.core :as gfycat.core]
[taoensso.timbre :as log]
[status-im.utils.gfycat.core :as gfycat]
[status-im.constants :as constants]
[status-im.utils.identicon :as identicon]
[status-im.utils.datetime :as time]
[status-im.ui.components.styles :as styles]
[status-im.ui.components.react :as react]))
[status-im.ui.components.react :as react]
[status-im.ui.components.colors :as colors]
[status-im.chat.views.message.datemark :as message.datemark]
[status-im.ui.screens.desktop.main.chat.styles :as styles]
[status-im.i18n :as i18n]))
(views/defview toolbar-chat-view []
(views/letsubs [{:keys [chat-id name public-key public? group-chat]} [:get-current-chat]
@ -20,43 +24,33 @@
(if (string/blank? name)
(gfycat.core/generate-gfy public-key)
(or name
"Chat name")))]
[react/view {:style {:align-items :center :padding 11 :justify-content :center}}
(i18n/label :t/chat-name))))]
[react/view {:style styles/toolbar-chat-view}
[react/view {:style {:flex-direction :row}}
(when public?
[icons/icon :icons/public-chat])
(when (and group-chat (not public?))
[icons/icon :icons/group-chat])
[react/text {:style {:font-size 16 :color :black :font-weight "600"}}
[react/text {:style styles/toolbar-chat-name}
chat-name]]
(when pending?
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:add-pending-contact chat-id])}
[react/view {:style {:background-color :white :border-radius 6 :margin-top 3 :padding 4}} ;style/add-contact
[react/text {:style {:font-size 14 :color "#939ba1"}}
"Add to contacts"]]])])))
[react/view {:style styles/add-contact} ;style/add-contact
[react/text {:style styles/add-contact-text}
(i18n/label :t/add-to-contacts)]]])])))
(views/defview message-author-name [{:keys [outgoing from] :as message}]
(views/letsubs [current-account [:get-current-account]
incoming-name [:get-contact-name-by-identity from]]
(if outgoing
[react/text {:style message.style/author} (:name current-account)]
(let [name (or incoming-name (gfycat/generate-gfy from))]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:show-contact-dialog from name (boolean incoming-name)])}
[react/text {:style message.style/author} name]]))))
(def photo-style
{:borderRadius 15
:width 30
:height 30})
[react/text {:style styles/author} name]])))
(views/defview member-photo [from]
(views/letsubs [photo-path nil];[:photo-path from]]
[react/view
[react/image {:source {:uri (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)}
:style photo-style}]]))
[react/image {:source {:uri (identicon/identicon from)}
:style styles/photo-style}]])
(views/defview my-photo [from]
(views/letsubs [account [:get-current-account]]
@ -65,16 +59,42 @@
[react/image {:source {:uri (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)}
:style photo-style}]])))
:style styles/photo-style}]])))
(defn message [text me? {:keys [outgoing message-id chat-id message-status user-statuses timestamp
from current-public-key content-type group-chat type value] :as message}]
(views/defview message-with-timestamp [text {:keys [timestamp] :as message} style]
[react/view {:style style}
[react/view {:style styles/message-wrapper}
[react/text {:style styles/message-text
:selectable true}
text]
[react/text {:style (styles/message-timestamp-placeholder)}
(time/timestamp->time timestamp)]
[react/text {:style (styles/message-timestamp)}
(time/timestamp->time timestamp)]]])
(views/defview text-only-message [text message]
[react/view {:style (styles/message-row message)}
[message-with-timestamp text message (styles/message-box message)]])
(views/defview photo-placeholder []
[react/view {:style styles/photo-style}])
(views/defview message-with-name-and-avatar [text {:keys [from first-in-group? last-in-group?] :as message}]
[react/view {:style (styles/message-row message)}
[react/view {:style styles/message-row-column}
(when first-in-group?
[message-author-name message])
[react/view {:style styles/not-first-in-group-wrapper}
(if last-in-group?
[member-photo from]
[photo-placeholder])
[message-with-timestamp text message (styles/message-box message)]]]])
(defn message [text me? {:keys [message-id chat-id message-status user-statuses from
current-public-key content-type group-chat outgoing type value] :as message}]
(if (= type :datemark)
^{:key (str "datemark" message-id)}
[react/view {:style {:margin-vertical 20}}
[react/text
(string/capitalize (last (string/split value #"-")))]
[react/view {:style {:height 1 :background-color "#e8ebec"}}]]
[message.datemark/chat-datemark value]
(when (= content-type constants/text-content-type)
(reagent.core/create-class
{:component-did-mount
@ -89,19 +109,9 @@
:reagent-render
(fn []
^{:key (str "message" message-id)}
[react/view {:style {:flex-direction :row :flex 1 :margin-vertical 12}}
(if outgoing
[my-photo from]
[member-photo from])
[react/view {:style {:padding-horizontal 12 :background-color :white :border-radius 8 :flex 1}}
[react/view {:style {:flex-direction :row}}
[message-author-name message]
[react/view {:style {:flex 1}}]
[react/text {:style {:color styles/color-gray4 :font-size 12}} (time/timestamp->time timestamp)]]
;;TODO use https://github.com/status-im/status-react/pull/3299
;;[rn-hl/hyperlink {:linkStyle {:color "#2980b9"} :on-press #(re-frame/dispatch [:show-link-dialog %1])}
[react/text
text]]])}))))
(if (and group-chat (not outgoing))
[message-with-name-and-avatar text message]
[text-only-message text message]))}))))
(views/defview messages-view [{:keys [chat-id group-chat]}]
(views/letsubs [chat-id* (atom nil)
@ -113,7 +123,7 @@
(js/setTimeout #(when scroll-ref (.scrollToEnd @scroll-ref)) 400))
messages (re-frame/subscribe [:get-current-chat-messages-stream])
current-public-key (re-frame/subscribe [:get-current-public-key])]
[react/view {:style {:flex 1 :background-color :white :margin-horizontal 16}}
[react/view {:style styles/messages-view}
[react/scroll-view {:scrollEventThrottle 16
:on-scroll (fn [e]
(let [ne (.-nativeEvent e)
@ -122,52 +132,48 @@
(when @scroll-timer (js/clearTimeout @scroll-timer))
(reset! scroll-timer (js/setTimeout #(re-frame/dispatch [:load-more-messages]) 300)))
(reset! scroll-height (+ y (.-height (.-layoutMeasurement ne))))))
:on-content-size-change #(when (or (not @scroll-height) (< (- %2 @scroll-height) 500))
(.scrollToEnd @scroll-ref))
:on-content-size-change #(.scrollToEnd @scroll-ref)
:ref #(reset! scroll-ref %)}
[react/view {:style {:padding-vertical 60}}
[react/view {:style styles/messages-scrollview-inner}
(doall
(for [[index {:keys [from content message-id] :as message-obj}] (map-indexed vector (reverse @messages))]
^{:key (str message index)}
(for [[index {:keys [from content message-id type value] :as message-obj}] (map-indexed vector (reverse @messages))]
^{:key (or message-id (str type value))}
[message content (= from @current-public-key) (assoc message-obj :group-chat group-chat)]))]]])))
(views/defview chat-text-input []
(views/letsubs [{:keys [input-text]} [:get-current-chat]
inp-ref (atom nil)]
[react/view {:style {:height 90 :margin-horizontal 16 :background-color :white :border-radius 12}}
[react/view {:style {:flex-direction :row :margin-horizontal 16 :margin-top 16 :flex 1 :margin-bottom 16}}
(views/letsubs [inp-ref (atom nil)]
[react/view {:style styles/chat-box}
[react/view {:style styles/chat-box-inner}
[react/view {:style {:flex 1}}
[react/text-input {:default-value (or input-text "")
:placeholder "Type a message..."
[react/text-input {:placeholder (i18n/label :t/type-a-message)
:auto-focus true
:multiline true
:blur-on-submit true
:style {:flex 1}
:style styles/chat-text-input
:ref #(reset! inp-ref %)
:on-key-press (fn [e]
(let [native-event (.-nativeEvent e)
key (.-key native-event)]
(when (= key "Enter")
(js/setTimeout #(do
key (.-key native-event)
modifiers (js->clj (.-modifiers native-event))
should-send (and (= key "Enter") (not (contains? (set modifiers) "shift")))]
(when should-send
(.clear @inp-ref)
(.focus @inp-ref)) 200)
(.focus @inp-ref)
(re-frame/dispatch [:send-current-message]))))
:on-change (fn [e]
(let [native-event (.-nativeEvent e)
text (.-text native-event)]
(re-frame/dispatch [:set-chat-input-text text])))}]]
[react/touchable-highlight {:on-press (fn []
(js/setTimeout #(do (.clear @inp-ref) (.focus @inp-ref)) 200)
(.clear @inp-ref)
(.focus @inp-ref)
(re-frame/dispatch [:send-current-message]))}
[react/view {:style {:margin-left 16 :width 30 :height 30 :border-radius 15 :background-color "#eef2f5" :align-items :center
:justify-content :center :transform [{:rotate "90deg"}]}}
[react/view {:style styles/send-icon}
[icons/icon :icons/arrow-left]]]]]))
(views/defview chat-view []
(views/letsubs [current-chat [:get-current-chat]]
[react/view {:style {:flex 1 :background-color :white}}
[react/view {:style styles/chat-view}
[toolbar-chat-view]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[messages-view current-chat]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[chat-text-input]]))

View File

@ -0,0 +1,14 @@
(ns status-im.ui.screens.desktop.main.styles
(:require [status-im.ui.components.colors :as colors]))
(def main-views
{:flex 1
:flex-direction :row})
(def left-sidebar
{:width 340
:background-color colors/white})
(def pane-separator
{:width 1
:background-color colors/gray-light})

View File

@ -0,0 +1,87 @@
(ns status-im.ui.screens.desktop.main.tabs.home.styles
(:require [status-im.ui.components.colors :as colors]))
(def chat-list-view
{:flex 1
:background-color colors/white})
(defn chat-list-item [current?]
{:padding-horizontal 16
:flex-direction :row
:flex 1
:justify-content :space-between
:background-color (if current? colors/gray-lighter colors/white)
:align-items :center})
(def chat-list-header
{:flex-direction :row
:align-items :center
:padding 11})
(def img-container
{:height 78
:justify-content :center})
(def chat-icon
{:width 46
:height 46
:border-radius 46
:margin-right 16})
(def unread-messages-icon
{:position :absolute
:width 22
:height 22
:border-radius 22
:left 28
:top 10
:justify-content :center
:align-items :center
:background-color colors/blue})
(defn unread-messages-text [large?]
{:color colors/white
:font-size (if large? 10 12)})
(def chat-list-separator
{:height 1
:background-color colors/gray-light})
(def chat-name-box
{:flex-direction :row
:align-items :center})
(def chat-name-last-msg-box
{:flex 1
:padding-vertical 16})
(defn chat-name [current?]
{:font-size 14
:font-weight (if current? "600" :normal)})
(def chat-last-message
{:color colors/gray
:font-size 14})
(def timestamp
{:justify-content :flex-start
:align-items :flex-end
:padding-vertical 16})
(def add-new
{:background-color colors/blue
:width 34
:height 34
:border-radius 34
:justify-content :center
:align-items :center})
(defn topic-image [color]
(merge chat-icon
{:background-color color
:align-items :center
:justify-content :center}))
(def topic-text
{:font-size 25.6
:color colors/white})

View File

@ -2,6 +2,12 @@
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.utils.gfycat.core :as gfycat]
[status-im.i18n :as i18n]
[status-im.ui.screens.desktop.main.tabs.home.styles :as styles]
[clojure.string :as string]
[status-im.ui.screens.home.views.inner-item :as chat-item]
[taoensso.timbre :as log]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.react :as react]))
@ -12,36 +18,60 @@
[react/text {:font :medium}
@unviewed-messages-count]])))
(views/defview chat-list-item-inner-view [{:keys [chat-id name group-chat public? public-key]}]
(let [name (str
(if public? "#" "")
(or name
(gfycat/generate-gfy public-key)))]
[react/view {:style {:padding 12 :background-color :white :flex-direction :row :align-items :center}}
(when public?
[icons/icon :icons/public-chat])
(views/defview chat-list-item-inner-view [{:keys [chat-id name group-chat color public? public-key] :as chat-item}]
(letsubs [photo-path [:get-chat-photo chat-id]
unviewed-messages-count [:unviewed-messages-count chat-id]
chat-name [:get-chat-name chat-id]
current-chat-id [:get-current-chat-id]
last-message [:get-last-message chat-id]]
(let [name (or chat-name
(gfycat/generate-gfy public-key))
[unviewed-messages-label large?] (if (< 99 unviewed-messages-count)
["99+" true]
[unviewed-messages-count false])]
[react/view {:style (styles/chat-list-item (= current-chat-id chat-id))}
[react/view {:style styles/img-container}
(if public?
[react/view {:style (styles/topic-image color)}
[react/text {:style styles/topic-text}
(string/capitalize (second name))]]
[react/image {:style styles/chat-icon
:source {:uri photo-path}}])
(when (pos? unviewed-messages-count)
[react/view {:style styles/unread-messages-icon}
[react/text {:style (styles/unread-messages-text large?)} unviewed-messages-label]])]
[react/view {:style styles/chat-name-last-msg-box}
[react/view {:style styles/chat-name-box}
(when (and group-chat (not public?))
[icons/icon :icons/group-chat])
[react/text
name]
[react/view {:style {:flex 1}}]
[unviewed-indicator chat-id]]))
(when public?
[icons/icon :icons/public-chat])
[react/text {:ellipsize-mode :tail
:number-of-lines 1
:style (styles/chat-name (= current-chat-id chat-id))}
name]]
[react/text {:ellipsize-mode :tail
:number-of-lines 1
:style styles/chat-last-message}
(or (:content last-message) (i18n/label :no-messages-yet))]]
[react/view {:style styles/timestamp}
[chat-item/message-timestamp (:timestamp last-message)]]])))
(defn chat-list-item [[chat-id chat]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat chat-id])}
[react/view
[chat-list-item-inner-view (assoc chat :chat-id chat-id)]]])
[chat-list-item-inner-view (assoc chat :chat-id chat-id)]])
(views/defview chat-list-view []
(views/letsubs [home-items [:home-items]]
[react/view {:style {:flex 1 :background-color :white}}
[react/view {:style {:align-items :center :flex-direction :row :padding 11}}
[react/view {:style styles/chat-list-view}
[react/view {:style styles/chat-list-header}
[react/view {:style {:flex 1}}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :new-contact])}
[icons/icon :icons/add]]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/view {:style styles/add-new}
[icons/icon :icons/add {:style {:tint-color :white}}]]]]
[react/view {:style styles/chat-list-separator}]
[react/scroll-view
[react/view
(for [[index chat] (map-indexed vector home-items)]
^{:key (str chat index)}
^{:key (first chat)}
[chat-list-item chat])]]]))

View File

@ -0,0 +1,135 @@
(ns status-im.ui.screens.desktop.main.tabs.profile.styles
(:require [status-im.ui.components.colors :as colors]))
(def profile-view
{:align-items :center})
(def profile-badge
{:margin-top 34
:align-items :center
:margin-bottom 16})
(def logout-row
{:justify-content :space-between
:flex-direction :row
:margin-horizontal 24
:align-self :stretch
:margin-top 60})
(defn logout-row-text [color]
{:color color
:font-size 16})
(def profile-photo
{:border-radius 100
:width 100
:height 100})
(def profile-user-name
{:font-weight :bold
:font-size 18})
(def share-contact-code
{:flex-direction :row
:justify-content :space-between
:align-items :center
:height 45
:width 240
:margin-horizontal 50
:border-radius 8
:background-color (colors/alpha colors/blue 0.1)})
(def share-contact-code-text-container
{:margin-left 32
:justify-content :center
:align-items :center})
(def share-contact-code-text
{:color colors/blue
:font-size 14})
(def share-contact-icon-container
{:margin-right 12
:width 22
:height 22
:align-items :center
:justify-content :center})
(def qr-code-container
{:align-items :center
:padding-top 16
:padding-bottom 46
:padding-left 58
:padding-right 58})
(def close-icon-container
{:flex 1
:margin-top 22
:margin-right 22
:margin-bottom 16
:flex-direction :row
:justify-content :flex-end})
(def close-icon
{:height 24
:width 24
:tint-color colors/gray-icon})
(def check-icon
{:height 16
:width 16
:margin-right 8
:tint-color colors/tooltip-green-text})
(def qr-code-title
{:font-size 20
:font-weight "600"
:margin-bottom 32})
(def qr-code
{:width 130
:height 130
:margin-bottom 16})
(def qr-code-text
{:font-size 16
:text-align :center
:margin-bottom 16})
(def qr-code-copy
{:width 185
:height 45
:border-radius 8
:background-color colors/blue
:justify-content :center
:align-items :center})
(def qr-code-copy-text
{:font-size 16
:color colors/white})
(defn tooltip-container [opacity]
{:position :absolute
:align-items :center
:opacity opacity
:top -34})
(def tooltip-icon-text
{:flex-direction :row
:justify-content :space-between
:align-items :center
:height 24
:border-radius 8
:padding-left 10
:padding-right 10
:background-color colors/tooltip-green})
(def tooltip-triangle
{:width 0
:height 0
:border-top-width 9.1
:border-left-width 9.1
:border-right-width 9.1
:border-left-color :transparent
:border-right-color :transparent
:border-top-color colors/tooltip-green})

View File

@ -2,40 +2,73 @@
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.ui.components.react :as react]
[status-im.ui.screens.profile.user.views :as profile]
[status-im.utils.build :as build]
[status-im.utils.utils :as utils]
[status-im.ui.components.colors :as colors]
[status-im.i18n :as i18n]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[taoensso.timbre :as log]
[clojure.string :as string]
[status-im.ui.components.qr-code-viewer.views :as qr-code-viewer]
[status-im.ui.screens.desktop.main.tabs.profile.styles :as styles]
[status-im.ui.screens.profile.user.views :as profile]))
(defn profile-badge [{:keys [name]}]
[react/view {:margin-vertical 10}
[react/text {:style {:font-weight :bold}
(defn profile-badge [{:keys [name photo-path]}]
[react/view styles/profile-badge
[react/image {:source {:uri photo-path}
:style styles/profile-photo}]
[react/text {:style styles/profile-user-name
:number-of-lines 1}
name]])
(defn profile-info-item [{:keys [label value]}]
[react/view
[react/view
[react/text
label]
[react/view {:height 10}]
[react/text {:number-of-lines 1
:ellipsizeMode :middle}
value]]])
(views/defview copied-tooltip [opacity]
(views/letsubs []
[react/view {:style (styles/tooltip-container opacity)}
[react/view {:style styles/tooltip-icon-text}
[vector-icons/icon :icons/check
{:style styles/check-icon}]
[react/text {:style {:font-size 14 :color colors/tooltip-green-text}}
(i18n/label :sharing-copied-to-clipboard)]]
[react/view {:style styles/tooltip-triangle}]]))
(defn my-profile-info [{:keys [public-key]}]
(views/defview qr-code []
(views/letsubs [{:keys [public-key]} [:get-current-account]
tooltip-opacity [:get-in [:tooltips :qr-copied]]]
[react/view
[profile-info-item
{:label "Contact Key"
:value public-key}]])
[react/view {:style styles/qr-code-container}
[react/text {:style styles/qr-code-title}
(string/replace (i18n/label :qr-code-public-key-hint) "\n" "")]
[react/view {:style styles/qr-code}
[qr-code-viewer/qr-code {:value public-key :size 130}]]
[react/view {:style {:align-items :center}}
[react/text {:style styles/qr-code-text}
public-key]
(when tooltip-opacity
[copied-tooltip tooltip-opacity])]
[react/touchable-highlight {:on-press #(do
(re-frame/dispatch [:copy-to-clipboard public-key])
(re-frame/dispatch [:show-tooltip :qr-copied]))}
[react/view {:style styles/qr-code-copy}
[react/text {:style styles/qr-code-copy-text}
(i18n/label :copy-qr)]]]]]))
(defn share-contact-code []
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :qr-code])}
[react/view {:style styles/share-contact-code}
[react/view {:style styles/share-contact-code-text-container}
[react/text {:style styles/share-contact-code-text}
(i18n/label :share-contact-code)]]
[react/view {:style styles/share-contact-icon-container
:accessibility-label :share-my-contact-code-button}
[vector-icons/icon :icons/qr {:style {:tint-color colors/blue}}]]]])
(views/defview profile []
(views/letsubs [current-account [:get-current-account]]
[react/view {:margin-top 40 :margin-horizontal 10}
[react/view
[profile-badge current-account]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/view
[my-profile-info current-account]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:logout])
:style {:margin-top 60}}
[react/view
[react/text {:style {:color :red}} "Log out"]]]]))
[react/view styles/profile-view
[profile-badge current-account]
[share-contact-code]
[react/view {:style styles/logout-row}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:logout])}
[react/text {:style (styles/logout-row-text colors/red)} (i18n/label :t/logout)]]
[react/view [react/text {:style (styles/logout-row-text colors/gray)} "V" build/version]]]]))

View File

@ -2,6 +2,7 @@
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.screens.desktop.main.tabs.profile.views :as profile.views]
[status-im.ui.screens.desktop.main.tabs.home.views :as home.views]
[status-im.ui.screens.desktop.main.styles :as styles]
[status-im.ui.screens.desktop.main.chat.views :as chat.views]
[status-im.ui.screens.desktop.main.add-new.views :as add-new.views]
[status-im.ui.components.desktop.tabs :as tabs]
@ -26,15 +27,16 @@
(let [component (case view-id
:chat chat.views/chat-view
:new-contact add-new.views/new-contact
:qr-code profile.views/qr-code
status-view)]
[react/view {:style {:flex 1}}
[component]])))
(views/defview main-views []
[react/view {:style {:flex 1 :flex-direction :row}}
[react/view {:style {:width 280 :background-color :white}}
[react/view {:style styles/main-views}
[react/view {:style styles/left-sidebar}
[react/view {:style {:flex 1}}
[tab-views]]
[tabs/main-tabs]]
[react/view {:style {:width 1 :background-color "#e8ebec"}}]
[react/view {:style styles/pane-separator}]
[main-view]])

View File

@ -8,6 +8,8 @@
[status-im.ui.screens.accounts.recover.views :as recover.views]
[status-im.ui.screens.accounts.views :as accounts.views]))
(enable-console-print!)
(views/defview main []
(views/letsubs [view-id [:get :view-id]]
(let [component (case view-id
@ -15,7 +17,7 @@
:accounts accounts.views/accounts
:recover recover.views/recover
:create-account create.views/create-account
(:new-contact :chat :home) main.views/main-views
(:new-contact :chat :home :qr-code) main.views/main-views
:login login.views/login
react/view)]
[react/view {:style {:flex 1}}

View File

@ -345,8 +345,8 @@
[:process-pending-messages]
[:update-wallet]
[:update-transactions]
(when platform/mobile? [:get-fcm-token])
[:sync-wallet-transactions]
[:get-fcm-token]
[:update-sign-in-time]]
(seq events-after) (into events-after))}))

View File

@ -107,6 +107,7 @@
(defstyle datetime-text
{:color component.styles/text4-color
:android {:font-size 14}
:desktop {:font-size 14}
:ios {:font-size 15}})
(def new-messages-container

View File

@ -3,7 +3,12 @@
[status-im.utils.platform :as platform])
(:require-macros [status-im.utils.styles :refer [defnstyle]]))
(def tabs-height (if platform/ios? 52 56))
(def tabs-height
(cond
platform/android? 56
platform/ios? 52
platform/desktop? 68))
(def tab-height (dec tabs-height))
(def tabs-container

View File

@ -1,7 +1,8 @@
(ns status-im.ui.screens.profile.events
(:require [clojure.spec.alpha :as spec]
[re-frame.core :as re-frame]
[status-im.ui.components.react :refer [show-image-picker]]
[status-im.ui.components.react :as react]
[status-im.chat.constants :as chat-const]
[status-im.ui.screens.profile.navigation]
[status-im.ui.screens.accounts.utils :as accounts.utils]
[status-im.chat.events :as chat-events]
@ -15,7 +16,7 @@
:open-image-picker
;; the image picker is only used here for now, this effect can be use in other scenarios as well
(fn [callback-event]
(show-image-picker
(react/show-image-picker
(fn [image]
(let [path (get (js->clj image) "path")
_ (log/debug path)
@ -114,3 +115,40 @@
(handlers-macro/merge-fx cofx
{:db (update db :my-profile/seed assoc :step :finish :error nil :word nil)}
(accounts.utils/clean-seed-phrase))))
(re-frame/reg-fx
:copy-to-clipboard
(fn [value]
(react/copy-to-clipboard value)))
(handlers/register-handler-fx
:copy-to-clipboard
(fn [_ [_ value]]
{:copy-to-clipboard value}))
(re-frame/reg-fx
:show-tooltip
(let [tooltips (atom {})]
(fn [tooltip-id]
(when-let [{:keys [interval-id]} (@tooltips tooltip-id)]
(js/clearInterval interval-id))
(let [interval-id (js/setInterval
#(let [{:keys [opacity interval-id cnt]} (@tooltips tooltip-id)]
(when opacity
(swap! tooltips assoc-in [tooltip-id :cnt] (inc cnt))
(if (and opacity (>= 0.0 opacity))
(do
(log/debug "remove interval:" interval-id)
(js/clearInterval interval-id)
(re-frame/dispatch [:set-in [:tooltips tooltip-id] nil])
(swap! tooltips dissoc interval-id))
(do (re-frame/dispatch [:set-in [:tooltips tooltip-id] opacity])
(when (< 10 cnt)
(swap! tooltips assoc-in [tooltip-id :opacity] (- opacity 0.05)))))))
100)]
(swap! tooltips assoc tooltip-id {:opacity 1.0 :interval-id interval-id :cnt 0})))))
(handlers/register-handler-fx
:show-tooltip
(fn [_ [_ tooltip-id]]
{:show-tooltip tooltip-id}))

View File

@ -1,7 +1,8 @@
(ns status-im.ui.screens.profile.subs
(:require [re-frame.core :refer [reg-sub]]
[clojure.string :as string]
[status-im.utils.build :as build]))
[status-im.utils.build :as build]
[status-im.utils.platform :as platform]))
(reg-sub
:get-profile-unread-messages-number
@ -12,7 +13,8 @@
(reg-sub
:get-app-version
(fn [{:keys [web3-node-version]}]
(str build/version " (" build/build-no ")\nnode " (or web3-node-version "N/A") "")))
(let [version (if platform/desktop? build/version build/build-no)]
(str build/version " (" version ")\nnode " (or web3-node-version "N/A") ""))))
(reg-sub :get-device-UUID
(fn [db]

View File

@ -17,6 +17,7 @@
status-im.ui.screens.currency-settings.subs
status-im.ui.screens.browser.subs
status-im.ui.screens.add-new.new-chat.subs
status-im.ui.screens.add-new.new-public-chat.subs
status-im.ui.screens.profile.subs))
(reg-sub :get

View File

@ -1,7 +1,8 @@
(ns status-im.utils.keychain.core
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.react-native.js-dependencies :as rn]))
[status-im.react-native.js-dependencies :as rn]
[status-im.utils.platform :as platform]))
(def key-bytes 64)
(def username "status-im.encryptionkey")
@ -73,3 +74,6 @@
(defn reset []
(log/debug "resetting key...")
(.resetGenericPassword rn/keychain))
(defn set-username []
(when platform/desktop? (.setUsername rn/keychain username)))

View File

@ -1,7 +1,8 @@
(ns status-im.utils.keychain.events
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.utils.keychain.core :as keychain]))
[status-im.utils.keychain.core :as keychain]
[status-im.utils.platform :as platform]))
(defn handle-key-error [event {:keys [error key]}]
(if (= :weak-key error)
@ -13,6 +14,7 @@
(re-frame/reg-fx
:get-encryption-key
(fn [event]
(when platform/desktop? (keychain/set-username))
(.. (keychain/get-encryption-key)
(then #(re-frame/dispatch (conj event %)))
(catch (partial handle-key-error event)))))

View File

@ -6,7 +6,8 @@
[status-im.utils.config :as config]
[status-im.utils.utils :as utils]
[status-im.ui.components.react :refer [copy-to-clipboard]]
[taoensso.timbre :as log]))
[taoensso.timbre :as log]
[status-im.utils.platform :as platform]))
;; Work in progress namespace responsible for push notifications and interacting
;; with Firebase Cloud Messaging.
@ -28,6 +29,8 @@
;; NOTE: Only need to explicitly request permissions on iOS.
(defn request-permissions []
(if platform/desktop?
(dispatch [:request-notifications-granted {}])
(-> (.requestPermissions (.-default rn/react-native-fcm))
(.then
(fn [_]
@ -35,7 +38,7 @@
(dispatch [:request-notifications-granted {}]))
(fn [_]
(log/debug "notifications-denied")
(dispatch [:request-notifications-denied {}])))))
(dispatch [:request-notifications-denied {}]))))))
(defn get-fcm-token []
(-> (.getFCMToken (object/get rn/react-native-fcm "default"))

View File

@ -17,6 +17,8 @@
(def android? (= os "android"))
(def ios? (= os "ios"))
(def desktop? (= os "desktop"))
(def mobile? (not= os "desktop"))
(def iphone-x? (and ios? (ios/iphone-x-dimensions?)))
(def platform-specific

View File

@ -17,7 +17,7 @@
(defn body [style]
`(let [style# ~style
common# (dissoc style# :android :ios)
common# (dissoc style# :android :ios :desktop)
platform# (keyword status-im.utils.platform/os)
platform-specific# (get style# platform#)]
(if platform-specific#

125
ubuntu-server.js Executable file
View File

@ -0,0 +1,125 @@
#!/usr/bin/env node
/**
* Copyright (C) 2016, Canonical Ltd.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
console.debug = console.log;
var net = require('net');
var repl = require('repl');
var vm = require('vm');
var util = require('util');
var Buffer = require('buffer').Buffer;
var realmConstructor = require("./node_modules/realm/lib/index.js");
console.log("Loaded realmConstructor: " + realmConstructor);
var DEBUG = 1;
function rnUbuntuServer(readable, writable) {
console.reportErrorsAsExceptions = false; // XXX:
var sandbox = { console: console, util: util, outerRealmConstructor: realmConstructor };
vm.createContext(sandbox);
var state = 'start';
var length = 0;
var buffer = new Buffer(0);
var internalEval = function(code) {
DEBUG > 3 && console.error("-- internalEval: executing script(length=" + code.length + "): " + code.slice(0, 80) + " ... " + code.slice(-80));
DEBUG > 3 && console.error("-- before sandbox=" + util.inspect(sandbox, { colors: true, depth: null }));
var result = vm.runInContext(code, sandbox);
DEBUG > 3 && console.error("-- internalEval: result = " + result);
DEBUG > 3 && console.error("-- after sandbox=" + util.inspect(sandbox, { colors: true, depth: null }));
return result;
};
var sendResponse = function(result) {
function sendResponsePacket(response) {
const sizeBuf = new Buffer(4);
const dataBuf = new Buffer(response);
sizeBuf.writeUInt32LE(dataBuf.length, 0);
writable.write(sizeBuf);
writable.write(dataBuf);
}
var stringifiedResult = JSON.stringify(result);
DEBUG > 3 && console.error("-- sending result=" + stringifiedResult);
if (stringifiedResult === undefined) {
sendResponsePacket('undefined');
return;
}
sendResponsePacket(stringifiedResult);
}
readable.on('error', function (exc) {
console.warn("ignoring exception: " + exc);
});
readable.on('data', function(chunk) {
DEBUG > 2 && console.error("-- Data received from RN Client: state = " + state)
DEBUG > 2 && console.error("-- chunk length: " + chunk.length)
DEBUG > 2 && console.error("-- buffer length(original): " + buffer.length)
if (chunk == null || state === 'eof')
return;
buffer = Buffer.concat([buffer, chunk]);
DEBUG > 2 && console.error("-- buffer length(concat): " + buffer.length)
while(true) {
if (state === 'start') {
if (buffer.length < 4)
return;
length = buffer.readUInt32LE(0);
DEBUG > 2 && console.error("-- New Packet: length=" + length);
if (buffer.length >= length + 4) {
var result = internalEval(buffer.toString('utf8', 4, length + 4));
var tmpBuffer = new Buffer(buffer.length - 4 - length);
buffer.copy(tmpBuffer, 0, length + 4, buffer.length);
buffer = tmpBuffer;
sendResponse(result);
} else {
state = 'script';
}
}
if (state === 'script') {
DEBUG > 2 && console.error("-- Packet length: " + length);
if (buffer.length >= length + 4) {
var result = internalEval(buffer.toString('utf8', 4, length + 4));
var tmpBuffer = new Buffer(buffer.length - 4 - length);
buffer.copy(tmpBuffer, 0, length + 4, buffer.length);
buffer = tmpBuffer;
state = 'start';
sendResponse(result);
} else {
return;
}
}
}
});
readable.on('end', function() {
state = 'eof';
DEBUG && console.error("-- Session ended");
});
}
if (process.argv.indexOf('--pipe') != -1) {
console.log = console.error
rnUbuntuServer(process.stdin, process.stdout);
} else {
var server = net.createServer((sock) => {
DEBUG && console.error("-- Connection from RN client");
rnUbuntuServer(sock, sock);
}).listen(5000, function() { console.error("-- Server starting") });
}