Compare commits

..
1 Commits
Author SHA1 Message Date
Omar Basem 9bbeac1ed5 updates 2023-06-26 13:42:11 +04:00
270 changed files with 2770 additions and 2660 deletions
+37
View File
@@ -0,0 +1,37 @@
module.exports = {
testRunner: 'jest',
testRegex: '\\.visual\\.js$',
'runner-config': 'visual-test/config.json',
devices: {
simulator: {
type: 'ios.simulator',
device: {
type: 'iPhone 11 Pro',
},
},
},
apps: {
'ios.release': {
name: 'StatusIm',
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/StatusIm.app',
build: 'make release-ios',
},
'ios.debug': {
name: 'StatusIm',
type: 'ios.app',
binaryPath: process.env.TEST_BINARY_PATH,
build: "make run-ios SIMULATOR='iPhone 11 Pro'",
},
},
configurations: {
'ios.sim.release': {
device: 'simulator',
app: 'ios.release',
},
'ios.sim.debug': {
device: 'simulator',
app: 'ios.debug',
},
},
};
+8
View File
@@ -343,6 +343,14 @@ test: ##@test Run tests once in NodeJS
yarn shadow-cljs compile test && \
node --require ./test-resources/override.js target/test/test.js
run-visual-test-ios: export TARGET := clojure
run-visual-test-ios: XCODE_DERIVED_DATA := $(HOME)/Library/Developer/Xcode/DerivedData
run-visual-test-ios: APPLICATION_NAME := StatusIm-brfnruzfrkkycpbndmdoeyrigthc
run-visual-test-ios: export TEST_BINARY_PATH := $(XCODE_DERIVED_DATA)/$(APPLICATION_NAME)/Build/Products/Debug-iphonesimulator/StatusIm.app
run-visual-test-ios: ##@test Run tests once in NodeJS
detox test --configuration ios.sim.debug
component-test-watch: export TARGET := clojure
component-test-watch: export COMPONENT_TEST := true
component-test-watch: export BABEL_ENV := test
+1 -1
View File
@@ -169,7 +169,7 @@ For VS Code users.
### Start and connect the REPL
1. Open the `status-mobile` folder.
1. Start [Status development](starting-guide.md#development) (Starting the `run-clojure` and `run-metro` jobs in split view in the VS Code integrated terminal works great.)
1. Start [Status development](STARTING_GUIDE.md#development) (Starting the `run-clojure` and `run-metro` jobs in split view in the VS Code integrated terminal works great.)
1. Run the VS Code command: **Calva: Connect to a running REPL Server in the project**
1. Select the project type `shadow-cljs`
1. Accept the suggested connection `host:port`
@@ -1156,7 +1156,23 @@ RCT_EXPORT_METHOD(deactivateKeepAwake)
- (NSString*) deviceName
{
return [[UIDevice currentDevice] name];;
NSString* deviceName = nil;
if ([self.deviceId rangeOfString:@"iPod"].location != NSNotFound) {
deviceName = @"iPod Touch";
}
else if([self.deviceId rangeOfString:@"iPad"].location != NSNotFound) {
deviceName = @"iPad";
}
else if([self.deviceId rangeOfString:@"iPhone"].location != NSNotFound){
deviceName = @"iPhone";
}
else if([self.deviceId rangeOfString:@"AppleTV"].location != NSNotFound){
deviceName = @"Apple TV";
}
return deviceName;
}
- (NSDictionary *)constantsToExport
-11
View File
@@ -17,20 +17,9 @@ let
# Override some packages and utilities
pkgsOverlay = import ./overlay.nix;
# Fix for lack of Android SDK for M1 Macs.
systemOverride = let
inherit (builtins) currentSystem getEnv;
envSystemOverride = getEnv "NIXPKGS_SYSTEM_OVERRIDE";
in
if envSystemOverride != "" then
envSystemOverride
else
currentSystem;
in
# import nixpkgs with a config override
(import nixpkgsSrc) {
config = defaultConfig // config;
system = systemOverride;
overlays = [ pkgsOverlay ];
}
+1 -4
View File
@@ -3,10 +3,7 @@
# for the Android development environment.
#
{ androidenv, lib, stdenv }:
assert lib.assertMsg (stdenv.system != "aarch64-darwin")
"aarch64-darwin not supported for Android SDK. Use: NIXPKGS_SYSTEM_OVERRIDE=x86_64-darwin";
{ androidenv }:
# The "android-sdk-license" license is accepted
# by setting android_sdk.accept_license = true.
+1 -7
View File
@@ -25,7 +25,7 @@ cleanup() {
# If you want to clean after every build set _NIX_CLEAN=true
if [[ -n "${_NIX_CLEAN}" ]]; then
trap cleanup EXIT ERR INT QUIT
trap cleanup EXIT ERR INT QUIT
fi
# build output will end up under /nix, we have to extract it
@@ -44,12 +44,6 @@ if [[ -z "${TARGET}" ]]; then
exit 1
fi
# Hack fix for missing Android SDK for aarch64 on Darwin. See systemOverride in `nix/pkgs.nix`.
if [[ "${TARGET}" =~ ^(targets.status-go.mobile.android|targets.mobile.android.release)$ ]]; then
os=$(uname -s | tr '[:upper:]' '[:lower:]')
export NIXPKGS_SYSTEM_OVERRIDE="x86_64-${os}"
fi
# Some defaults flags, --pure could be optional in the future.
# NOTE: The --keep-failed flag can be used for debugging issues.
nixOpts=(
+1 -2
View File
@@ -8,8 +8,7 @@ file_group() {
if [[ "${UNAME}" == "Linux" ]]; then
stat -Lc "%G" "${1}" 2>/dev/null
elif [[ "${UNAME}" == "Darwin" ]]; then
# Avoid using Nix GNU stat when in Nix shell.
/usr/bin/stat -Lf "%Sg" "${1}" 2>/dev/null
stat -Lf "%Sg" "${1}" 2>/dev/null
fi
}
+1 -6
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
#
# This script is used by the Makefile to have an implicit nix-shell.
# The following environment variables modify the script behavior:
# - TARGET: This attribute is passed via --attr to Nix, defining the scope.
@@ -63,12 +64,6 @@ if [[ -n "${_NIX_PURE}" ]]; then
pureDesc='pure '
fi
# Hack fix for missing Android SDK for aarch64 on Darwin. See systemOverride in `nix/pkgs.nix`.
if [[ "${TARGET}" =~ ^(android-sdk|android|gradle|keytool|status-go)$ ]]; then
os=$(uname -s | tr '[:upper:]' '[:lower:]')
export NIXPKGS_SYSTEM_OVERRIDE="x86_64-${os}"
fi
echo -e "${GRN}Configuring ${pureDesc}Nix shell for target '${TARGET}'...${RST}" 1>&2
# Save derivation from being garbage collected
+1
View File
@@ -90,6 +90,7 @@
"@testing-library/react-native": "^11.2.0",
"@types/jest": "^28.1.6",
"concurrently": "^7.6.0",
"detox": "^19.9.1",
"jest": "^26.6.3",
"jest-circus": "^26.0.0",
"jest-image-snapshot": "^5.1.0",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,92 +0,0 @@
import { useAnimatedStyle, useDerivedValue } from 'react-native-reanimated';
export function background(color, progress) {
return useDerivedValue(function () {
'worklet';
const r = parseInt(color.substring(1, 3), 16);
const g = parseInt(color.substring(3, 5), 16);
const b = parseInt(color.substring(5, 7), 16);
return progress.value <= 25 ? `rgba(${r}, ${g}, ${b}, .4)` : `rgba(${r}, ${g}, ${b}, .2)`;
});
}
export function opacity(progress) {
return useDerivedValue(function () {
'worklet';
return progress.value <= 25 ? 1 : 0.3;
});
}
export function avatarOpacity(progress) {
return useDerivedValue(function () {
'worklet';
const progressValue = progress.value;
switch (true) {
case progressValue <= 25:
return 1;
case progressValue <= 50:
return 0.1;
default:
return 0.3;
}
});
}
export function ringOpacity(progress) {
return useDerivedValue(function () {
'worklet';
return progress.value <= 50 ? 1 : 0.3;
});
}
export function userHashOpacity(progress) {
return useDerivedValue(function () {
'worklet';
const progressValue = progress.value;
switch (true) {
case progressValue <= 25:
return 1;
case progressValue <= 50:
return 0.3;
case progressValue <= 75:
return 1;
default:
return 0.3;
}
});
}
export function userHashColor(progress) {
return useDerivedValue(function () {
'worklet';
const progressValue = progress.value;
switch (true) {
case progressValue <= 25:
return 'rgba(255, 255, 255, .6)';
case progressValue <= 50:
return 'rgba(255, 255, 255, .6)';
case progressValue <= 75:
return 'rgb(255, 255, 255)';
default:
return 'rgba(255, 255, 255, .6)';
}
});
}
export function emojiHashStyle(progress) {
return useAnimatedStyle(function () {
'worklet';
const progressValue = progress.value;
switch (true) {
case progressValue <= 25:
return { opacity: 1 };
case progressValue <= 75:
return { opacity: 0.3 };
default:
return { opacity: 1 };
}
});
}
+21 -9
View File
@@ -1,8 +1,6 @@
import { useDerivedValue, withTiming, Easing } from 'react-native-reanimated';
const slideAnimationDuration = 300;
const totalPages = 4;
const pageSize = 100 / totalPages;
const easeOut = {
duration: slideAnimationDuration,
@@ -17,15 +15,29 @@ export function dynamicProgressBarWidth(staticProgressBarWidth, progress) {
});
}
export function carouselLeftPosition(windowWidth, progress, isDragging, dragAmount) {
export function carouselLeftPosition(windowWidth, progress) {
return useDerivedValue(function () {
'worklet';
const progressValue = progress.value;
const dragAmountValue = dragAmount.value;
const baseOffset = (Math.floor(progressValue / pageSize) % totalPages) * windowWidth;
const adjustedOffset = baseOffset === 0 && dragAmountValue > 0 ? baseOffset : -baseOffset + dragAmountValue;
return isDragging.value ? withTiming(adjustedOffset, easeOut) : withTiming(-baseOffset, easeOut);
switch (true) {
case progressValue < 25:
return 0;
case progressValue === 25:
return withTiming(-windowWidth, easeOut);
case progressValue < 50:
return -windowWidth;
case progressValue === 50:
return withTiming(-2 * windowWidth, easeOut);
case progressValue < 75:
return -2 * windowWidth;
case progressValue === 75:
return withTiming(-3 * windowWidth, easeOut);
case progressValue < 100:
return -3 * windowWidth;
case progressValue === 100:
return withTiming(-4 * windowWidth, easeOut);
default:
return 0;
}
});
}
-1
View File
@@ -13,7 +13,6 @@ which is explained in better detail in this video
https://www.youtube.com/watch?v=iEBoZDHCN5Y&t=2205s
there is a bug with the pitch and roll calculations provided directly from the sensor data so the calculation had to
be done using the quaternions directly.
This link is useful to understand this: https://engineering.stackexchange.com/questions/3348/calculating-pitch-yaw-and-roll-from-mag-acc-and-gyro-data
*/
export function sensorAnimatedImage(zIndex, offset, stretch) {
'worklet';
+1 -59
View File
@@ -1,4 +1,4 @@
import { useDerivedValue, withTiming, withSequence, withDelay, Easing, runOnJS } from 'react-native-reanimated';
import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated';
import * as constants from './constants';
// Derived Values
@@ -88,61 +88,3 @@ export function screenZIndex(screenState) {
}
});
}
export function screenBorderRadius(screenState) {
return useDerivedValue(function () {
'worklet';
switch (screenState.value) {
case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION:
return withDelay(constants.SHELL_ANIMATION_TIME, withTiming(0, { duration: 0 }));
case constants.OPEN_SCREEN_WITH_SLIDE_ANIMATION:
case constants.OPEN_SCREEN_WITHOUT_ANIMATION:
return 0;
case constants.CLOSE_SCREEN_WITH_SLIDE_ANIMATION:
return withDelay(constants.SHELL_ANIMATION_TIME, withTiming(20, { duration: 0 }));
case constants.CLOSE_SCREEN_WITHOUT_ANIMATION:
case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION:
return 20;
}
});
}
export function screenGestureOnUpdate(screenLeft) {
return function (event) {
'worklet';
const absoluteX = event.absoluteX;
if (absoluteX !== null) {
screenLeft.value = event.absoluteX;
}
};
}
export function screenGestureOnEnd(data) {
return function (event) {
'worklet';
const { screenLeft, screenState, screenWidth, leftVelocity, rightVelocity, screenClosedCallback } = data;
const absoluteX = event.absoluteX ?? 0;
const velocityX = event.velocityX ?? 0;
const closeScreen = velocityX > rightVelocity || (velocityX > leftVelocity && absoluteX >= screenWidth / 2);
// Velocity (points/sec) = Distance/time
var animationVelocity = (screenWidth * 1000) / constants.SHELL_ANIMATION_TIME;
if (Math.abs(velocityX) > animationVelocity) {
animationVelocity = velocityX; // Faster fling
}
const newDistance = closeScreen ? screenWidth - absoluteX : absoluteX;
const animationTime = (newDistance * 1000) / animationVelocity;
screenLeft.value = withTiming(closeScreen ? screenWidth : 0, {
duration: animationTime,
easing: Easing.bezier(0, 0, 0.58, 1),
});
if (closeScreen) {
runOnJS(screenClosedCallback)(animationTime);
}
};
}
+73 -75
View File
@@ -364,79 +364,77 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(defn mock
[module]
(case module
"react-native-languages" react-native-languages
"react-native-background-timer" background-timer
"react-native-image-crop-picker" image-crop-picker
"react-native-gesture-handler" react-native-gesture-handler
"react-native-static-safe-area-insets" react-native-static-safe-area-insets
"react-native-config" config
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
:getBottomSpace (fn [])})
"react-native-screens" (clj->js {})
"react-native-reanimated" react-native-reanimated
"react-native-redash/lib/module/v1" react-native-redash
"react-native-redash" react-native-redash
"react-native-fetch-polyfill" fetch
"react-native-status-keycard" status-keycard
"react-native-keychain" keychain
"react-native-touch-id" touchid
"@react-native-community/netinfo" net-info
"react-native-dialogs" dialogs
"react-native" react-native
"react-native-fs" fs
"react-native-mail" react-native-mail
"react-native-image-resizer" image-resizer
"react-native-haptic-feedback" react-native-haptic-feedback
"react-native-device-info" react-native-device-info
"react-native-push-notification" react-native-push-notification
"react-native-linear-gradient" react-native-gradien
"@react-native-community/masked-view" masked-view
"react-native-blob-util" react-native-blob-util
"react-native-navigation" react-native-navigation
"@react-native-community/push-notification-ios" push-notification-ios
"@react-native-community/blur" react-native-blur
"@react-native-community/cameraroll" react-native-camera-roll
"react-native-camera-kit" react-native-camera-kit
"react-native-permissions" react-native-permissions
"rn-emoji-keyboard" rn-emoji-keyboard
"react-native-hole-view" react-native-hole-view
"react-native-draggable-flatlist" react-native-draggable-flatlist
"react-native-webview" react-native-webview
"@react-native-community/audio-toolkit" react-native-audio-toolkit
"react-native-image-viewing" react-native-image-viewing
"react-native-share" react-native-share
"@react-native-async-storage/async-storage" async-storage
"react-native-svg" react-native-svg
"react-native-transparent-video" react-native-transparent-video
"react-native-orientation-locker" react-native-orientation-locker
"../src/js/worklets/core.js" worklet-factory
"../src/js/worklets/shell/bottom_tabs.js" #js {}
"../src/js/worklets/shell/home_stack.js" #js {}
"../src/js/worklets/shell/floating_screen.js" #js {}
"../src/js/worklets/bottom_sheet.js" #js {}
"../src/js/worklets/record_audio.js" #js {}
"../src/js/worklets/scroll_view.js" #js {}
"../src/js/worklets/onboarding_carousel.js" #js {}
"../src/js/worklets/lightbox.js" #js {}
"../src/js/worklets/parallax.js" #js {}
"../src/js/worklets/identifiers_highlighting.js" #js {}
"./fleets.js" default-fleets
"@walletconnect/client" wallet-connect-client
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
"../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json"))
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
"../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json"))
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
"../translations/zh_hant.json" (js/JSON.parse (slurp
"./translations/zh_hant.json"))
"../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json"))
"react-native-languages" react-native-languages
"react-native-background-timer" background-timer
"react-native-image-crop-picker" image-crop-picker
"react-native-gesture-handler" react-native-gesture-handler
"react-native-static-safe-area-insets" react-native-static-safe-area-insets
"react-native-config" config
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
:getBottomSpace (fn [])})
"react-native-screens" (clj->js {})
"react-native-reanimated" react-native-reanimated
"react-native-redash/lib/module/v1" react-native-redash
"react-native-redash" react-native-redash
"react-native-fetch-polyfill" fetch
"react-native-status-keycard" status-keycard
"react-native-keychain" keychain
"react-native-touch-id" touchid
"@react-native-community/netinfo" net-info
"react-native-dialogs" dialogs
"react-native" react-native
"react-native-fs" fs
"react-native-mail" react-native-mail
"react-native-image-resizer" image-resizer
"react-native-haptic-feedback" react-native-haptic-feedback
"react-native-device-info" react-native-device-info
"react-native-push-notification" react-native-push-notification
"react-native-linear-gradient" react-native-gradien
"@react-native-community/masked-view" masked-view
"react-native-blob-util" react-native-blob-util
"react-native-navigation" react-native-navigation
"@react-native-community/push-notification-ios" push-notification-ios
"@react-native-community/blur" react-native-blur
"@react-native-community/cameraroll" react-native-camera-roll
"react-native-camera-kit" react-native-camera-kit
"react-native-permissions" react-native-permissions
"rn-emoji-keyboard" rn-emoji-keyboard
"react-native-hole-view" react-native-hole-view
"react-native-draggable-flatlist" react-native-draggable-flatlist
"react-native-webview" react-native-webview
"@react-native-community/audio-toolkit" react-native-audio-toolkit
"react-native-image-viewing" react-native-image-viewing
"react-native-share" react-native-share
"@react-native-async-storage/async-storage" async-storage
"react-native-svg" react-native-svg
"react-native-transparent-video" react-native-transparent-video
"react-native-orientation-locker" react-native-orientation-locker
"../src/js/worklets/core.js" worklet-factory
"../src/js/worklets/shell/bottom_tabs.js" #js {}
"../src/js/worklets/shell/home_stack.js" #js {}
"../src/js/worklets/shell/floating_screen.js" #js {}
"../src/js/worklets/bottom_sheet.js" #js {}
"../src/js/worklets/record_audio.js" #js {}
"../src/js/worklets/scroll_view.js" #js {}
"../src/js/worklets/onboarding_carousel.js" #js {}
"../src/js/worklets/lightbox.js" #js {}
"../src/js/worklets/parallax.js" #js {}
"./fleets.js" default-fleets
"@walletconnect/client" wallet-connect-client
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
"../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json"))
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
"../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json"))
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
"../translations/zh_hant.json" (js/JSON.parse (slurp "./translations/zh_hant.json"))
"../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json"))
nil))
+1 -10
View File
@@ -4,8 +4,7 @@
[taoensso.timbre :as log]
[react-native.platform :as platform]
[react-native.core :as rn]
[utils.transforms :as types]
[clojure.string :as string]))
[utils.transforms :as types]))
(defn status
[]
@@ -388,14 +387,6 @@
:build-id (.-buildId status)
:device-id (.-deviceId status)}))
(defn get-installation-name
[]
;; NOTE(rasom): Only needed for android devices currently
(when platform/android?
(string/join " "
((juxt :model :device-id)
(get-device-model-info)))))
(defn get-node-config
[callback]
(log/debug "[native-module] get-node-config")
+18 -66
View File
@@ -4,9 +4,7 @@
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.blur :as blur]
[reagent.core :as reagent]
[quo2.components.buttons.style :as style]))
[reagent.core :as reagent]))
(defn themes
[customization-color]
@@ -68,16 +66,6 @@
:background-color {:default colors/neutral-80-opa-5
:pressed colors/neutral-80-opa-10
:disabled colors/neutral-80-opa-5}}
:blurred {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-100
:icon-background-color {:default colors/neutral-20
:blurred colors/neutral-80-opa-10}
:label-color colors/neutral-100
:background-color {:default colors/neutral-10
:pressed colors/neutral-10
:disabled colors/neutral-10-opa-10-blur}
:blur-overlay-color colors/neutral-10-opa-40-blur
:blur-type :light}
:blur-bg-outline {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
@@ -147,16 +135,6 @@
:background-color {:default colors/white-opa-5
:pressed colors/white-opa-10
:disabled colors/white-opa-5}}
:blurred {:icon-color colors/white
:icon-secondary-color colors/white
:icon-background-color {:default colors/neutral-80
:blurred colors/white-opa-10}
:label-color colors/white
:background-color {:default colors/neutral-90
:pressed colors/neutral-90
:disabled colors/neutral-90-opa-10-blur}
:blur-overlay-color colors/neutral-80-opa-40
:blur-type :dark}
:blur-bg-outline {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
@@ -179,11 +157,12 @@
24 8))})
(defn style-container
[type size disabled background-color border-color icon above width before after blur-active?]
[type size disabled background-color border-color icon above width before after]
(merge {:height size
:align-items :center
:justify-content :center
:flex-direction (if above :column :row)
:background-color background-color
:padding-horizontal (when-not (or icon before after)
(case size
56 16
@@ -213,11 +192,7 @@
56 0
40 9
32 5
24 4))
:overflow :hidden}
(when (or (and (= type :blurred) (not blur-active?))
(not= type :blurred))
{:background-color background-color})
24 4))}
(shape-style-container type icon size)
(when width
{:width width})
@@ -249,23 +224,18 @@
(fn
[{:keys [on-press disabled type size before after above icon-secondary-no-color
width customization-color theme override-background-color pressed
on-long-press accessibility-label icon icon-no-color style inner-style test-ID
blur-active? override-before-margins override-after-margins icon-size icon-container-size
icon-container-rounded?]
on-long-press accessibility-label icon icon-no-color style inner-style test-ID]
:or {type :primary
size 40
customization-color :primary
blur-active? true}}
customization-color :primary}}
children]
(let [{:keys [icon-color icon-secondary-color background-color label-color border-color blur-type
blur-overlay-color icon-background-color]}
(let [{:keys [icon-color icon-secondary-color background-color label-color border-color]}
(get-in (themes customization-color)
[theme type])
state (cond disabled :disabled
(or @pressed-in pressed) :pressed
:else :default)
blur-state (if blur-active? :blurred :default)
icon-size (or icon-size (when (= 24 size) 12))
icon-size (when (= 24 size) 12)
icon-secondary-color (or icon-secondary-color icon-color)]
[rn/touchable-without-feedback
(merge {:test-ID test-ID
@@ -294,17 +264,9 @@
above
width
before
after
blur-active?)
after)
(when (= state :pressed) {:opacity 0.9})
inner-style)}
(when (and (= type :blurred)
blur-active?)
[blur/view
{:blur-radius 20
:blur-type blur-type
:overlay-color blur-overlay-color
:style style/blur-view}])
(when above
[rn/view
[quo2.icons/icon above
@@ -313,16 +275,11 @@
:size icon-size}]])
(when before
[rn/view
{:style (style/before-icon-style
{:override-margins override-before-margins
:size size
:icon-container-size icon-container-size
:icon-background-color (get icon-background-color blur-state)
:icon-container-rounded? icon-container-rounded?
:icon-size icon-size})}
[quo2.icons/icon before
{:color icon-secondary-color
:size icon-size}]])
{:container-style {:margin-left (if (= size 40) 12 8)
:margin-right 4}
:color icon-secondary-color
:size icon-size}]])
[rn/view
(cond
(or icon icon-no-color)
@@ -344,16 +301,11 @@
children)]
(when after
[rn/view
{:style (style/after-icon-style
{:override-margins override-after-margins
:size size
:icon-container-size icon-container-size
:icon-background-color (get icon-background-color blur-state)
:icon-container-rounded? icon-container-rounded?
:icon-size icon-size})}
[quo2.icons/icon after
{:no-color icon-secondary-no-color
:color icon-secondary-color
:size icon-size}]])]]]))))
{:container-style {:margin-left 4
:margin-right (if (= size 40) 12 8)}
:no-color icon-secondary-no-color
:color icon-secondary-color
:size icon-size}]])]]]))))
(def button (theme/with-theme button-internal))
-42
View File
@@ -1,42 +0,0 @@
(ns quo2.components.buttons.style)
(def blur-view
{:position :absolute
:top 0
:left 0
:right 0
:bottom 0})
(defn before-icon-style
[{:keys [override-margins size icon-container-size icon-background-color icon-container-rounded?
icon-size]}]
(merge
{:margin-left (or (get override-margins :left)
(if (= size 40) 12 8))
:margin-right (or (get override-margins :right) 4)
:align-items :center
:justify-content :center}
(when icon-container-size
{:width icon-container-size
:height icon-container-size})
(when icon-background-color
{:background-color icon-background-color})
(when icon-container-rounded?
{:border-radius (/ (or icon-container-size icon-size) 2)})))
(defn after-icon-style
[{:keys [override-margins size icon-container-size icon-background-color icon-container-rounded?
icon-size]}]
(merge
{:margin-left (or (get override-margins :left) 4)
:margin-right (or (get override-margins :right)
(if (= size 40) 12 8))
:align-items :center
:justify-content :center}
(when icon-container-size
{:width icon-container-size
:height icon-container-size})
(when icon-background-color
{:background-color icon-background-color})
(when icon-container-rounded?
{:border-radius (/ (or icon-container-size icon-size) 2)})))
+2 -6
View File
@@ -7,12 +7,8 @@
[button/button
(merge
opts
{:after (if selected :i/chevron-top :i/chevron-down)
:icon-size 12
:icon-container-size 14
:icon-container-rounded? true
:override-after-margins {:left 7
:right 9}
{:after (if selected :i/pullup :i/dropdown)
:icon-secondary-no-color true
:pressed selected
:on-press #(when on-change (on-change selected))})
children]))
+2 -4
View File
@@ -56,10 +56,8 @@
(when (and (not locked?)
muted?)
[quo2.icons/icon :i/muted
{:size 20
:color colors/neutral-40
:container-style {:margin-right 1
:margin-top 2}}])
{:size 20
:color colors/neutral-40}])
(when (and (not locked?)
(not muted?)
(pos? (int mentions-count)))
@@ -1,9 +1,7 @@
(ns quo2.components.record-audio.record-audio.--tests--.record-audio-component-spec
(:require [quo2.components.record-audio.record-audio.view :as record-audio]
[react-native.audio-toolkit :as audio]
[test-helpers.component :as h]
[reagent.core :as reagent]
[utils.datetime :as datetime]))
[test-helpers.component :as h]))
(h/describe "record audio component"
(h/before-each
@@ -36,16 +34,12 @@
(.toHaveBeenCalledTimes 1))))
(h/test "record-audio on-reviewing-audio works"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
(let [event (js/jest.fn)]
(h/render [record-audio/record-audio
{:on-reviewing-audio event
:record-audio-permission-granted true}])
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
(reset! on-meter on-meter-fn))
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))
(with-redefs [audio/start-recording (fn [_ on-start _]
(on-start))
audio/get-recorder-file-path (fn [] "file-path")]
(h/fire-event
:on-start-should-set-responder
@@ -54,30 +48,25 @@
:locationY 70
:timestamp 0
:identifier 0}})
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
(h/advance-timers-by-time 100)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (h/expect event)
(.toHaveBeenCalledTimes 1))))))
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (h/expect event)
(.toHaveBeenCalledTimes 1)))))
(h/test "record-audio on-send works after reviewing audio"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
(let [event (js/jest.fn)]
(h/render [record-audio/record-audio
{:on-send event
:record-audio-permission-granted true}])
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
(reset! on-meter on-meter-fn))
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))
(with-redefs [audio/start-recording (fn [_ on-start _]
(on-start))
audio/get-recorder-file-path (fn [] "audio-file-path")
audio/get-player-duration (fn [] 5000)]
(h/fire-event
@@ -87,57 +76,46 @@
:locationY 70
:timestamp 0
:identifier 0}})
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
(h/advance-timers-by-time 100)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 200
:identifier 0}})
(h/fire-event
:on-start-should-set-responder
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 0
:identifier 0}})
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 80
:locationY 80
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))
(-> (js/expect event)
(.toHaveBeenCalledWith {:file-path "audio-file-path"
:duration 5000}))))))
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 200
:identifier 0}})
(h/fire-event
:on-start-should-set-responder
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 0
:identifier 0}})
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 80
:locationY 80
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))
(-> (js/expect event)
(.toHaveBeenCalledWith {:file-path "audio-file-path"
:duration 5000})))))
(h/test "record-audio on-send works after sliding to the send button"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)
last-now-ms (atom nil)
duration-ms (atom nil)]
(let [event (js/jest.fn)]
(h/render [record-audio/record-audio
{:on-send event
:record-audio-permission-granted true}])
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
(reset! on-meter on-meter-fn))
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))
(with-redefs [audio/start-recording (fn [_ on-start _]
(on-start))
audio/stop-recording (fn [_ on-stop _]
(on-stop))
audio/get-recorder-file-path (fn [] "audio-file-path")
datetime/timestamp (fn []
(let [now-ms (.now js/Date)]
(reset! last-now-ms now-ms)
now-ms))]
audio/get-recorder-file-path (fn [] "audio-file-path")]
(h/fire-event
:on-start-should-set-responder
(h/get-by-test-id "record-audio")
@@ -145,46 +123,36 @@
:locationY 70
:timestamp 0
:identifier 0}})
(with-redefs [datetime/timestamp (fn []
(let [now-plus-ms (+ (.now js/Date) 1000)
time-diff-ms (- now-plus-ms @last-now-ms)]
(when-not @duration-ms
(reset! duration-ms time-diff-ms))
now-plus-ms))]
(h/advance-timers-by-time 100)
(h/fire-event
:on-responder-move
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 80
:locationY -30
:pageX 80
:pageY -30
:identifier 0}})
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 40
:locationY 80
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))
(-> (js/expect event)
(.toHaveBeenCalledWith {:file-path "audio-file-path"
:duration @duration-ms}))))))
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-move
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 80
:locationY -30
:pageX 80
:pageY -30
:identifier 0}})
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 40
:locationY 80
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))
(-> (js/expect event)
(.toHaveBeenCalledWith {:file-path "audio-file-path"
:duration 500})))))
(h/test "record-audio on-cancel works after reviewing audio"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
(let [event (js/jest.fn)]
(h/render [record-audio/record-audio
{:on-cancel event
:record-audio-permission-granted true}])
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
(reset! on-meter on-meter-fn))
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))]
(with-redefs [audio/start-recording (fn [_ on-start _]
(on-start))]
(h/fire-event
:on-start-should-set-responder
(h/get-by-test-id "record-audio")
@@ -192,37 +160,32 @@
:locationY 70
:timestamp 0
:identifier 0}})
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 200
:identifier 0}})
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 40
:locationY 80
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))))))
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 70
:locationY 70
:timestamp 200
:identifier 0}})
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX 40
:locationY 80
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1)))))
(h/test "record-audio on-cancel works after sliding to the cancel button"
(let [event (js/jest.fn)
on-meter (reagent/atom nil)]
(h/test "cord-audio on-cancel works after sliding to the cancel button"
(let [event (js/jest.fn)]
(h/render [record-audio/record-audio
{:on-cancel event
:record-audio-permission-granted true}])
(with-redefs [audio/new-recorder (fn [_ on-meter-fn _]
(reset! on-meter on-meter-fn))
audio/start-recording (fn [_ on-start _]
(on-start)
(js/setInterval #(@on-meter) 100))
(with-redefs [audio/start-recording (fn [_ on-start _]
(on-start))
audio/stop-recording (fn [_ on-stop _]
(on-stop))]
(h/fire-event
@@ -232,25 +195,22 @@
:locationY 70
:timestamp 0
:identifier 0}})
(with-redefs [datetime/timestamp (fn [] (+ (.now js/Date) 1000))]
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-move
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX -30
:locationY 80
:pageX -30
:pageY 80
:identifier 0}})
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX -10
:locationY 70
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1)))))))
(h/advance-timers-by-time 500)
(h/fire-event
:on-responder-move
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX -30
:locationY 80
:pageX -30
:pageY 80
:identifier 0}})
(h/fire-event
:on-responder-release
(h/get-by-test-id "record-audio")
{:nativeEvent {:locationX -10
:locationY 70
:timestamp 200
:identifier 0}})
(h/advance-timers-by-time 250)
(-> (js/expect event)
(.toHaveBeenCalledTimes 1))))))
@@ -29,7 +29,7 @@
(defn f-record-button-big
[recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating?
record-button-at-initial-position? locked? reviewing-audio? recording-length-ms
record-button-at-initial-position? locked? reviewing-audio? recording-timer recording-length-ms
clear-timeout touch-active? recorder-ref reload-recorder-fn idle? on-send on-cancel]
(let [scale (reanimated/use-shared-value 1)
opacity (reanimated/use-shared-value 0)
@@ -82,6 +82,7 @@
(reset! ready-to-lock? false)
(reset! idle? true)
(js/setTimeout #(reset! idle? false) 1000)
(js/clearInterval @recording-timer)
(reset! recording-length-ms 0)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))))
@@ -15,13 +15,11 @@
[quo2.components.record-audio.record-audio.buttons.lock-button :as lock-button]
[quo2.components.record-audio.record-audio.buttons.delete-button :as delete-button]
[quo2.components.record-audio.record-audio.buttons.record-button :as record-button]
[clojure.string :as string]
[utils.datetime :as datetime]
[react-native.platform :as platform]))
[clojure.string :as string]))
(def ^:private min-audio-duration-ms 1000)
(def ^:private max-audio-duration-ms (if platform/ios? 120800 120500))
(def ^:private metering-interval 25)
(def ^:private min-audio-duration-ms 500)
(def ^:private max-audio-duration-ms 120000)
(def ^:private metering-interval 100)
(def ^:private base-filename "am")
(def ^:private default-format ".aac")
@@ -110,7 +108,7 @@
time-str]]))
(defn- f-play-button
[playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio? max-duration-ms]
[playing-audio? player-ref playing-timer audio-current-time-ms seeking-audio?]
(let [on-play (fn []
(reset! playing-audio? true)
(reset! playing-timer
@@ -119,22 +117,8 @@
(let [current-time (audio/get-player-current-time @player-ref)
player-state (audio/get-state @player-ref)
playing? (= player-state audio/PLAYING)]
(when (and playing?
(not @seeking-audio?)
(> current-time 0)
(< current-time max-duration-ms))
(reset! audio-current-time-ms current-time))
(when (>= current-time max-duration-ms)
(audio/stop-playing
@player-ref
(fn []
(reset! playing-audio? false)
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil)
(reset! audio-current-time-ms 0)
(reset! seeking-audio? false)))
#(log/error "[record-audio] stop player - error: " %)))))
(when (and playing? (not @seeking-audio?) (> current-time 0))
(reset! audio-current-time-ms current-time))))
100)))
on-pause (fn []
(reset! playing-audio? false)
@@ -159,7 +143,7 @@
[{:keys [on-init on-start-recording on-send on-cancel on-reviewing-audio
record-audio-permission-granted
on-request-record-audio-permission on-check-audio-permissions
audio-file on-lock max-duration-ms]}]
audio-file on-lock]}]
[:f>
;; TODO we need to refactor this, and use :f> with defined function, currenly state is reseted each
;; time parent component
@@ -176,7 +160,6 @@
audio-current-time-ms (reagent/atom 0)
seeking-audio? (reagent/atom false)
force-show-controls? (reagent/atom (some? audio-file))
recording-start-ms (atom (datetime/timestamp))
clear-timeout (atom nil)
record-button-at-initial-position? (atom true)
record-button-is-animating? (atom false)
@@ -184,6 +167,7 @@
recorder-ref (atom nil)
player-ref (atom nil)
touch-active? (atom false)
recording-timer (atom nil)
playing-timer (atom nil)
output-file (atom audio-file)
reached-max-duration? (atom false)
@@ -194,7 +178,7 @@
rec-options
(merge
audio/default-recorder-options
{:filename (str base-filename (datetime/timestamp) default-format)
{:filename (str base-filename (.now js/Date) default-format)
:meteringInterval metering-interval})
destroy-player
(fn []
@@ -225,44 +209,13 @@
(fn []
(audio/destroy-recorder @recorder-ref)
(reset! recorder-ref nil))
recorder-on-meter
(fn []
(when @recording-start-ms
(let [now-ms (datetime/timestamp)
recording-duration (- now-ms
@recording-start-ms)]
(reset! recording-length-ms recording-duration)
(when (>= recording-duration max-audio-duration-ms)
(reset! reached-max-duration? (not @locked?))
(reset! reviewing-audio? true)
(reset! idle? false)
(reset! locked? false)
(reset! recording? false)
(reset! ready-to-lock? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(audio/stop-recording
@recorder-ref
(fn []
(reset! output-file (audio/get-recorder-file-path
@recorder-ref))
(reload-player nil)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))
(js/setTimeout #(reset! idle? false) 1000)
(reset! recording-length-ms 0)
(reset! recording-start-ms nil)
(when on-reviewing-audio
(on-reviewing-audio (audio/get-recorder-file-path
@recorder-ref))))
(log/debug "[record-audio] new recorder - on meter"))))
reload-recorder
(fn []
(when @recorder-ref
(destroy-recorder))
(reset! recorder-ref (audio/new-recorder
rec-options
recorder-on-meter
#(log/debug "[record-audio] new recorder - on meter")
#(log/debug "[record-audio] new recorder - on ended"))))
reset-recorder
(fn []
@@ -285,6 +238,9 @@
(when @clear-timeout
(js/clearTimeout @clear-timeout)
(reset! clear-timeout nil))
(when @recording-timer
(js/clearInterval @recording-timer)
(reset! recording-timer nil))
(when @playing-timer
(js/clearInterval @playing-timer)
(reset! playing-timer nil))
@@ -302,7 +258,7 @@
record-button-area)
new-recorder (audio/new-recorder
rec-options
recorder-on-meter
#(log/debug "[record-audio] new recorder - on meter")
#(log/debug "[record-audio] new recorder - on ended"))]
(reset! touch-timestamp (oops/oget e "nativeEvent.timestamp"))
(reset! touch-identifier (oops/oget e "nativeEvent.identifier"))
@@ -313,13 +269,44 @@
(reset! recording? pressed-record-button?))
(when pressed-record-button?
(reset! playing-audio? false)
(when @recording-timer
(js/clearInterval @recording-timer))
(reset! output-file nil)
(reset! recorder-ref new-recorder)
(audio/start-recording
new-recorder
(fn []
(reset! recording-start-ms (datetime/timestamp))
(reset! audio-current-time-ms 0)
(reset! recording-timer
(js/setInterval
(fn []
(if (< @recording-length-ms max-audio-duration-ms)
(reset! recording-length-ms
(+ @recording-length-ms metering-interval))
(do
(reset! reached-max-duration? (not @locked?))
(reset! reviewing-audio? true)
(reset! idle? false)
(reset! locked? false)
(reset! recording? false)
(reset! ready-to-lock? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(audio/stop-recording
new-recorder
(fn []
(reset! output-file (audio/get-recorder-file-path
new-recorder))
(reload-player nil)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))
(js/setTimeout #(reset! idle? false) 1000)
(js/clearInterval @recording-timer)
(reset! recording-length-ms 0)
(when on-reviewing-audio
(on-reviewing-audio (audio/get-recorder-file-path
new-recorder))))))
metering-interval))
(log/debug "[record-audio] start recording - success"))
#(log/error "[record-audio] start recording - error: " %))
(when on-start-recording
@@ -437,8 +424,7 @@
(reset! force-show-controls? false)
(when on-send
(on-send {:file-path @output-file
:duration (min max-audio-duration-ms
(int (audio/get-player-duration @player-ref)))}))
:duration (int (audio/get-player-duration @player-ref))}))
(when @player-ref
(audio/stop-playing
@player-ref
@@ -489,8 +475,8 @@
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %))
(js/setTimeout #(reset! idle? false) 1000)
(js/clearInterval @recording-timer)
(reset! recording-length-ms 0)
(reset! recording-start-ms nil)
(reset! disabled? false))
(if (> touch-timestamp-diff min-touch-duration) 0 250)))
(and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?))
@@ -515,8 +501,8 @@
(reset! ready-to-lock? false)
(reset! idle? true)
(js/setTimeout #(reset! idle? false) 1000)
(js/clearInterval @recording-timer)
(reset! recording-length-ms 0)
(reset! recording-start-ms nil)
(reset! disabled? false)
(log/debug "[record-audio] stop recording - success"))
#(log/error "[record-audio] stop recording - error: " %)))
@@ -546,12 +532,11 @@
(when @reviewing-audio?
[:<>
[:f> f-play-button playing-audio? player-ref playing-timer audio-current-time-ms
seeking-audio? max-duration-ms]
seeking-audio?]
[:f> soundtrack/f-soundtrack
{:audio-current-time-ms audio-current-time-ms
:player-ref @player-ref
:seeking-audio? seeking-audio?
:max-audio-duration-ms max-duration-ms}]])
:seeking-audio? seeking-audio?}]])
(when (or @recording? @reviewing-audio?)
[:f> f-time-counter @recording? @recording-length-ms @ready-to-delete? @reviewing-audio?
@audio-current-time-ms])
@@ -582,6 +567,7 @@
record-button-at-initial-position?
locked?
reviewing-audio?
recording-timer
recording-length-ms
clear-timeout
touch-active?
@@ -10,8 +10,8 @@
(def ^:private thumb-dark (js/require "../resources/images/icons2/12x12/thumb-dark.png"))
(defn f-soundtrack
[{:keys [audio-current-time-ms player-ref style seeking-audio? max-audio-duration-ms]}]
(let [audio-duration-ms (min max-audio-duration-ms (audio/get-player-duration player-ref))]
[{:keys [audio-current-time-ms player-ref style seeking-audio?]}]
(let [audio-duration-ms (audio/get-player-duration player-ref)]
[:<>
[slider/slider
{:test-ID "soundtrack"
-4
View File
@@ -121,11 +121,7 @@
(def white-70-blur (alpha white 0.7))
(def neutral-80-opa-1-blur (alpha "#192438" 0.1))
(def neutral-5-opa-70-blur (alpha neutral-5 0.7))
(def neutral-10-opa-10-blur (alpha neutral-10 0.1))
(def neutral-10-opa-40-blur (alpha neutral-10 0.4))
(def neutral-80-opa-80-blur (alpha "#192438" 0.8))
(def neutral-90-opa-10-blur (alpha neutral-90 0.1))
(def neutral-90-opa-40-blur (alpha neutral-90 0.4))
(def neutral-90-opa-70-blur (alpha neutral-90 0.7))
(def neutral-95-opa-70-blur neutral-95-opa-70)
(def neutral-100-opa-70-blur neutral-100-opa-70)
-7
View File
@@ -1,6 +1,5 @@
(ns react-native.cameraroll
(:require ["@react-native-community/cameraroll" :as CameraRoll]
[react-native.fs :as fs]
[utils.transforms :as transforms]
[taoensso.timbre :as log]))
@@ -15,9 +14,3 @@
(-> (.getAlbums CameraRoll (clj->js opts))
(.then #(callback (transforms/js->clj %)))
(.catch #(log/warn "could not get camera roll albums" %))))
(defn save-image
[path]
(-> (.save CameraRoll (clj->js path))
(.then #(fs/unlink path))
(.catch #(fs/unlink path))))
-4
View File
@@ -68,10 +68,6 @@
(fn []
(js->clj (.get (.-Dimensions ^js react-native) "screen") :keywordize-keys true))))
(def small-screen?
(let [height (:height (get-screen))]
(< height 700)))
(defn hw-back-add-listener
[callback]
(.addEventListener (.-BackHandler ^js react-native) "hardwareBackPress" callback))
-6
View File
@@ -19,8 +19,6 @@
(defn gesture-pan [] (.Pan ^js Gesture))
(defn gesture-long-press [] (.LongPress ^js Gesture))
(defn gesture-pinch [] (.Pinch ^js Gesture))
(defn on-begin [gesture handler] (.onBegin ^js gesture handler))
@@ -37,10 +35,6 @@
(defn min-distance [gesture dist] (.minDistance ^js gesture dist))
(defn fail-offset-x [gesture offset] (.failOffsetX ^js gesture offset))
(defn hit-slop [gesture settings] (.hitSlop ^js gesture settings))
(defn number-of-taps [gesture count] (.numberOfTaps ^js gesture count))
(defn enabled [gesture enabled?] (.enabled ^js gesture enabled?))
+1 -3
View File
@@ -1,5 +1,5 @@
(ns react-native.permissions
(:require ["react-native-permissions" :refer (check openSettings requestMultiple PERMISSIONS RESULTS)]
(:require ["react-native-permissions" :refer (check requestMultiple PERMISSIONS RESULTS)]
[react-native.platform :as platform]))
(def permissions-map
@@ -38,5 +38,3 @@
(-> (check (get permissions-map permission))
(.then #(on-result (not (#{(.-BLOCKED RESULTS) (.-DENIED RESULTS)} %))))
(.catch #(on-error %))))
(def open-settings openSettings)
-1
View File
@@ -34,7 +34,6 @@
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent reanimated)))
(def view (reagent/adapt-react-class (.-View reanimated)))
(def text (reagent/adapt-react-class (.-Text reanimated)))
(def scroll-view (reagent/adapt-react-class (.-ScrollView reanimated)))
(def image (reagent/adapt-react-class (.-Image reanimated)))
+12 -12
View File
@@ -22,7 +22,7 @@
(rf/defn fetch
[cofx id]
(let [network (get-in cofx [:db :networks/current-network])]
(get-in cofx [:db :profile/profile :custom-bootnodes network id])))
(get-in cofx [:db :multiaccount :custom-bootnodes network id])))
(rf/defn set-input
{:events [:bootnodes.ui/input-changed]}
@@ -54,26 +54,26 @@
(defn custom-bootnodes-in-use?
[{:keys [db]}]
(let [network (:networks/current-network db)]
(get-in db [:profile/profile :custom-bootnodes-enabled? network])))
(get-in db [:multiaccount :custom-bootnodes-enabled? network])))
(rf/defn delete
[{{:profile/keys [profile] :as db} :db :as cofx} id]
(let [network (:networks/current-network db)
new-profile (update-in profile [:custom-bootnodes network] dissoc id)]
[{{:keys [multiaccount] :as db} :db :as cofx} id]
(let [network (:networks/current-network db)
new-multiaccount (update-in multiaccount [:custom-bootnodes network] dissoc id)]
(rf/merge cofx
{:db (assoc db :profile/profile new-profile)}
{:db (assoc db :multiaccount new-multiaccount)}
(multiaccounts.update/multiaccount-update
:custom-bootnodes
(:custom-bootnodes new-profile)
(:custom-bootnodes new-multiaccount)
{:success-event (when (custom-bootnodes-in-use? cofx)
[:multiaccounts.update.callback/save-settings-success])}))))
(rf/defn upsert
{:events [:bootnodes.ui/save-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:bootnodes/keys [manage] :profile/keys [profile] :as db} :db
random-id-generator :random-id-generator
:as cofx}]
[{{:bootnodes/keys [manage] :keys [multiaccount] :as db} :db
random-id-generator :random-id-generator
:as cofx}]
(let [{:keys [name id url]} manage
network (:networks/current-network db)
bootnode (build
@@ -82,7 +82,7 @@
(:value url)
network)
new-bootnodes (assoc-in
(:custom-bootnodes profile)
(:custom-bootnodes multiaccount)
[network (:id bootnode)]
bootnode)]
@@ -99,7 +99,7 @@
{:events [:bootnodes.ui/custom-bootnodes-switch-toggled]}
[{:keys [db] :as cofx} value]
(let [current-network (:networks/current-network db)
bootnodes-settings (get-in db [:profile/profile :custom-bootnodes-enabled?])]
bootnodes-settings (get-in db [:multiaccount :custom-bootnodes-enabled?])]
(multiaccounts.update/multiaccount-update
cofx
:custom-bootnodes-enabled?
+11 -11
View File
@@ -20,9 +20,9 @@
{:random-id-generator (constantly "some-id")
:db {:bootnodes/manage new-bootnode
:networks/current-network "mainnet_rpc"
:profile/profile {:not-empty
"would throw an error if was empty"}}})]
(is (= expected (get-in actual [:db :profile/profile :custom-bootnodes])))))
:multiaccount {:not-empty
"would throw an error if was empty"}}})]
(is (= expected (get-in actual [:db :multiaccount :custom-bootnodes])))))
(testing "adding an existing bootnode"
(let [new-bootnode {:id {:value "a"}
:name {:value "new-name"}
@@ -35,13 +35,13 @@
{:random-id-generator (constantly "some-id")
:db {:bootnodes/manage new-bootnode
:networks/current-network "mainnet_rpc"
:profile/profile {:custom-bootnodes
:multiaccount {:custom-bootnodes
{"mainnet_rpc"
{"a" {:name "name"
:address "url"
:chain "mainnet_rpc"
:id "a"}}}}}})]
(is (= expected (get-in actual [:db :profile/profile :custom-bootnodes]))))))
(is (= expected (get-in actual [:db :multiaccount :custom-bootnodes]))))))
(deftest set-input-bootnode
(testing "it validates names"
@@ -72,7 +72,7 @@
(deftest edit-bootnode
(let [db {:networks/current-network "mainnet_rpc"
:profile/profile
:multiaccount
{:custom-bootnodes
{"mainnet_rpc"
{"a" {:id "a"
@@ -123,7 +123,7 @@
(deftest fetch-bootnode
(testing "it fetches the bootnode from the db"
(let [cofx {:db {:networks/current-network "mainnet_rpc"
:profile/profile {:custom-bootnodes
:multiaccount {:custom-bootnodes
{"mainnet_rpc"
{"a" {:id "a"
:name "name"
@@ -138,7 +138,7 @@
(testing "it returns true when enabled"
(is (model/custom-bootnodes-in-use?
{:db {:networks/current-network "mainnet_rpc"
:profile/profile {:custom-bootnodes-enabled?
:multiaccount {:custom-bootnodes-enabled?
{"mainnet_rpc" true}}}}))))
(testing "is on a different network"
(testing "it returns false when not enabled"
@@ -146,13 +146,13 @@
(testing "it returns true when enabled"
(is (not (model/custom-bootnodes-in-use?
{:db {:networks/current-network "goerli_rpc"
:profile/profile {:custom-bootnodes-enabled?
:multiaccount {:custom-bootnodes-enabled?
{"mainnnet_rpc" true}}}}))))))
(deftest delete-bootnode
(testing "non existing bootnode"
(let [cofx {:db {:networks/current-network "mainnet_rpc"
:profile/profile {:custom-bootnodes
:multiaccount {:custom-bootnodes
{"mainnet_rpc"
{"a" {:id "a"
:name "name"
@@ -165,7 +165,7 @@
(is (model/fetch actual "a")))))
(testing "existing bootnode"
(let [cofx {:db {:networks/current-network "mainnet_rpc"
:profile/profile {:custom-bootnodes
:multiaccount {:custom-bootnodes
{"mainnet_rpc"
{"a" {:id "a"
:name "name"
+1 -1
View File
@@ -385,7 +385,7 @@
(rf/defn web3-send-async
[cofx dapp-name {:keys [method params id] :as payload} message-id]
(let [message? (web3-sign-message? method)
dapps-address (get-in cofx [:db :profile/profile :dapps-address])
dapps-address (get-in cofx [:db :multiaccount :dapps-address])
typed? (and (not= constants/web3-personal-sign method)
(not= constants/web3-eth-sign method))]
(if (or message? (= constants/web3-send-transaction method))
+1 -1
View File
@@ -42,7 +42,7 @@
(defn get-permission-data
[cofx allowed-permission]
(let [multiaccount (get-in cofx [:db :profile/profile])]
(let [multiaccount (get-in cofx [:db :multiaccount])]
(get {constants/dapp-permission-contact-code (:public-key multiaccount)
constants/dapp-permission-web3 [(:dapps-address multiaccount)]}
allowed-permission)))
+1 -1
View File
@@ -9,7 +9,7 @@
(let [dapp-name "test.com"
dapp-name2 "test2.org"
cofx {:db (assoc-in (:db (browser/open-url {:db {}} dapp-name))
[:profile/profile :public-key]
[:multiaccount :public-key]
"public-key")}
dapp-id (core.tests/get-dapp-id cofx dapp-name)]
(testing "dapps permissions are initialized"
+23 -8
View File
@@ -1,13 +1,16 @@
(ns status-im.chat.models.images
(:require ["react-native-blob-util" :default ReactNativeBlobUtil]
(:require ["@react-native-community/cameraroll" :as CameraRoll]
["react-native-blob-util" :default ReactNativeBlobUtil]
[re-frame.core :as re-frame]
[react-native.share :as share]
[react-native.cameraroll :as cameraroll]
[utils.i18n :as i18n]
[react-native.permissions :as permissions]
[status-im.ui.components.react :as react]
[status-im2.config :as config]
[react-native.fs :as fs]
[utils.re-frame :as rf]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]))
(def temp-image-url (str (fs/cache-dir) "/StatusIm_Image.jpeg"))
@@ -19,7 +22,7 @@
:path temp-image-url}))
(.fetch "GET" base64-uri)
(.then #(on-success (.path %)))
(.catch #(log/error "could not download image" {:error %}))))
(.catch #(log/error "could not save image"))))
(defn share-image
[uri]
@@ -30,11 +33,23 @@
#(fs/unlink downloaded-url)
#(fs/unlink downloaded-url)))))
(defn save-image-to-gallery
[base64-uri on-success]
(-> (download-image-http base64-uri cameraroll/save-image)
(.then on-success)
(.catch #(log/error (str "could not save image to gallery" %)))))
(defn save-to-gallery [path] (.save CameraRoll path))
(re-frame/reg-fx
::save-image-to-gallery
(fn [base64-uri]
(if platform/ios?
(-> (download-image-http base64-uri save-to-gallery)
(.catch #(utils/show-popup (i18n/label :t/error)
(i18n/label :t/external-storage-denied))))
(permissions/request-permissions
{:permissions [:write-external-storage]
:on-allowed #(download-image-http base64-uri save-to-gallery)
:on-denied (fn []
(utils/set-timeout
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/external-storage-denied))
50))}))))
(re-frame/reg-fx
::chat-open-image-picker-camera
+9 -10
View File
@@ -11,18 +11,17 @@
[status-im2.contexts.chat.composer.link-preview.events :as link-preview]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.string :as utils.string]))
[utils.re-frame :as rf]))
(defn text->emoji
"Replaces emojis in a specified `text`"
[text]
(utils.string/safe-replace text
#":([a-z_\-+0-9]*):"
(fn [[original emoji-id]]
(if-let [emoji-map (object/get (.-lib emojis) emoji-id)]
(.-char ^js emoji-map)
original))))
(utils/safe-replace text
#":([a-z_\-+0-9]*):"
(fn [[original emoji-id]]
(if-let [emoji-map (object/get (.-lib emojis) emoji-id)]
(.-char ^js emoji-map)
original))))
;; effects
(re-frame/reg-fx
@@ -148,7 +147,7 @@
(when-not (string/blank? input-text)
(let [{:keys [message-id]}
(get-in db [:chat/inputs current-chat-id :metadata :responding-to-message])
preferred-name (get-in db [:profile/profile :preferred-name])
preferred-name (get-in db [:multiaccount :preferred-name])
emoji? (message-content/emoji-only-content? {:text input-text
:response-to message-id})]
{:chat-id current-chat-id
@@ -170,7 +169,7 @@
{:chat-id chat-id
:album-id album-id
:content-type constants/content-type-image
:image-path (utils.string/safe-replace resized-uri #"file://" "")
:image-path (utils/safe-replace resized-uri #"file://" "")
:image-width width
:image-height height
:text input-text
+2 -2
View File
@@ -83,7 +83,7 @@
[{db :db} chat-id]
{:db (mark-chat-all-read db chat-id)
:clear-message-notifications [[chat-id]
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
:json-rpc/call [{:method "wakuext_markAllRead"
:params [chat-id]
:on-success #(re-frame/dispatch [::mark-all-read-successful])}]})
@@ -94,7 +94,7 @@
(let [community-chat-ids (map #(str community-id %)
(keys (get-in db [:communities community-id :chats])))]
{:clear-message-notifications [community-chat-ids
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
:json-rpc/call [{:method "wakuext_markAllReadInCommunity"
:params [community-id]
:on-success #(re-frame/dispatch
+1 -1
View File
@@ -9,7 +9,7 @@
{:db (assoc db
:commands/select-account
{:message message
:from (ethereum/get-default-account (:profile/wallet-accounts db))})
:from (ethereum/get-default-account (:multiaccount/accounts db))})
:show-select-acc-sheet nil})
(rf/defn set-selected-account
+6 -6
View File
@@ -11,7 +11,7 @@
[status-im.bottom-sheet.events :as bottom-sheet]
[status-im2.common.toasts.events :as toasts]
[status-im2.constants :as constants]
[status-im2.contexts.shell.activity-center.events :as activity-center]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
@@ -102,7 +102,7 @@
(rf/defn handle-requests-to-join
[{:keys [db]} requests]
(let [my-public-key (get-in db [:profile/profile :public-key])]
(let [my-public-key (get-in db [:multiaccount :public-key])]
{:db (reduce (fn [db {:keys [public-key] :as request}]
(let [my-request? (= my-public-key public-key)]
(if my-request?
@@ -278,7 +278,7 @@
(let [community-chat-ids (map #(str community-id %)
(keys (get-in db [:communities community-id :chats])))]
{:clear-message-notifications [community-chat-ids
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
:dispatch [:shell/close-switcher-card community-id]
:json-rpc/call [{:method "wakuext_leaveCommunity"
:params [community-id]
@@ -795,14 +795,14 @@
(rf/defn store-category-state
{:events [::store-category-state]}
[{:keys [db]} community-id category-id state-open?]
(let [public-key (get-in db [:profile/profile :public-key])
(let [public-key (get-in db [:multiaccount :public-key])
hash (category-hash public-key community-id category-id)]
{::async-storage/set! {hash state-open?}}))
(rf/defn update-category-states-in-db
{:events [::category-states-loaded]}
[{:keys [db]} community-id hashes states]
(let [public-key (get-in db [:profile/profile :public-key])
(let [public-key (get-in db [:multiaccount :public-key])
categories-old (get-in db [:communities community-id :categories])
categories (reduce (fn [acc [category-id category]]
(let [hash (get hashes category-id)
@@ -816,7 +816,7 @@
(rf/defn load-category-states
{:events [:communities/load-category-states]}
[{:keys [db]} community-id]
(let [public-key (get-in db [:profile/profile :public-key])
(let [public-key (get-in db [:multiaccount :public-key])
categories (get-in db [:communities community-id :categories])
{:keys [keys hashes]} (reduce (fn [acc category]
(let [category-id (get category 0)
+2 -2
View File
@@ -6,7 +6,7 @@
[status-im2.contexts.contacts.events :as contacts-store]
[utils.re-frame :as rf]
[status-im.utils.types :as types]
[status-im2.contexts.shell.activity-center.events :as activity-center]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.navigation.events :as navigation]))
(rf/defn clean-up-chat
@@ -49,7 +49,7 @@
(assoc-in [:contacts/contacts public-key :added?] false))
:dispatch [:shell/close-switcher-card public-key]
:clear-message-notifications
[[public-key] (get-in db [:profile/profile :remote-push-notifications-enabled?])]}
[[public-key] (get-in db [:multiaccount :remote-push-notifications-enabled?])]}
(activity-center/notifications-fetch-unread-count)
fxs)))
+1 -1
View File
@@ -5,7 +5,7 @@
(defn get-currency
[db]
(get-in db [:profile/profile :currency] :usd))
(get-in db [:multiaccount :currency] :usd))
(rf/defn set-currency
{:events [:wallet.settings.ui/currency-selected]}
+7 -9
View File
@@ -3,18 +3,16 @@
[status-im.currency.core :as models]))
(deftest get-currency
(is (= :usd (models/get-currency {:profile/profile {:currency :usd}})))
(is (= :usd (models/get-currency {:profile/profile {:not-empty "would throw an error if was empty"}})))
(is (= :aud (models/get-currency {:profile/profile {:currency :aud}}))))
(is (= :usd (models/get-currency {:multiaccount {:currency :usd}})))
(is (= :usd (models/get-currency {:multiaccount {:not-empty "would throw an error if was empty"}})))
(is (= :aud (models/get-currency {:multiaccount {:currency :aud}}))))
(deftest set-currency
(let [cofx (models/set-currency {:db {:profile/profile {:not-empty
"would throw an error if was empty"}}}
(let [cofx (models/set-currency {:db {:multiaccount {:not-empty "would throw an error if was empty"}}}
:usd)]
(is (= :usd (get-in cofx [:db :profile/profile :currency]))))
(is (= :usd (get-in cofx [:db :multiaccount :currency]))))
(is
(= :jpy
(get-in (models/set-currency {:db {:profile/profile {:not-empty
"would throw an error if was empty"}}}
(get-in (models/set-currency {:db {:multiaccount {:not-empty "would throw an error if was empty"}}}
:jpy)
[:db :profile/profile :currency]))))
[:db :multiaccount :currency]))))
+1 -1
View File
@@ -2,7 +2,7 @@
(:require [clojure.set :as set]
[status-im.data-store.messages :as messages]
[status-im2.constants :as constants]
[status-im2.contexts.shell.activity-center.notification-types :as notification-types]))
[status-im2.contexts.activity-center.notification-types :as notification-types]))
(defn mark-notifications-as-read
[notifications]
@@ -2,7 +2,7 @@
(:require [cljs.test :refer [deftest is testing]]
[status-im2.constants :as constants]
[status-im.data-store.activities :as store]
[status-im2.contexts.shell.activity-center.notification-types :as notification-types]))
[status-im2.contexts.activity-center.notification-types :as notification-types]))
(def chat-id
"0x04c66155")
+10 -10
View File
@@ -83,7 +83,7 @@
{:events [::save-username]}
[{:keys [db] :as cofx} custom-domain? username redirectToSummary]
(let [name (fullname custom-domain? username)
names (get-in db [:profile/profile :usernames] [])
names (get-in db [:multiaccount :usernames] [])
new-names (conj names name)]
(rf/merge cofx
(multiaccounts.update/multiaccount-update
@@ -102,7 +102,7 @@
[{:keys [db]}]
(let [{:keys [username address custom-domain?]} (:ens/registration db)
address (or address (ethereum/default-address db))
{:keys [public-key]} (:profile/profile db)
{:keys [public-key]} (:multiaccount db)
chain-id (ethereum/chain-id db)
username (fullname custom-domain? username)]
(ens/set-pub-key-prepare-tx
@@ -169,7 +169,7 @@
[{:keys [db]} address]
(let [{:keys [username]}
(:ens/registration db)
{:keys [public-key]} (:profile/profile db)
{:keys [public-key]} (:multiaccount db)
chain-id (ethereum/chain-id db)]
(ens/register-prepare-tx
chain-id
@@ -211,7 +211,7 @@
{:events [::set-username-candidate]}
[{:keys [db]} username]
(let [{:keys [custom-domain?]} (:ens/registration db)
usernames (into #{} (get-in db [:profile/profile :usernames]))
usernames (into #{} (get-in db [:multiaccount :usernames]))
state (state custom-domain? username usernames)]
(reset! resolve-last-id (random/id))
(merge
@@ -220,10 +220,10 @@
:username username
:state state)}
(when (= state :searching)
(let [{:profile/keys [profile]} db
{:keys [public-key]} profile
addresses (ethereum/addresses-without-watch db)
chain-id (ethereum/chain-id db)]
(let [{:keys [multiaccount]} db
{:keys [public-key]} multiaccount
addresses (ethereum/addresses-without-watch db)
chain-id (ethereum/chain-id db)]
{::resolve-owner [chain-id
(fullname custom-domain? username)
#(on-resolve-owner
@@ -323,8 +323,8 @@
(rf/defn remove-username
{:events [::remove-username]}
[{:keys [db] :as cofx} name]
(let [names (get-in db [:profile/profile :usernames] [])
preferred-name (get-in db [:profile/profile :preferred-name])
(let [names (get-in db [:multiaccount :usernames] [])
preferred-name (get-in db [:multiaccount :preferred-name])
new-names (remove #(= name %) names)]
(rf/merge cofx
(multiaccounts.update/multiaccount-update
+3 -3
View File
@@ -82,7 +82,7 @@
(defn current-address
[db]
(-> (get-in db [:profile/profile :address])
(-> (get-in db [:multiaccount :address])
normalized-hex))
(defn get-default-account
@@ -91,7 +91,7 @@
(defn default-address
[db]
(-> (get db :profile/wallet-accounts)
(-> (get db :multiaccount/accounts)
get-default-account
:address))
@@ -99,7 +99,7 @@
[db]
(into #{}
(remove #(= (:type %) :watch)
(map #(eip55/address->checksum (:address %)) (get db :profile/wallet-accounts)))))
(map #(eip55/address->checksum (:address %)) (get db :multiaccount/accounts)))))
(defn naked-address
[s]
@@ -401,5 +401,5 @@
[{:keys [db]}]
{:transactions/get-transfers
{:chain-tokens (:wallet/all-tokens db)
:addresses (map :address (get db :profile/wallet-accounts))
:addresses (map :address (get db :multiaccount/accounts))
:fetch-more? false}})
+8 -8
View File
@@ -52,8 +52,8 @@
status-im.wallet.choose-recipient.core
[status-im.wallet.core :as wallet]
status-im.wallet.custom-tokens.core
status-im2.contexts.shell.activity-center.events
status-im2.contexts.shell.activity-center.notification.contact-requests.events
status-im2.contexts.activity-center.events
status-im2.contexts.activity-center.notification.contact-requests.events
status-im2.contexts.shell.jump-to.events
status-im2.contexts.onboarding.events
status-im.chat.models.gaps
@@ -117,11 +117,11 @@
(rf/defn system-theme-mode-changed
{:events [:system-theme-mode-changed]}
[{:keys [db] :as cofx} _]
(let [current-theme-type (get-in cofx [:db :profile/profile :appearance])]
(let [current-theme-type (get-in cofx [:db :multiaccount :appearance])]
(when (and (multiaccounts.model/logged-in? db)
(= current-theme-type status-im2.constants/theme-type-system))
{:multiaccounts.ui/switch-theme-fx
[(get-in db [:profile/profile :appearance])
[(get-in db [:multiaccount :appearance])
(:view-id db) true]})))
(def authentication-options
@@ -146,7 +146,7 @@
[{:keys [db now] :as cofx}]
(let [new-account? (get db :onboarding-2/new-account?)
app-in-background-since (get db :app-in-background-since)
signed-up? (get-in db [:profile/profile :signed-up?])
signed-up? (get-in db [:multiaccount :signed-up?])
biometric-auth? (= (:auth-method db) "biometric")
requires-bio-auth (and
signed-up?
@@ -202,7 +202,7 @@
(cond
(= :chat view-id)
{::async-storage/set! {:chat-id (get-in cofx [:db :current-chat-id])
:key-uid (get-in cofx [:db :profile/profile :key-uid])}
:key-uid (get-in cofx [:db :multiaccount :key-uid])}
:db (assoc db :screens/was-focused-once? true)}
(= :login view-id)
@@ -291,7 +291,7 @@
(rf/defn close-information-box
{:events [:close-information-box]}
[{:keys [db]} id global?]
(let [public-key (get-in db [:profile/profile :public-key])
(let [public-key (get-in db [:multiaccount :public-key])
hash (information-box-id-hash id public-key global?)]
{::async-storage/set! {hash true}
:db (assoc-in db [:information-box-states id] true)}))
@@ -310,7 +310,7 @@
(rf/defn load-information-box-states
{:events [:load-information-box-states]}
[{:keys [db]}]
(let [public-key (get-in db [:profile/profile :public-key])
(let [public-key (get-in db [:multiaccount :public-key])
{:keys [keys hashes]} (reduce (fn [acc {:keys [id global?]}]
(let [hash (information-box-id-hash
id
+1 -1
View File
@@ -75,7 +75,7 @@
(rf/defn save
{:events [:fleet.ui/save-fleet-confirmed]}
[{:keys [db now] :as cofx} fleet]
(let [old-fleet (get-in db [:profile/profile :fleet])]
(let [old-fleet (get-in db [:multiaccount :fleet])]
(when (not= fleet old-fleet)
(rf/merge
cofx
+1 -1
View File
@@ -7,7 +7,7 @@
[re-frame.core :as re-frame]
[status-im2.contexts.chat.events :as chat.events]
[status-im2.constants :as constants]
[status-im2.contexts.shell.activity-center.events :as activity-center]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.navigation.events :as navigation]
[utils.re-frame :as rf]))
+88 -27
View File
@@ -12,6 +12,7 @@
status-im2.navigation.core
status-im2.subs.root ; so integration tests can run independently
[taoensso.timbre :as log]
[utils.security.core :as security]
[status-im2.constants :as constants]))
(def password "testabc")
@@ -24,7 +25,7 @@
(defn initialize-app!
[]
(rf/dispatch [:app-started]))
(rf/dispatch [:setup/app-started]))
(defn generate-and-derive-addresses!
[]
@@ -36,8 +37,20 @@
(defn assert-app-initialized
[]
(let [app-state @(rf/subscribe [:app-state])]
(is (= "active" app-state))))
(let [app-state @(rf/subscribe [:app-state])
multiaccounts-loading? @(rf/subscribe [:multiaccounts/loading])]
(is (= "active" app-state))
(is (false? multiaccounts-loading?))))
(defn assert-logout
[]
(let [multiaccounts-loading? @(rf/subscribe [:multiaccounts/loading])]
(is multiaccounts-loading?)))
(defn assert-multiaccounts-generated
[]
(let [wizard-state @(rf/subscribe [:intro-wizard/choose-key])]
(is (= 5 (count (:multiaccounts wizard-state))))))
(defn messenger-started
[]
@@ -47,6 +60,10 @@
[]
(is (messenger-started)))
(defn assert-multiaccount-loaded
[]
(is (false? @(rf/subscribe [:multiaccounts/loading]))))
(defn assert-community-created
[]
(is (= @(rf/subscribe [:communities/create]) community)))
@@ -60,7 +77,7 @@
(defn assert-new-account-created
[]
(is (true? (some #(= (:name %) account-name)
@(rf/subscribe [:profile/wallet-accounts])))))
@(rf/subscribe [:multiaccount/accounts])))))
(defn logout!
[]
@@ -69,11 +86,11 @@
(deftest initialize-app-test
(log/info "========= initialize-app-test ==================")
(rf-test/run-test-async
(rf/dispatch [:app-started])
(rf/dispatch [:setup/app-started])
(rf-test/wait-for
;; use initialize-view because it has the longest avg. time and
;; is dispatched by initialize-multiaccounts (last non-view event)
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(assert-app-initialized))))
(deftest create-account-test
@@ -81,26 +98,29 @@
(rf-test/run-test-async
(initialize-app!) ; initialize app
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!) ; generate 5 new keys
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(assert-multiaccount-loaded) ; assert keys are generated
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout)))))))
(deftest create-community-test
(log/info "====== create-community-test ==================")
(rf-test/run-test-async
(initialize-app!) ; initialize app
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!) ; generate 5 new keys
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(assert-multiaccount-loaded) ; assert keys are generated
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
@@ -113,17 +133,19 @@
[:status-im.communities.core/community-created]
(assert-community-created)
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout))))))))
(deftest create-wallet-account-test
(log/info "====== create-wallet-account-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!) ; generate 5 new keys
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(assert-multiaccount-loaded) ; assert keys are generated
(create-multiaccount!) ; create a multiaccount
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
@@ -133,28 +155,30 @@
[:wallet.accounts/account-stored]
(assert-new-account-created) ; assert account was created
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout))))))))
(deftest back-up-seed-phrase-test
(log/info "========= back-up-seed-phrase-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(assert-multiaccount-loaded)
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
(assert-messenger-started)
(rf/dispatch-sync [:set-in [:my-profile/seed :step] :12-words]) ; display seed phrase to user
(rf/dispatch-sync [:my-profile/enter-two-random-words]) ; begin prompting user for seed words
(let [{:keys [mnemonic]} @(rf/subscribe [:profile/profile])
seed @(rf/subscribe [:my-profile/seed])
word1 (second (:first-word seed))
word2 (second (:second-word seed))]
(is (= 12 (count (string/split mnemonic #" ")))) ; assert 12-word seed phrase
(let [ma @(rf/subscribe [:multiaccount])
seed @(rf/subscribe [:my-profile/seed])
word1 (second (:first-word seed))
word2 (second (:second-word seed))]
(is (= 12 (count (string/split (:mnemonic ma) #" ")))) ; assert 12-word seed phrase
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word1])
(rf/dispatch-sync [:my-profile/set-step :second-word])
(rf/dispatch-sync [:set-in [:my-profile/seed :word] word2])
@@ -164,13 +188,42 @@
[:my-profile/finish-success]
(is (nil? @(rf/subscribe [:mnemonic]))) ; assert seed phrase has been removed
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout)))))))))
(def multiaccount-name "Narrow Frail Lemming")
(def multiaccount-mnemonic
"tattoo ramp health green tongue universe style vapor become tape lava reason")
(def multiaccount-key-uid "0x694b8229524820a3a00a6e211141561d61b251ad99d6b65daf82a73c9a57697b")
(deftest recover-multiaccount-test
(log/info "========= recover-multiaccount-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:setup/initialize-view]
(rf/dispatch-sync [:init-root :onboarding])
(rf/dispatch-sync [:multiaccounts.recover.ui/recover-multiaccount-button-pressed])
(rf/dispatch-sync [:status-im.multiaccounts.recover.core/enter-phrase-pressed])
(rf/dispatch-sync [:multiaccounts.recover/enter-phrase-input-changed
(security/mask-data multiaccount-mnemonic)])
(rf/dispatch [:multiaccounts.recover/enter-phrase-next-pressed])
(rf-test/wait-for
[:status-im.multiaccounts.recover.core/import-multiaccount-success]
(rf/dispatch-sync [:multiaccounts.recover/re-encrypt-pressed])
(rf/dispatch [:multiaccounts.recover/enter-password-next-pressed password])
(rf-test/wait-for
[:status-im.multiaccounts.recover.core/store-multiaccount-success]
(let [multiaccount @(rf/subscribe [:multiaccount])] ; assert multiaccount is recovered
(is (= multiaccount-key-uid (:key-uid multiaccount)))
(is (= multiaccount-name (:name multiaccount))))
(rf-test/wait-for ; wait for login
[::transport/messenger-started]
(assert-messenger-started)
(logout!)
(rf-test/wait-for [::logout/logout-method]
(assert-logout))))))))
(def chat-id
"0x0402905bed83f0bbf993cee8239012ccb1a8bc86907ead834c1e38476a0eda71414eed0e25f525f270592a2eebb01c9119a4ed6429ba114e51f5cb0a28dae1adfd")
@@ -179,10 +232,11 @@
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(assert-multiaccount-loaded)
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
@@ -193,17 +247,19 @@
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout))))))))
(deftest delete-chat-test
(log/info "========= delete-chat-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(assert-multiaccount-loaded)
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
@@ -217,17 +273,19 @@
(rf/dispatch-sync [:chat.ui/show-remove-confirmation chat-id])
(rf/dispatch-sync [:chat.ui/remove-chat chat-id])
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout))))))))
(deftest mute-chat-test
(log/info "========= mute-chat-test ==================")
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success] ; wait for the keys
(assert-multiaccount-loaded)
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
@@ -247,7 +305,8 @@
[:chat/mute-successfully]
(is (not @(rf/subscribe [:chats/muted chat-id])))
(logout!)
(rf-test/wait-for [::logout/logout-method])))))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout))))))))))
(deftest add-contact-test
(log/info "========= add-contact-test ==================")
@@ -259,10 +318,11 @@
(rf-test/run-test-async
(initialize-app!)
(rf-test/wait-for
[:profile/get-profiles-overview-success]
[:setup/initialize-view]
(generate-and-derive-addresses!)
(rf-test/wait-for
[:multiaccount-generate-and-derive-addresses-success]
(assert-multiaccount-loaded)
(create-multiaccount!)
(rf-test/wait-for
[::transport/messenger-started]
@@ -283,4 +343,5 @@
(let [contact @(rf/subscribe [:contacts/current-contact])]
(is (= three-words-name (:primary-name contact))))
(logout!)
(rf-test/wait-for [::logout/logout-method]))))))))))
(rf-test/wait-for [::logout/logout-method]
(assert-logout)))))))))))
+3 -3
View File
@@ -17,7 +17,7 @@
(rf/merge cofx
{:db (-> db
(assoc-in [:keycard :creating-backup?] backup-type))}
(when (:profile/profile db)
(when (:multiaccount db)
(navigation/navigate-to :my-profile nil))
(navigation/navigate-to :seed-phrase nil)))
@@ -48,7 +48,7 @@
{:events [::start-keycard-backup]}
[{:keys [db] :as cofx}]
{::multiaccounts.recover/import-multiaccount {:passphrase (-> db
:profile/key-storage
:multiaccounts/key-storage
:seed-phrase
mnemonic/sanitize-passphrase)
:password nil
@@ -66,7 +66,7 @@
:recovering? true
:selected-storage-type :advanced)
(assoc-in [:keycard :flow] :recovery)
(update :profile/key-storage dissoc :seed-phrase))
(update :multiaccounts/key-storage dissoc :seed-phrase))
:dismiss-keyboard nil}
(common/listen-to-hardware-back-button)
(navigation/navigate-to :keycard-onboarding-intro nil)))
+1 -1
View File
@@ -152,7 +152,7 @@
(if puk-restore?
(navigation/navigate-to :multiaccounts nil)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings]))
(when (:profile/login db)
(when (:multiaccounts/login db)
(common/get-keys-from-keycard)))))
(rf/defn on-change-puk-success
+14 -14
View File
@@ -52,7 +52,7 @@
:init
has-master-key?
:profile/profile
:multiaccount
(and (not paired?)
(zero? free-pairing-slots))
@@ -68,7 +68,7 @@
(defn find-multiaccount-by-keycard-instance-uid
[db keycard-instance-uid]
(when keycard-instance-uid
(->> (:profile/profiles-overview db)
(->> (:multiaccounts/multiaccounts db)
vals
(filter #(= keycard-instance-uid (:keycard-instance-uid %)))
first)))
@@ -76,7 +76,7 @@
(defn find-multiaccount-by-key-uid
[db key-uid]
(when key-uid
(->> (:profile/profiles-overview db)
(->> (:multiaccounts/multiaccounts db)
vals
(filter #(= (ethereum/normalized-hex key-uid) (:key-uid %)))
first)))
@@ -86,7 +86,7 @@
(get-pairing db (get-in db [:keycard :application-info :key-uid])))
([db key-uid]
(or
(get-in db [:profile/profile :keycard-pairing])
(get-in db [:multiaccount :keycard-pairing])
(get-in db [:keycard :secrets :pairing])
(when key-uid
(:keycard-pairing
@@ -324,7 +324,7 @@
(rf/defn get-keys-from-keycard
[{:keys [db]}]
(let [key-uid (get-in db [:profile/login :key-uid])
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
pin (string/join (get-in db [:keycard :pin :login]))]
(log/debug "[keycard] get-keys-from-keycard"
", not empty pin:"
@@ -339,11 +339,11 @@
(let [{:keys [key-uid encryption-public-key whisper-private-key]
:as account-data}
(js->clj data :keywordize-keys true)
{:keys [name]} (get-in db [:profile/profiles-overview key-uid])
{:keys [name]} (get-in db [:multiaccounts/multiaccounts key-uid])
key-uid (get-in db [:keycard :application-info :key-uid])
multiaccount-data (types/clj->json {:name name
:key-uid key-uid})
save-keys? (get-in db [:profile/login :save-password?])]
save-keys? (get-in db [:multiaccounts/login :save-password?])]
(rf/merge cofx
{:db
(-> db
@@ -353,13 +353,13 @@
assoc
:puk-retry-counter 5
:pin-retry-counter 3)
(assoc-in [:keycard :profile/profile]
(assoc-in [:keycard :multiaccount]
(update account-data :whisper-public-key ethereum/normalized-hex))
(assoc-in [:keycard :flow] nil)
(update :profile/login assoc
:password encryption-public-key
:key-uid key-uid
:name name))
(update :multiaccounts/login assoc
:password encryption-public-key
:key-uid key-uid
:name name))
:keycard/login-with-keycard {:multiaccount-data multiaccount-data
:password encryption-public-key
@@ -379,7 +379,7 @@
(hide-connection-sheet)
; do not try to display the popover if it is already open or
; we are in the login interface (which has a different handling)
(when-not (or (:profile/login db) (:popover/popover db))
(when-not (or (:multiaccounts/login db) (:popover/popover db))
(popover/show-popover {:view card-status}))))
(rf/defn blocked-keycard-popup
@@ -536,7 +536,7 @@
(defn keycard-multiaccount?
[db]
(boolean (get-in db [:profile/profile :keycard-pairing])))
(boolean (get-in db [:multiaccount :keycard-pairing])))
(rf/defn verify-pin
{:events [:keycard/verify-pin]}
+3 -3
View File
@@ -543,7 +543,7 @@
:not-paired :pair
:no-pairing-slots :no-slots
:init :card-ready
:profile/profile :import-multiaccount
:multiaccount :import-multiaccount
:begin))})
(rf/defn show-no-keycard-applet-alert
@@ -586,7 +586,7 @@
(common/clear-on-card-read)
(load-pin-screen)))))
(when (and (= card-state :profile/profile)
(when (and (= card-state :multiaccount)
(= flow :import))
(if (common/find-multiaccount-by-key-uid db key-uid)
(multiaccounts.recover/show-existing-multiaccount-alert key-uid)
@@ -599,7 +599,7 @@
(navigation/navigate-to :keycard-recovery-no-key nil)
(show-no-keycard-applet-alert)))
(when (and (= card-state :profile/profile)
(when (and (= card-state :multiaccount)
(#{:create :recovery} flow))
(show-keycard-has-multiaccount-alert)))))
+1 -1
View File
@@ -62,7 +62,7 @@
"04f2a432677a1b7c4f1bb22078135821d1d10fce23422297b5c808a545f2b61cdba38ee7394762172fc6ff5e9e28db7535e555efe2812905ffd4e0c25e82a98ae8"
"whisper-address" "87df2285f90b71221fab6267b7cb37532fedbb1f"
"wallet-address" "7e92236392a850980d00d0cd2a4b92886bd7fe7b"})
[:db :keycard :profile/profile])
[:db :keycard :multiaccount])
[:whisper-private-key
:whisper-public-key
:encryption-public-key
+1 -1
View File
@@ -11,7 +11,7 @@
(rf/defn delete-card
[{:keys [db] :as cofx}]
(let [key-uid (get-in db [:keycard :application-info :key-uid])
multiaccount-key-uid (get-in db [:profile/profile :key-uid])]
multiaccount-key-uid (get-in db [:multiaccount :key-uid])]
(if (and key-uid
(= key-uid multiaccount-key-uid))
{:keycard/delete nil}
+10 -10
View File
@@ -69,9 +69,9 @@
:error nil
:status nil))
:hide-popover nil})
(when (:profile/profile db)
(when (:multiaccount db)
(navigation/navigate-to :my-profile nil))
(when-not (:profile/login db)
(when-not (:multiaccounts/login db)
(if (:popover/popover db)
(navigation/navigate-replace :keycard-pin nil)
(navigation/navigate-to :keycard-pin nil)))))
@@ -94,7 +94,7 @@
key-uid (get-in db [:keycard :application-info :key-uid])
paired? (get-in db [:keycard :application-info :paired?])
multiaccount (get-in db
[:profile/profiles-overview (get-in db [:profile/login :key-uid])])
[:multiaccounts/multiaccounts (get-in db [:multiaccounts/login :key-uid])])
multiaccount-key-uid (get multiaccount :key-uid)
multiaccount-mismatch? (or (nil? multiaccount)
(not= multiaccount-key-uid key-uid))]
@@ -151,7 +151,7 @@
:multiaccounts-stack
[:multiaccounts
:keycard-login-pin])
(let [{:keys [name]} (get-in db [:profile/profiles-overview key-uid])
(let [{:keys [name]} (get-in db [:multiaccounts/multiaccounts key-uid])
multiaccount-data (types/clj->json {:name name
:key-uid key-uid})
account-data {:key-uid key-uid
@@ -161,14 +161,14 @@
(-> db
(assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :pin :login] [])
(assoc-in [:keycard :profile/profile]
(assoc-in [:keycard :multiaccount]
(update account-data :whisper-public-key ethereum/normalized-hex))
(assoc-in [:keycard :flow] nil)
(update :profile/login assoc
:password encryption-public-key
:key-uid key-uid
:name name
:save-password? true))
(update :multiaccounts/login assoc
:password encryption-public-key
:key-uid key-uid
:name name
:save-password? true))
:keycard/login-with-keycard
{:multiaccount-data multiaccount-data
:key-uid key-uid
+15 -16
View File
@@ -134,7 +134,7 @@
(rf/defn intro-wizard
{:events [:multiaccounts.create.ui/intro-wizard]}
[{:keys [db] :as cofx}]
(let [accs (get db :profile/profiles-overview)]
(let [accs (get db :multiaccounts/multiaccounts)]
(rf/merge cofx
{:db (-> db
(update :keycard dissoc :flow)
@@ -157,7 +157,7 @@
:interceptors [(re-frame/inject-cofx :random-guid-generator)
(re-frame/inject-cofx ::multiaccounts.create/get-signing-phrase)]}
[{:keys [db] :as cofx}]
(let [{{:keys [secrets flow] :profile/keys [profile]} :keycard} db
(let [{{:keys [multiaccount secrets flow]} :keycard} db
{:keys [address
name
public-key
@@ -172,7 +172,7 @@
instance-uid
key-uid
recovered]}
profile
multiaccount
{:keys [pairing paired-on]} secrets
{:keys [name]}
(if (nil? name)
@@ -238,7 +238,7 @@
(multiaccounts.model/logged-in? db)
(navigation/set-stack-root :profile-stack [:my-profile :keycard-settings])
(:profile/login db)
(:multiaccounts/login db)
(return-to-keycard-login)
:else
@@ -263,9 +263,9 @@
[{:keys [db] :as cofx}]
(let [pairing (get-in db [:keycard :secrets :pairing])
paired-on (get-in db [:keycard :secrets :paired-on])
instance-uid (get-in db [:keycard :profile/profile :instance-uid])
instance-uid (get-in db [:keycard :multiaccount :instance-uid])
account (-> db
:profile/login
:multiaccounts/login
(assoc :keycard-pairing pairing)
(assoc :save-password? false))
key-uid (-> account :key-uid)
@@ -275,15 +275,14 @@
password (ethereum/sha3 (security/safe-unmask-data (get-in db
[:keycard
:migration-password])))
encryption-pass (get-in db [:keycard :profile/profile :encryption-public-key])
encryption-pass (get-in db [:keycard :multiaccount :encryption-public-key])
login-params {:key-uid key-uid
:multiaccount-data (types/clj->json account)
:password encryption-pass
:chat-key (get-in db
[:keycard :profile/profile :whisper-private-key])}]
:chat-key (get-in db [:keycard :multiaccount :whisper-private-key])}]
{:db (-> db
(assoc-in [:profile/profiles-overview key-uid :keycard-pairing] pairing)
(assoc :profile/login account)
(assoc-in [:multiaccounts/multiaccounts key-uid :keycard-pairing] pairing)
(assoc :multiaccounts/login account)
(assoc :auth-method keychain/auth-method-none)
(update :keycard dissoc :flow :migration-password)
(dissoc :recovered-account?))
@@ -291,7 +290,7 @@
(rf/defn delete-multiaccount
[{:keys [db]}]
(let [key-uid (get-in db [:profile/login :key-uid])]
(let [key-uid (get-in db [:multiaccounts/login :key-uid])]
{:keycard/delete-multiaccount-before-migration
{:key-uid key-uid
:on-error #(re-frame/dispatch [::delete-multiaccount-error %])
@@ -314,7 +313,7 @@
cofx
{:db (-> db
(assoc-in
[:keycard :profile/profile]
[:keycard :multiaccount]
(-> account-data
(update :address ethereum/normalized-hex)
(update :whisper-address ethereum/normalized-hex)
@@ -324,7 +323,7 @@
(update :whisper-public-key ethereum/normalized-hex)
(update :wallet-public-key ethereum/normalized-hex)
(update :wallet-root-public-key ethereum/normalized-hex)
(update :instance-uid #(get-in db [:keycard :profile/profile :instance-uid] %))))
(update :instance-uid #(get-in db [:keycard :multiaccount :instance-uid] %))))
(assoc-in [:keycard :multiaccount-wallet-address] (:wallet-address account-data))
(assoc-in [:keycard :multiaccount-whisper-public-key] (:whisper-public-key account-data))
(assoc-in [:keycard :pin :status] nil)
@@ -363,7 +362,7 @@
pin (common/vector->string (get-in db [:keycard :pin :import-multiaccount]))]
(rf/merge cofx
{:db (-> db
(assoc-in [:keycard :profile/profile :instance-uid] instance-uid)
(assoc-in [:keycard :multiaccount :instance-uid] instance-uid)
(assoc-in [:keycard :pin :status] :verifying)
(assoc-in [:keycard :secrets]
{:pairing pairing'
@@ -388,7 +387,7 @@
(rf/merge
cofx
{:db (update-in db
[:keycard :profile/profile]
[:keycard :multiaccount]
(fn [multiacc]
(assoc multiacc
:recovered (get db :recovered-account?)
+2 -2
View File
@@ -12,7 +12,7 @@
{:events [:keycard/sign]}
[{:keys [db] :as cofx} hash on-success]
(let [card-connected? (get-in db [:keycard :card-connected?])
key-uid (get-in db [:profile/profile :key-uid])
key-uid (get-in db [:multiaccount :key-uid])
keycard-key-uid (get-in db [:keycard :application-info :key-uid])
keycard-pin-retries (get-in db [:keycard :application-info :pin-retry-counter])
keycard-match? (= key-uid keycard-key-uid)
@@ -28,7 +28,7 @@
(when (ethereum/address= from address)
(reduced path)))
nil
(:profile/wallet-accounts db))]
(:multiaccount/accounts db))]
(cond
(not keycard-match?)
(common/show-wrong-keycard-alert cofx)
+6 -6
View File
@@ -44,7 +44,7 @@
:initialized? true
:pin-retry-counter 3
:puk-retry-counter 5
:key-uid (get-in @re-frame.db/app-db [:profile/login :key-uid])})
:key-uid (get-in @re-frame.db/app-db [:multiaccounts/login :key-uid])})
(swap! state assoc :pin default-pin :puk default-puk :password kk1-password)
(connect-card))
@@ -389,11 +389,11 @@
pin
on-failure
#(let [{:keys [key-uid wallet-root-address]}
(get @re-frame.db/app-db :profile/profile)
accounts (get @re-frame.db/app-db :profile/wallet-accounts)
(get @re-frame.db/app-db :multiaccount)
accounts (get @re-frame.db/app-db :multiaccount/accounts)
hashed-password account-password
path-num (inc (get-in @re-frame.db/app-db
[:profile/profile :latest-derived-path]))
[:multiaccount :latest-derived-path]))
path (str "m/" path-num)]
(native-module/multiaccount-load-account
wallet-root-address
@@ -460,8 +460,8 @@
(when (= path (:path acc))
(reduced address)))
nil
(:profile/wallet-accounts @re-frame.db/app-db))
(-> (:profile/wallet-accounts @re-frame.db/app-db)
(:multiaccount/accounts @re-frame.db/app-db))
(-> (:multiaccount/accounts @re-frame.db/app-db)
first
:address))
password account-password]
+27 -27
View File
@@ -2,6 +2,7 @@
(:require [re-frame.core :as re-frame]
[utils.i18n :as i18n]
[status-im.keycard.common :as common]
[status-im.multiaccounts.key-storage.core :as key-storage]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[utils.re-frame :as rf]
[status-im2.navigation.events :as navigation]
@@ -118,37 +119,36 @@
(defn handle-account-removal
[{:keys [db] :as cofx} keys-removed-from-card?]
(let [key-uid (get-in db [:profile/profile :key-uid])
(let [key-uid (get-in db [:multiaccount :key-uid])
instance-uid (get-in db [:keycard :application-info :instance-uid])
pairings (get-in db [:keycard :pairings])]
(rf/merge
cofx
{:db (-> db
(update :profile/profiles-overview dissoc key-uid)
(assoc-in [:keycard :secrets] nil)
(update-in [:keycard :pairings]
dissoc
(keyword instance-uid))
(update-in [:keycard :pairings] dissoc instance-uid)
(assoc-in [:keycard :whisper-public-key] nil)
(assoc-in [:keycard :wallet-address] nil)
(assoc-in [:keycard :application-info] nil)
(assoc-in [:keycard :pin]
{:status nil
:error-label nil
:on-verified nil}))
:keycard/persist-pairings (dissoc pairings (keyword instance-uid))
:utils/show-popup {:title (i18n/label (if keys-removed-from-card?
:t/profile-deleted-title
:t/database-reset-title))
:content (i18n/label (if keys-removed-from-card?
:t/profile-deleted-keycard
:t/database-reset-content))
:on-dismiss #(re-frame/dispatch [:logout])}}
;;should be reimplemented
;;:key-storage/delete-profile {:key-uid key-uid
;;:on-success #(log/debug "[keycard] remove account ok")
;; :on-error #(log/warn "[keycard] remove account: " %)}
{:db (-> db
(update :multiaccounts/multiaccounts dissoc key-uid)
(assoc-in [:keycard :secrets] nil)
(update-in [:keycard :pairings]
dissoc
(keyword instance-uid))
(update-in [:keycard :pairings] dissoc instance-uid)
(assoc-in [:keycard :whisper-public-key] nil)
(assoc-in [:keycard :wallet-address] nil)
(assoc-in [:keycard :application-info] nil)
(assoc-in [:keycard :pin]
{:status nil
:error-label nil
:on-verified nil}))
:keycard/persist-pairings (dissoc pairings (keyword instance-uid))
:utils/show-popup {:title (i18n/label (if keys-removed-from-card?
:t/profile-deleted-title
:t/database-reset-title))
:content (i18n/label (if keys-removed-from-card?
:t/profile-deleted-keycard
:t/database-reset-content))
:on-dismiss #(re-frame/dispatch [:logout])}
::key-storage/delete-multiaccount {:key-uid key-uid
:on-success #(log/debug "[keycard] remove account ok")
:on-error #(log/warn "[keycard] remove account: " %)}}
(common/clear-on-card-connected)
(common/hide-connection-sheet))))
+1 -1
View File
@@ -34,7 +34,7 @@
(rf/defn generate-new-keycard-account
{:events [:wallet.accounts/generate-new-keycard-account]}
[{:keys [db]}]
(let [path-num (inc (get-in db [:profile/profile :latest-derived-path]))
(let [path-num (inc (get-in db [:multiaccount :latest-derived-path]))
path (str constants/path-wallet-root "/" path-num)
pin (common/vector->string (get-in db [:keycard :pin :export-key]))]
{:db
+1 -1
View File
@@ -8,7 +8,7 @@
(rf/defn save-log-level
{:events [:log-level.ui/change-log-level-confirmed]}
[{:keys [db now] :as cofx} log-level]
(let [old-log-level (get-in db [:profile/profile :log-level])]
(let [old-log-level (get-in db [:multiaccount :log-level])]
(when (not= old-log-level log-level)
(rf/merge cofx
(multiaccounts.update/multiaccount-update
+9 -9
View File
@@ -36,7 +36,7 @@
(defn preferred-mailserver-id
[db]
(get-in db [:profile/profile :pinned-mailservers (node/current-fleet-key db)]))
(get-in db [:multiaccount :pinned-mailservers (node/current-fleet-key db)]))
(defn connection-error-dismissed
[db]
@@ -61,7 +61,7 @@
(defn fetch-use-mailservers?
[{:keys [db]}]
(get-in db [:profile/profile :use-mailservers?]))
(get-in db [:multiaccount :use-mailservers?]))
(defonce showing-connection-error-popup? (atom false))
@@ -105,7 +105,7 @@
[{:keys [db] :as cofx}]
(let [current-fleet (node/current-fleet-key db)
error-dismissed? (connection-error-dismissed db)
pinned-mailserver (get-in db [:profile/profile :pinned-mailservers current-fleet])]
pinned-mailserver (get-in db [:multiaccount :pinned-mailservers current-fleet])]
(when (and pinned-mailserver
(not error-dismissed?)
(not @showing-connection-error-popup?))
@@ -266,9 +266,9 @@
(rf/defn upsert
{:events [:mailserver.ui/save-pressed]
:interceptors [(re-frame/inject-cofx :random-id-generator)]}
[{{:mailserver.edit/keys [mailserver] :profile/keys [profile] :as db} :db
random-id-generator :random-id-generator
:as cofx}]
[{{:mailserver.edit/keys [mailserver] :keys [multiaccount] :as db} :db
random-id-generator :random-id-generator
:as cofx}]
(let [{:keys [name url id]} mailserver
current-fleet (node/current-fleet-key db)]
@@ -364,7 +364,7 @@
{:events [:mailserver.ui/connect-confirmed]}
[{:keys [db] :as cofx} current-fleet mailserver-id]
(let [pinned-mailservers (-> db
(get-in [:profile/profile :pinned-mailservers])
(get-in [:multiaccount :pinned-mailservers])
(assoc current-fleet mailserver-id))]
(rf/merge cofx
{:db (assoc db :mailserver/current-id mailserver-id)
@@ -379,7 +379,7 @@
[{:keys [db] :as cofx}]
(let [current-fleet (node/current-fleet-key db)
pinned-mailservers (-> db
(get-in [:profile/profile :pinned-mailservers])
(get-in [:multiaccount :pinned-mailservers])
(dissoc current-fleet))]
(rf/merge cofx
{:json-rpc/call [{:method "wakuext_setPinnedMailservers"
@@ -396,7 +396,7 @@
[{:keys [db] :as cofx}]
(let [current-fleet (node/current-fleet-key db)
mailserver-id (:mailserver/current-id db)
pinned-mailservers (get-in db [:profile/profile :pinned-mailservers])]
pinned-mailservers (get-in db [:multiaccount :pinned-mailservers])]
(rf/merge cofx
(multiaccounts.update/multiaccount-update
:pinned-mailservers
+4 -4
View File
@@ -12,7 +12,7 @@
(rf/defn sheet-defaults
[{:keys [db]}]
(let [remember-choice? (get-in db [:profile/profile :remember-syncing-choice?])]
(let [remember-choice? (get-in db [:multiaccount :remember-syncing-choice?])]
{:db (assoc db
:mobile-network/remember-choice?
(or (nil? remember-choice?)
@@ -22,7 +22,7 @@
[{:keys [db] :as cofx}]
(let [initialized? (get db :network-status/initialized?)
logged-in? (multiaccounts.model/logged-in? db)
{:keys [remember-syncing-choice?]} (:profile/profile db)]
{:keys [remember-syncing-choice?]} (:multiaccount db)]
(apply
rf/merge
cofx
@@ -91,13 +91,13 @@
(rf/defn mobile-network-set-syncing
{:events [:mobile-network/set-syncing]}
[{:keys [db] :as cofx} syncing?]
(let [{:keys [remember-syncing-choice?]} (:profile/profile db)]
(let [{:keys [remember-syncing-choice?]} (:multiaccount db)]
((apply-settings syncing? remember-syncing-choice?) cofx)))
(rf/defn mobile-network-ask-on-mobile-network?
{:events [:mobile-network/ask-on-mobile-network?]}
[{:keys [db] :as cofx} ask?]
(let [{:keys [syncing-on-mobile-network?]} (:profile/profile db)]
(let [{:keys [syncing-on-mobile-network?]} (:multiaccount db)]
((apply-settings syncing-on-mobile-network? (not ask?)) cofx)))
(rf/defn mobile-network-restore-defaults
@@ -101,7 +101,7 @@
(.catch #(cb (generate-error-result %))))))
(re-frame/reg-fx
:biometric/get-supported-biometric-auth
:get-supported-biometric-auth
(fn []
(let [callback #(re-frame/dispatch [:init.callback/get-supported-biometric-auth-success %])]
;;NOTE: if we can't save user password, we can't support biometrics
@@ -149,8 +149,8 @@
(rf/defn update-biometric
[{db :db :as cofx} biometric-auth?]
(let [key-uid (or (get-in db [:profile/profile :key-uid])
(get-in db [:profile/login :key-uid]))]
(let [key-uid (or (get-in db [:multiaccount :key-uid])
(get-in db [:multiaccounts/login :key-uid]))]
(rf/merge cofx
(keychain/save-auth-method
key-uid
@@ -214,7 +214,7 @@
cofx
{:db (-> db
(assoc :auth-method keychain/auth-method-none)
(assoc-in [:profile/login :save-password?] false))}
(assoc-in [:multiaccounts/login :save-password?] false))}
(popover/hide-popover)))
(rf/defn setup-done
+6 -6
View File
@@ -120,7 +120,7 @@
(rf/defn switch-preview-privacy-mode-flag
[{:keys [db]}]
(let [private? (get-in db [:profile/profile :preview-privacy?])]
(let [private? (get-in db [:multiaccount :preview-privacy?])]
{::blank-preview-flag-changed private?}))
(re-frame/reg-fx
@@ -153,7 +153,7 @@
{:events [:multiaccounts.ui/switch-theme]}
[cofx theme view-id]
(let [theme (or theme
(get-in cofx [:db :profile/profile :appearance])
(get-in cofx [:db :multiaccount :appearance])
constants/theme-type-dark)]
{:multiaccounts.ui/switch-theme-fx [theme view-id false]}))
@@ -180,7 +180,7 @@
(rf/defn save-profile-picture
{:events [::save-profile-picture]}
[cofx path ax ay bx by]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
(rf/merge cofx
{:json-rpc/call [{:method "multiaccounts_storeIdentityImage"
:params [key-uid (clean-path path) ax ay bx by]
@@ -191,7 +191,7 @@
(rf/defn save-profile-picture-from-url
{:events [::save-profile-picture-from-url]}
[cofx url]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
(rf/merge cofx
{:json-rpc/call [{:method "multiaccounts_storeIdentityImageFromURL"
:params [key-uid url]
@@ -207,7 +207,7 @@
(rf/defn delete-profile-picture
{:events [::delete-profile-picture]}
[cofx name]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
(rf/merge cofx
{:json-rpc/call [{:method "multiaccounts_deleteIdentityImage"
:params [key-uid]
@@ -219,7 +219,7 @@
(rf/defn get-profile-picture
[cofx]
(let [key-uid (get-in cofx [:db :profile/profile :key-uid])]
(let [key-uid (get-in cofx [:db :multiaccount :key-uid])]
{:json-rpc/call [{:method "multiaccounts_getIdentityImages"
:params [key-uid]
:on-success #(re-frame/dispatch [::update-local-picture %])}]}))
+3 -3
View File
@@ -225,13 +225,13 @@
:keycard-pairing keycard-pairing
:keycard-paired-on keycard-paired-on))
db (assoc db
:profile/login {:key-uid key-uid
:multiaccounts/login {:key-uid key-uid
:name name
:password password
:creating? true
:processing true}
:profile/profile new-multiaccount
:profile/wallet-accounts [wallet-account]
:multiaccount new-multiaccount
:multiaccount/accounts [wallet-account]
:networks/current-network config/default-network
:networks/networks (data-store.settings/rpc->networks config/default-networks))
settings (assoc new-multiaccount
@@ -1,13 +1,150 @@
(ns status-im.multiaccounts.key-storage.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im.bottom-sheet.events :as bottom-sheet]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.mnemonic :as mnemonic]
[utils.i18n :as i18n]
[status-im.keycard.backup-key :as keycard.backup]
[status-im.keycard.common :as common]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.logout.core :as multiaccounts.logout]
[status-im.multiaccounts.model :as multiaccounts.model]
[status-im.multiaccounts.recover.core :as multiaccounts.recover]
[native-module.core :as native-module]
[status-im.popover.core :as popover]
[utils.re-frame :as rf]
[status-im.utils.types :as types]
[status-im2.navigation.events :as navigation]
[utils.security.core :as security]))
(rf/defn key-and-storage-management-pressed
"This event can be dispatched before login and from profile and needs to redirect accordingly"
{:events [::key-and-storage-management-pressed]}
[{:keys [db] :as cofx}]
(navigation/navigate-to
cofx
(if (multiaccounts.model/logged-in? db)
:actions-logged-in
:actions-not-logged-in)
nil))
(rf/defn move-keystore-checked
{:events [::move-keystore-checked]}
[{:keys [db] :as cofx} checked?]
{:db (assoc-in db [:multiaccounts/key-storage :move-keystore-checked?] checked?)})
(rf/defn reset-db-checked
{:events [::reset-db-checked]}
[{:keys [db] :as cofx} checked?]
{:db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] checked?)})
(rf/defn navigate-back
{:events [::navigate-back]}
[{:keys [db] :as cofx}]
(rf/merge
cofx
{:db (-> db
(dissoc :recovered-account?)
(update :keycard
dissoc
:from-key-storage-and-migration?
:creating-backup?
:factory-reset-card?))}
(navigation/navigate-back)))
(rf/defn enter-seed-pressed
"User is logged out and probably wants to move multiaccount to Keycard. Navigate to enter seed phrase screen"
{:events [::enter-seed-pressed]}
[{:keys [db] :as cofx}]
(rf/merge
cofx
{:db (assoc db :recovered-account? true)}
(navigation/navigate-to :seed-phrase nil)))
(rf/defn seed-phrase-input-changed
{:events [::seed-phrase-input-changed]}
[{:keys [db] :as cofx} masked-seed-phrase]
(let [seed-phrase (security/safe-unmask-data masked-seed-phrase)]
{:db (update db
:multiaccounts/key-storage assoc
:seed-phrase (when seed-phrase
(string/lower-case seed-phrase))
:seed-shape-invalid? (or (empty? seed-phrase)
(not (mnemonic/valid-length? seed-phrase)))
:seed-word-count (mnemonic/words-count seed-phrase))}))
(rf/defn key-uid-seed-mismatch
{:events [::show-seed-key-uid-mismatch-error-popup]}
[cofx _]
(popover/show-popover cofx {:view :seed-key-uid-mismatch}))
(rf/defn key-uid-matches
{:events [::key-uid-matches]}
[{:keys [db] :as cofx} _]
(let [backup? (get-in db [:keycard :creating-backup?])]
(if backup?
(keycard.backup/start-keycard-backup cofx)
(navigation/navigate-to cofx :storage nil))))
(defn validate-seed-against-key-uid
"Check if the key-uid was generated with the given seed-phrase"
[{:keys [import-mnemonic-fn on-success on-error]} {:keys [seed-phrase key-uid]}]
(import-mnemonic-fn
(mnemonic/sanitize-passphrase seed-phrase)
nil
(fn [result]
(let [{:keys [keyUid]} (types/json->clj result)]
;; if the key-uid from app-db is same as the one returned by multiaccount import,
;; it means that this seed was used to generate this multiaccount
(if (= key-uid keyUid)
(on-success)
(on-error))))))
(re-frame/reg-fx
:key-storage/delete-imported-key
::validate-seed-against-key-uid
(partial validate-seed-against-key-uid
{:import-mnemonic-fn native-module/multiaccount-import-mnemonic
:on-success #(re-frame/dispatch [::key-uid-matches])
:on-error #(re-frame/dispatch [::show-seed-key-uid-mismatch-error-popup])}))
(rf/defn seed-phrase-validated
{:events [::seed-phrase-validated]}
[{:keys [db] :as cofx} validation-error]
(let [error? (-> validation-error
types/json->clj
:error
string/blank?
not)
onboarding? (not (or (:multiaccounts/login db) (:multiaccount db)))]
(if error?
(popover/show-popover cofx {:view :custom-seed-phrase})
{::validate-seed-against-key-uid {:seed-phrase (-> db :multiaccounts/key-storage :seed-phrase)
;; Unique key-uid of the account for which we are going to move
;; keys
:key-uid (or (-> db :multiaccounts/login :key-uid)
(-> db :multiaccount :key-uid)
(and onboarding?
(-> db
:keycard
:application-info
:key-uid)))}})))
(rf/defn choose-storage-pressed
{:events [::choose-storage-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [seed-phrase]} (:multiaccounts/key-storage db)]
{::multiaccounts/validate-mnemonic
[(mnemonic/sanitize-passphrase seed-phrase)
#(re-frame/dispatch [::seed-phrase-validated %])]}))
(rf/defn keycard-storage-pressed
{:events [::keycard-storage-pressed]}
[{:keys [db]} selected?]
{:db (assoc-in db [:multiaccounts/key-storage :keycard-storage-selected?] selected?)})
(re-frame/reg-fx
::delete-imported-key
(fn [{:keys [key-uid address password on-success on-error]}]
(let [hashed-pass (ethereum/sha3 (security/safe-unmask-data password))]
(native-module/delete-imported-key
@@ -19,3 +156,132 @@
(if-not (string/blank? error)
(on-error error)
(on-success))))))))
#_"Multiaccount has been deleted from device. We now need to emulate the restore seed phrase process, and make the user land on Keycard setup screen.
To ensure that keycard setup works, we need to:
1. Import multiaccount, derive required keys and save them at the correct location in app-db
2. Take the user to :keycard-onboarding-intro screen in :intro-login-stack
The exact events dispatched for this flow if consumed from the UI are:
:m.r/enter-phrase-input-changed
:m.r/enter-phrase-next-pressed
:m.r/re-encrypt-pressed
:i/on-key-storage-selected ([:intro-wizard :selected-storage-type] is set to :advanced)
:m.r/select-storage-next-pressed
We don't need to take the exact steps, just set the required state and redirect to correct screen
"
(rf/defn import-multiaccount
[{:keys [db] :as cofx}]
{:dispatch [:bottom-sheet/hide-old]
::multiaccounts.recover/import-multiaccount
{:passphrase (get-in db [:multiaccounts/key-storage :seed-phrase])
:password nil
:success-event ::import-multiaccount-success}})
(rf/defn delete-multiaccount-and-init-keycard-onboarding
{:events [::delete-multiaccount-and-init-keycard-onboarding]}
[{:keys [db] :as cofx}]
(rf/merge
{:dispatch [:bottom-sheet/hide-old]
:db (assoc-in db [:multiaccounts/key-storage :reset-db-checked?] true)}
(import-multiaccount)))
(rf/defn storage-selected
{:events [::storage-selected]}
[{:keys [db] :as cofx}]
(if (get-in db [:multiaccounts/key-storage :reset-db-checked?])
(popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning})
(bottom-sheet/hide-bottom-sheet-old cofx)))
(rf/defn skip-password-pressed
{:events [::skip-password-pressed]}
[cofx]
(popover/show-popover cofx {:view :transfer-multiaccount-to-keycard-warning}))
(rf/defn password-changed
{:events [::password-changed]}
[{db :db} password]
(let [unmasked-pass (security/safe-unmask-data password)]
{:db (update db
:keycard assoc
:migration-password password
:migration-password-error nil
:migration-password-valid? (and unmasked-pass (> (count unmasked-pass) 5)))}))
(rf/defn verify-password-result
{:events [::verify-password-result]}
[{:keys [db] :as cofx} result]
(let [{:keys [error]} (types/json->clj result)]
(if (string/blank? error)
(rf/merge
cofx
{:db (update db :keycard dissoc :migration-password-error :migration-password-valid?)}
(import-multiaccount))
{:db (assoc-in db [:keycard :migration-password-error] (i18n/label :t/wrong-password))})))
(rf/defn verify-password
{:events [::verify-password]}
[{:keys [db] :as cofx}]
(native-module/verify-database-password
(get-in db [:multiaccounts/login :key-uid])
(ethereum/sha3 (security/safe-unmask-data (get-in db [:keycard :migration-password])))
#(re-frame/dispatch [::verify-password-result %])))
(rf/defn handle-multiaccount-import
{:events [::import-multiaccount-success]}
[{:keys [db] :as cofx} root-data derived-data]
(rf/merge cofx
{:db (-> db
(update :intro-wizard
assoc
:root-key root-data
:derived derived-data
:recovering? true
:selected-storage-type :advanced)
(assoc-in [:keycard :flow] :recovery)
(assoc-in [:keycard :from-key-storage-and-migration?] true)
(assoc-in [:keycard :converting-account?]
(not (get-in db [:multiaccounts/key-storage :reset-db-checked?])))
(assoc-in [:keycard :delete-account?]
(true? (get-in db [:multiaccounts/key-storage :reset-db-checked?])))
(dissoc :multiaccounts/key-storage))}
(popover/hide-popover)
(common/listen-to-hardware-back-button)
(navigation/navigate-to :keycard-onboarding-intro nil)))
(rf/defn goto-multiaccounts-screen
{:events [::hide-popover-and-goto-multiaccounts-screen]}
[cofx _]
(rf/merge cofx
(popover/hide-popover)
(navigation/navigate-to :multiaccounts nil)))
(rf/defn confirm-logout-and-goto-key-storage
{:events [::confirm-logout-and-goto-key-storage]}
[{:keys [db] :as cofx}]
(rf/merge cofx
{:db (assoc db :goto-key-storage? true)}
(multiaccounts.logout/logout)))
(rf/defn logout-and-goto-key-storage
{:events [::logout-and-goto-key-storage]}
[_]
{:ui/show-confirmation
{:title (i18n/label :t/logout-title)
:content (i18n/label :t/logout-key-management)
:confirm-button-text (i18n/label :t/logout)
:on-accept #(re-frame/dispatch [::confirm-logout-and-goto-key-storage])
:on-cancel nil}})
(comment
;; check import mnemonic output
(native-module/multiaccount-import-mnemonic
"rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"
nil
(fn [result]
(prn (types/json->clj result))))
;; check delete account output
(native-module/delete-multiaccount "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7"
(fn [result]
(prn ::--delete-account-res-> result))))
@@ -0,0 +1,67 @@
(ns status-im.multiaccounts.key-storage.core-test
(:require [cljs.test :refer-macros [deftest is testing]]
[clojure.string :as string]
[status-im.multiaccounts.key-storage.core :as models]
[utils.security.core :as security]))
(deftest move-keystore-checked
(testing "Checks checkbox on-press"
(let [res (models/move-keystore-checked {:db {}} true)]
(is (= true (get-in res [:db :multiaccounts/key-storage :move-keystore-checked?]))))))
(deftest seed-phrase-input-changed
(testing "nil seed phrase shape is invalid"
(let [res (models/seed-phrase-input-changed {:db {}} (security/mask-data nil))]
(is (get-in res [:db :multiaccounts/key-storage :seed-shape-invalid?]))))
(let [sample-phrase "h h h h h h h h h h h H" ;; 12 characters
res (models/seed-phrase-input-changed {:db {}} (security/mask-data sample-phrase))]
(testing "Seed shape for 12 letter seed phrase is valid"
(is (false? (get-in res [:db :multiaccounts/key-storage :seed-shape-invalid?]))))
(testing "Seed words counted correctly"
(is (= 12 (get-in res [:db :multiaccounts/key-storage :seed-word-count]))))
(testing "Seed phrase is lowercased"
(is (= (get-in res [:db :multiaccounts/key-storage :seed-phrase])
(string/lower-case sample-phrase))))))
(def seed-key-uid-pair
{:seed-phrase "rocket mixed rebel affair umbrella legal resemble scene virus park deposit cargo"
:key-uid "0x3831d0f22996a65970a214f0a94bfa9a63a21dac235d8dadb91be8e32e7d3ab7"})
(defn mock-import-mnemonic-fn
[_ _ _]
;; return json with keyUid, the real world will have more info in the response
(str "{\"keyUid\": \"" (:key-uid seed-key-uid-pair) "\"}"))
(deftest validate-seed-against-key-uid
(testing "Success event is triggered if correct seed is entered for selected multiaccount (key-uid)"
(models/validate-seed-against-key-uid
{:import-mnemonic-fn mock-import-mnemonic-fn
:on-success #(is true) ; this callback should be called
:on-error #(is false)}
{:seed-phrase (:seed-phrase seed-key-uid-pair)
:key-uid (:key-uid seed-key-uid-pair)}))
(testing "Error event is triggered if incorrect seed is entered for selected multiaccount"
(models/validate-seed-against-key-uid
{:import-mnemonic-fn mock-import-mnemonic-fn
:on-success #(is false)
:on-error #(is true)}
{:seed-phrase (:seed-phrase seed-key-uid-pair)
:key-uid "0xInvalid-Will-make-the-function-fail"})))
(deftest handle-multiaccount-import
(testing "Sets correct state for Keycard onboarding after multiaccounts seeds are derived"
(let [res (models/handle-multiaccount-import {:db {}} :passed-root-data :passed-derived-data)]
(is (= :passed-root-data (get-in res [:db :intro-wizard :root-key])))
(is (= :passed-derived-data (get-in res [:db :intro-wizard :derived])))
(is (= :advanced (get-in res [:db :intro-wizard :selected-storage-type]))) ; :advanced storage type
; means Keycard
(is (= :recovery (get-in res [:db :keycard :flow])))
(is (get-in res [:db :keycard :from-key-storage-and-migration?])))))
(comment
(security/safe-unmask-data (security/mask-data nil)))
+78 -39
View File
@@ -16,6 +16,7 @@
[status-im.ethereum.transactions.core :as transactions]
[status-im.fleet.core :as fleet]
[utils.i18n :as i18n]
[status-im.keycard.common :as keycard.common]
[status-im.mobile-sync-settings.core :as mobile-network]
[status-im.multiaccounts.biometric.core :as biometric]
[status-im.multiaccounts.core :as multiaccounts]
@@ -36,7 +37,7 @@
[status-im.wallet.core :as wallet]
[status-im.wallet.prices :as prices]
[status-im2.common.json-rpc.events :as json-rpc]
[status-im2.contexts.shell.activity-center.events :as activity-center]
[status-im2.contexts.activity-center.events :as activity-center]
[status-im2.contexts.chat.messages.link-preview.events :as link-preview]
[status-im2.contexts.contacts.events :as contacts]
[status-im2.navigation.events :as navigation]
@@ -45,8 +46,7 @@
[status-im2.common.log :as logging]
[taoensso.timbre :as log]
[status-im2.contexts.shell.jump-to.utils :as shell.utils]
[utils.security.core :as security]
[status-im.keycard.common :as keycard.common]))
[utils.security.core :as security]))
(re-frame/reg-fx
::initialize-transactions-management-enabled
@@ -146,7 +146,7 @@
(rf/merge
cofx
{:db (assoc db
:profile/wallet-accounts
:multiaccount/accounts
(rpc->accounts accounts))
;; NOTE: Local notifications should be enabled only after wallet was started
::enable-local-notifications nil}
@@ -179,7 +179,7 @@
(rf/defn login-local-paired-user
{:events [:multiaccounts.login/local-paired-user]}
[{:keys [db]}]
(let [{:keys [key-uid name password]} (get-in db [:syncing :profile/profile])]
(let [{:keys [key-uid name password]} (get-in db [:syncing :multiaccount])]
{::login [key-uid
(types/clj->json {:name name
:key-uid key-uid})
@@ -188,9 +188,9 @@
(rf/defn login
{:events [:multiaccounts.login.ui/password-input-submitted]}
[{:keys [db]}]
(let [{:keys [key-uid password name]} (:profile/login db)]
(let [{:keys [key-uid password name]} (:multiaccounts/login db)]
{:db (-> db
(assoc-in [:profile/login :processing] true)
(assoc-in [:multiaccounts/login :processing] true)
(dissoc :intro-wizard :recovered-account?)
(update :keycard dissoc :flow))
::login [key-uid
@@ -201,7 +201,7 @@
(rf/defn export-db-submitted
{:events [:multiaccounts.login.ui/export-db-submitted]}
[{:keys [db]}]
(let [{:keys [key-uid password name]} (:profile/login db)]
(let [{:keys [key-uid password name]} (:multiaccounts/login db)]
{::export-db [key-uid
(types/clj->json {:name name
:key-uid key-uid})
@@ -216,7 +216,7 @@
(rf/defn import-db-submitted
{:events [:multiaccounts.login.ui/import-db-submitted]}
[{:keys [db]}]
(let [{:keys [key-uid password name]} (:profile/login db)]
(let [{:keys [key-uid password name]} (:multiaccounts/login db)]
{::import-db [key-uid
(types/clj->json {:name name
:key-uid key-uid})
@@ -394,7 +394,7 @@
[{:keys [db] :as cofx} node-config-json]
(let [node-config (types/json->clj node-config-json)]
{:db (assoc-in db
[:profile/profile :wakuv2-config]
[:multiaccount :wakuv2-config]
(get node-config :WakuV2Config))}))
(rf/defn get-node-config
@@ -438,10 +438,10 @@
merged-networks (merge networks config/default-networks-by-id)]
(rf/merge cofx
{:db (-> db
(dissoc :profile/login)
(dissoc :multiaccounts/login)
(assoc :networks/current-network current-network
:networks/networks merged-networks
:profile/profile multiaccount))}
:multiaccount multiaccount))}
(data-store.chats/fetch-chats-rpc
{:on-success
#(do (re-frame/dispatch [:chats-list/load-success %])
@@ -476,29 +476,29 @@
(rf/defn check-last-chat
{:events [::check-last-chat]}
[{:keys [db]}]
{::open-last-chat (get-in db [:profile/profile :key-uid])})
{::open-last-chat (get-in db [:multiaccount :key-uid])})
(rf/defn update-wallet-accounts
[{:keys [db]} accounts]
(let [existing-accounts (into {} (map #(vector (:address %1) %1) (:profile/wallet-accounts db)))
(let [existing-accounts (into {} (map #(vector (:address %1) %1) (:multiaccount/accounts db)))
reduce-fn (fn [existing-accs new-acc]
(let [address (:address new-acc)]
(if (:removed new-acc)
(dissoc existing-accs address)
(assoc existing-accs address new-acc))))
new-accounts (reduce reduce-fn existing-accounts (rpc->accounts accounts))]
{:db (assoc db :profile/wallet-accounts (vals new-accounts))}))
{:db (assoc db :multiaccount/accounts (vals new-accounts))}))
(rf/defn get-chats-callback
{:events [::get-chats-callback]}
[{:keys [db] :as cofx}]
(let [{:networks/keys [current-network networks]} db
notifications-enabled? (get-in db [:profile/profile :notifications-enabled?])
notifications-enabled? (get-in db [:multiaccount :notifications-enabled?])
current-network-config (get networks current-network)
network-id (str (get-in networks
[current-network :config :NetworkId]))
remote-push-notifications-enabled?
(get-in db [:profile/profile :remote-push-notifications-enabled?])]
(get-in db [:multiaccount :remote-push-notifications-enabled?])]
(rf/merge cofx
(cond-> {::eip1559/check-eip1559-activation
{:network-id network-id
@@ -510,7 +510,7 @@
(fn [accounts tokens custom-tokens favourites]
(re-frame/dispatch [::initialize-wallet
accounts tokens custom-tokens favourites]))]
::open-last-chat (get-in db [:profile/profile :key-uid])}
::open-last-chat (get-in db [:multiaccount :key-uid])}
(or notifications-enabled? remote-push-notifications-enabled?)
(assoc ::notifications/enable remote-push-notifications-enabled?))
(transport/start-messenger)
@@ -555,21 +555,23 @@
(rf/defn create-only-events
[{:keys [db] :as cofx} recovered-account?]
(let [{:profile/keys [profile profiles-overview wallet-accounts]}
(let [{:keys [multiaccount
:multiaccounts/multiaccounts
:multiaccount/accounts]}
db
{:keys [creating?]} (:profile/login db)
first-account? (and creating? (empty? profiles-overview))
{:keys [creating?]} (:multiaccounts/login db)
first-account? (and creating?
(empty? multiaccounts))
tos-accepted? (get db :tos/accepted?)
{:networks/keys [current-network networks]} db
network-id (str (get-in networks [current-network :config :NetworkId]))]
(shell.utils/change-selected-stack-id :communities-stack true nil)
(rf/merge cofx
{:db (-> db
(dissoc :profile/login)
(dissoc :multiaccounts/login)
(assoc :tos/next-root :enable-notifications :chats/loading? false)
(assoc-in [:profile/profile :multiaccounts/first-account]
first-account?))
::get-tokens [network-id wallet-accounts recovered-account?]}
(assoc-in [:multiaccount :multiaccounts/first-account] first-account?))
::get-tokens [network-id accounts recovered-account?]}
(finish-keycard-setup)
(transport/start-messenger)
(communities/fetch)
@@ -577,7 +579,7 @@
{:on-success #(re-frame/dispatch [:chats-list/load-success %])})
(multiaccounts/switch-preview-privacy-mode-flag)
(link-preview/request-link-preview-whitelist)
(logging/set-log-level (:log-level profile))
(logging/set-log-level (:log-level multiaccount))
(navigation/init-root :enable-notifications))))
(defn- keycard-setup?
@@ -593,7 +595,7 @@
:on-card-read
:card-read-in-progress?
:pin
:profile/profile)
:multiaccount)
(assoc :tos-accept-next-root
(if login-only?
:shell-stack
@@ -604,9 +606,9 @@
(rf/defn multiaccount-login-success
[{:keys [db now] :as cofx}]
(let [{:keys [key-uid password save-password? creating?]}
(:profile/login db)
(:multiaccounts/login db)
multiaccounts (:profile/profiles-overview db)
multiaccounts (:multiaccounts/multiaccounts db)
recovered-account? (get db :recovered-account?)
login-only? (not (or creating?
recovered-account?
@@ -636,12 +638,29 @@
(login-only-events key-uid password save-password?)
(create-only-events recovered-account?)))))
;; FIXME(Ferossgp): We should not copy keys as we denormalize the database,
;; this create desync between actual accounts and the one on login causing broken state
;; UPDATE(cammellos): This code is copying over some fields explicitly as some values
;; are alreayd in `multiaccounts/login` and should not be overriden, as they come from
;; the keychain (save-password), this is not very explicit and we should probably
;; make it clearer
(rf/defn open-login
[{:keys [db]} override-multiaccount]
{:db (-> db
(update :multiaccounts/login
merge
override-multiaccount)
(update :multiaccounts/login
dissoc
:error
:password))})
(rf/defn open-login-callback
{:events [:multiaccounts.login.callback/get-user-password-success]}
[{:keys [db] :as cofx} password]
(let [key-uid (get-in db [:profile/login :key-uid])
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
keycard-account? (boolean (get-in db
[:profile/profiles-overview
[:multiaccounts/multiaccounts
key-uid
:keycard-pairing]))
goto-key-storage? (:goto-key-storage? db)]
@@ -649,7 +668,7 @@
(rf/merge
cofx
{:db (update-in db
[:profile/login]
[:multiaccounts/login]
assoc
:password password
:save-password? true)
@@ -671,7 +690,7 @@
(rf/defn get-credentials
[{:keys [db] :as cofx} key-uid]
(let [keycard-multiaccount? (boolean (get-in db
[:profile/profiles-overview key-uid :keycard-pairing]))]
[:multiaccounts/multiaccounts key-uid :keycard-pairing]))]
(log/debug "[login] get-credentials"
"keycard-multiacc?"
keycard-multiaccount?)
@@ -683,8 +702,12 @@
"Auth method: nil - not supported, \"none\" - not selected, \"password\", \"biometric\", \"biometric-prepare\""
{:events [:multiaccounts.login/get-auth-method-success]}
[{:keys [db] :as cofx} auth-method]
(let [key-uid (get-in db [:profile/login :key-uid])
keycard-profile? (boolean (get-in db [:profile/profiles-overview key-uid :keycard-pairing]))]
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
keycard-multiaccount? (boolean (get-in db
[:multiaccounts/multiaccounts key-uid :keycard-pairing]))]
(log/debug "[login] get-auth-method-success"
"auth-method" auth-method
"keycard-multiacc?" keycard-multiaccount?)
(rf/merge
cofx
{:db (assoc db :auth-method auth-method)}
@@ -693,14 +716,15 @@
(biometric/biometric-auth %)
(= auth-method keychain/auth-method-password)
(get-credentials % key-uid)
(and keycard-profile? (get-in db [:keycard :card-connected?]))
(and keycard-multiaccount?
(get-in db [:keycard :card-connected?]))
(keycard.common/get-application-info % nil))
(open-login-callback nil))))
(rf/defn biometric-auth-done
{:events [:biometric-auth-done]}
[{:keys [db] :as cofx} {:keys [bioauth-success bioauth-message bioauth-code]}]
(let [key-uid (get-in db [:profile/login :key-uid])
(let [key-uid (get-in db [:multiaccounts/login :key-uid])
auth-method (get db :auth-method)]
(log/debug "[biometric] biometric-auth-done"
"bioauth-success" bioauth-success
@@ -710,7 +734,7 @@
(get-credentials cofx key-uid)
(rf/merge cofx
{:db (assoc-in db
[:profile/login :save-password?]
[:multiaccounts/login :save-password?]
(= auth-method keychain/auth-method-biometric))}
(when-not (= auth-method keychain/auth-method-biometric)
(keychain/save-auth-method key-uid keychain/auth-method-none))
@@ -737,7 +761,7 @@
(and (not save-password?)
bioauth-supported?
(= previous-auth-method keychain/auth-method-none)))
(assoc-in [:profile/login :save-password?] save-password?))}
(assoc-in [:multiaccounts/login :save-password?] save-password?))}
(when bioauth-supported?
(if save-password?
(popover/show-popover {:view :secure-with-biometric})
@@ -749,6 +773,21 @@
[_]
{:set-root :shell-stack})
(rf/defn multiaccount-selected
{:events [:multiaccounts.login.ui/multiaccount-selected]}
[{:keys [db] :as cofx} key-uid]
;; We specifically pass a bunch of fields instead of the whole multiaccount
;; as we want store some fields in multiaccount that are not here
(let [multiaccount (get-in db [:multiaccounts/multiaccounts key-uid])
keycard-multiaccount? (boolean (:keycard-pairing multiaccount))]
(rf/merge
cofx
(merge
{:db (update db :keycard dissoc :application-info)}
(when keycard-multiaccount? {:navigate-to :keycard-login-pin}))
(open-login (select-keys multiaccount
[:key-uid :name :public-key :images :customization-color])))))
(rf/defn hide-keycard-banner
{:events [:hide-keycard-banner]}
[{:keys [db]}]
@@ -11,16 +11,16 @@
:supported-biometric-auth false}}
{:keys [db]} (login/save-password initial-cofx true)]
(test/is (= false (contains? db :popover/popover)))
(test/is (= true (get-in db [:profile/login :save-password?])))
(test/is (= true (get-in db [:multiaccounts/login :save-password?])))
(test/testing "uncheck save password"
(let [{:keys [db]} (login/save-password {:db db} false)]
(test/is (= false (contains? db :popover/popover)))
(test/is (= false (get-in db [:profile/login :save-password?])))))))
(test/is (= false (get-in db [:multiaccounts/login :save-password?])))))))
(test/testing "check save password, biometric available"
(let [initial-cofx {:db {:auth-method keychain/auth-method-none
:supported-biometric-auth true}}
{:keys [db]} (login/save-password initial-cofx true)]
(test/is (= true (get-in db [:profile/login :save-password?])))
(test/is (= true (get-in db [:multiaccounts/login :save-password?])))
(test/testing "enable biometric auth"
(let [{:keys [db] :as res} (biometric/enable {:db db})]
(test/is (contains? res :biometric-auth/authenticate))
@@ -53,10 +53,10 @@
:bioauth-message nil
:bioauth-code nil}))
false)]
(test/is (= true (get-in db [:profile/login :save-password?])))
(test/is (= true (get-in db [:multiaccounts/login :save-password?])))
;; case 2 from https://github.com/status-im/status-mobile/issues/9573
(test/is (= keychain/auth-method-biometric-prepare (:auth-method db)))
(test/testing "disable biometric"
(let [{:keys [db]} (biometric/disable {:db db})]
(test/is (= false (get-in db [:profile/login :save-password?])))
(test/is (= false (get-in db [:multiaccounts/login :save-password?])))
(test/is (= keychain/auth-method-none (:auth-method db))))))))
@@ -10,28 +10,28 @@
(deftest on-password-input-submitted
(testing
"handling :multiaccounts.login.ui/password-input-submitted event"
(let [cofx {:db {:profile/login {:key-uid "key-uid"
:password "password"
:name "user"}}}
(let [cofx {:db {:multiaccounts/login {:key-uid "key-uid"
:password "password"
:name "user"}}}
efx (login.core/login cofx)]
(testing "Change multiaccount."
(is (= (::login.core/login efx)
["key-uid" "{\"name\":\"user\",\"key-uid\":\"key-uid\"}"
(ethereum/sha3 "password")])))
(testing "start activity indicator"
(is (= (get-in efx [:db :profile/login :processing]) true))))))
(is (= (get-in efx [:db :multiaccounts/login :processing]) true))))))
(deftest login-success
(testing ":accounts.login.callback/login-success event received."
(let [db {:profile/login {:address "address"
:password "password"}
:profile/profile data/multiaccount}
(let [db {:multiaccounts/login {:address "address"
:password "password"}
:multiaccount data/multiaccount}
cofx {:db db}
efx (login.core/multiaccount-login-success cofx)
json-rpc (into #{} (map :method (:json-rpc/call efx)))]
;; TODO: Account is now cleared only after all sign in fx are executed.
;; (testing ":accounts/login cleared."
;; (is (not (contains? new-db :profile/login))))
;; (is (not (contains? new-db :multiaccounts/login))))
(testing "Check the rest of effects."
(is (json-rpc "web3_clientVersion"))))))
@@ -44,7 +44,7 @@
"04f43da85ff1c333f3e7277b9ac4df92c9120fbb251f1dede7d41286e8c055acfeb845f6d2654821afca25da119daff9043530b296ee0e28e202ba92ec5842d617"
db
{:keycard
{:profile/profile
{:multiaccount
{:encryption-public-key epk
:whisper-private-key wpk
:wallet-address "83278851e290d2488b6add2a257259f5741a3b7d"
+4 -5
View File
@@ -12,7 +12,7 @@
(rf/defn logout-method
{:events [::logout-method]}
[{:keys [db] :as cofx} {:keys [auth-method logout?]}]
(let [key-uid (get-in db [:profile/profile :key-uid])]
(let [key-uid (get-in db [:multiaccount :key-uid])]
(rf/merge cofx
{:set-root :progress
:chat.ui/clear-inputs nil
@@ -21,9 +21,8 @@
::logout nil
::multiaccounts/webview-debug-changed false
:keychain/clear-user-password key-uid
:profile/get-profiles-overview #(re-frame/dispatch
[:profile/get-profiles-overview-success
%])}
:setup/open-multiaccounts #(re-frame/dispatch [:setup/initialize-multiaccounts
% {:logout? logout?}])}
(keychain/save-auth-method key-uid auth-method)
(wallet/clear-timeouts)
(init/initialize-app-db))))
@@ -57,7 +56,7 @@
(logout-method {:auth-method keychain/auth-method-biometric-prepare
:logout? false})
(fn [{:keys [db]}]
{:db (assoc-in db [:profile/login :save-password?] true)})))
{:db (assoc-in db [:multiaccounts/login :save-password?] true)})))
(re-frame/reg-fx
::logout
+10 -2
View File
@@ -1,5 +1,13 @@
(ns status-im.multiaccounts.model)
(defn logged-in?
[{:keys [profile/profile]}]
(boolean profile))
[{:keys [multiaccount]}]
(boolean multiaccount))
(defn credentials
[cofx]
(select-keys (get-in cofx [:db :multiaccounts/login]) [:key-uid :password :save-password?]))
(defn current-public-key
[cofx]
(get-in cofx [:db :multiaccount :public-key]))
+1 -1
View File
@@ -4,6 +4,6 @@
(deftest logged-in-test
(testing "multiaccount is defined"
(is (multiaccounts.model/logged-in? {:profile/profile {}})))
(is (multiaccounts.model/logged-in? {:multiaccount {}})))
(testing "multiaccount is not there"
(is (not (multiaccounts.model/logged-in? {})))))
+115 -9
View File
@@ -6,12 +6,14 @@
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.mnemonic :as mnemonic]
[utils.i18n :as i18n]
[status-im.keycard.nfc :as nfc]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.multiaccounts.create.core :as multiaccounts.create]
[native-module.core :as native-module]
[status-im.popover.core :as popover]
[utils.re-frame :as rf]
[status-im.utils.types :as types]
[status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]
[taoensso.timbre :as log]
[utils.security.core :as security]))
@@ -29,12 +31,13 @@
{:events [:multiaccounts.recover/passphrase-input-changed]}
[{:keys [db]} masked-recovery-phrase]
(let [recovery-phrase (security/safe-unmask-data masked-recovery-phrase)]
{:db (update db
:intro-wizard assoc
:passphrase (string/lower-case recovery-phrase)
:passphrase-error nil
:next-button-disabled? (or (empty? recovery-phrase)
(not (mnemonic/valid-length? recovery-phrase))))}))
(rf/merge
{:db (update db
:intro-wizard assoc
:passphrase (string/lower-case recovery-phrase)
:passphrase-error nil
:next-button-disabled? (or (empty? recovery-phrase)
(not (mnemonic/valid-length? recovery-phrase))))})))
(rf/defn validate-phrase-for-warnings
[{:keys [db]}]
@@ -55,7 +58,7 @@
:on-dismiss #(re-frame/dispatch [:pop-to-root :multiaccounts])}}
(let [{:keys [key-uid] :as multiaccount} (get-in db [:intro-wizard :root-key])
keycard-multiaccount? (boolean (get-in db
[:profile/profiles-overview key-uid
[:multiaccounts/multiaccounts key-uid
:keycard-pairing]))]
(if keycard-multiaccount?
;; trying to recover multiaccount created with keycard
@@ -119,13 +122,13 @@
:on-accept #(do
(re-frame/dispatch [:pop-to-root :multiaccounts])
(re-frame/dispatch
[:profile/profile-selected key-uid]))
[:multiaccounts.login.ui/multiaccount-selected key-uid]))
:on-cancel #(re-frame/dispatch [:pop-to-root :multiaccounts])}})
(rf/defn on-import-multiaccount-success
{:events [::import-multiaccount-success]}
[{:keys [db] :as cofx} {:keys [key-uid] :as root-data} derived-data]
(let [multiaccounts (:profile/profiles-overview db)]
(let [multiaccounts (:multiaccounts/multiaccounts db)]
(rf/merge
cofx
{:db (update db
@@ -175,3 +178,106 @@
{::multiaccounts/validate-mnemonic [passphrase
#(re-frame/dispatch [:multiaccounts.recover/phrase-validated
%])]}))
(rf/defn continue-to-import-mnemonic
{:events [::continue-pressed]}
[{:keys [db] :as cofx}]
(let [{:keys [password passphrase]} (:multiaccounts/recover db)]
(rf/merge cofx
{::import-multiaccount {:passphrase passphrase
:password password
:success-event ::import-multiaccount-success}}
(popover/hide-popover))))
(rf/defn dec-step
{:events [:multiaccounts.recover/dec-step]}
[{:keys [db] :as cofx}]
(let [step (get-in db [:intro-wizard :step])]
(if (= step :enter-phrase)
{:db (dissoc db :intro-wizard)}
{:db (update db
:intro-wizard assoc
:step
(case step
:recovery-success :enter-phrase
:select-key-storage :recovery-success
:create-code :select-key-storage)
:confirm-failure? false
:key-code nil)})))
(rf/defn cancel-pressed
{:events [:multiaccounts.recover/cancel-pressed]}
[{:keys [db] :as cofx} skip-alert?]
;; Workaround for multiple Cancel button clicks
;; that can break navigation tree
(let [step (get-in db [:intro-wizard :step])]
(when-not (#{:multiaccounts :login} (:view-id db))
(if (and (= step :select-key-storage) (not skip-alert?))
(utils/show-question
(i18n/label :t/are-you-sure-to-cancel)
(i18n/label :t/you-will-start-from-scratch)
#(re-frame/dispatch [:multiaccounts.recover/cancel-pressed true]))
(rf/merge cofx
dec-step
navigation/navigate-back)))))
(rf/defn select-storage-next-pressed
{:events [:multiaccounts.recover/select-storage-next-pressed]
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
[{:keys [db] :as cofx}]
(let [storage-type (get-in db [:intro-wizard :selected-storage-type])]
(if (= storage-type :advanced)
;;TODO: fix circular dependency to remove dispatch here
{:dispatch [:recovery.ui/keycard-option-pressed]}
(rf/merge cofx
{:db (update db :intro-wizard assoc :step :create-code)}
(navigation/navigate-to :create-password nil)))))
(rf/defn re-encrypt-pressed
{:events [:multiaccounts.recover/re-encrypt-pressed]}
[{:keys [db] :as cofx}]
(rf/merge cofx
{:db (update db
:intro-wizard
assoc
:step :select-key-storage
:selected-storage-type :default)}
(if (nfc/nfc-supported?)
(navigation/navigate-to :select-key-storage nil)
(select-storage-next-pressed))))
(rf/defn confirm-password-next-button-pressed
{:events [:multiaccounts.recover/enter-password-next-pressed]
:interceptors [(re-frame/inject-cofx :random-guid-generator)]}
[cofx key-code]
(store-multiaccount cofx key-code))
(rf/defn count-words
[{:keys [db]}]
(let [passphrase (get-in db [:intro-wizard :passphrase])]
{:db (assoc-in db
[:intro-wizard :passphrase-word-count]
(mnemonic/words-count passphrase))}))
(rf/defn run-validation
[{:keys [db] :as cofx}]
(let [passphrase (get-in db [:intro-wizard :passphrase])]
(when (= (last passphrase) " ")
(rf/merge cofx
(validate-phrase-for-warnings)))))
(rf/defn enter-phrase-input-changed
{:events [:multiaccounts.recover/enter-phrase-input-changed]}
[cofx input]
(rf/merge cofx
(set-phrase input)
(count-words)
(run-validation)))
(rf/defn enter-passphrase-input-changed
{:events [:multiaccounts.recover/enter-passphrase-input-changed]}
[{:keys [db]} masked-passphrase]
{:db (update db
:intro-wizard assoc
:password masked-passphrase
:password-error nil)})
@@ -73,13 +73,13 @@
(deftest on-import-multiaccount-success
(testing "importing a new multiaccount"
(let [res (models/on-import-multiaccount-success
{:db {:profile/profiles-overview {:acc1 {}}}}
{:db {:multiaccounts/multiaccounts {:acc1 {}}}}
{:key-uid :acc2}
nil)]
(is (nil? (:utils/show-confirmation res)))))
(testing "importing an existing multiaccount"
(let [res (models/on-import-multiaccount-success
{:db {:profile/profiles-overview {:acc1 {}}}}
{:db {:multiaccounts/multiaccounts {:acc1 {}}}}
{:key-uid :acc1}
nil)]
(is (contains? res :utils/show-confirmation)))))
@@ -35,7 +35,7 @@
(rf/defn password-reset-success
{:events [::password-reset-success]}
[{:keys [db] :as cofx}]
(let [{:keys [key-uid]} (:profile/profile db)
(let [{:keys [key-uid]} (:multiaccount db)
auth-method (get db :auth-method keychain/auth-method-none)
new-password (get-in db [:multiaccount/reset-password-form-vals :new-password])]
(rf/merge cofx
@@ -68,7 +68,7 @@
(rf/defn handle-verification-success
{:events [::handle-verification-success]}
[{:keys [db] :as cofx} form-vals]
(let [{:keys [key-uid name]} (:profile/profile db)]
(let [{:keys [key-uid name]} (:multiaccount db)]
(rf/merge cofx
{::change-db-password [key-uid form-vals]
:db (assoc db
@@ -98,4 +98,4 @@
{::validate-current-password-and-reset
(assoc form-vals
:address
(get-in db [:profile/profile :wallet-root-address]))})
(get-in db [:multiaccount :wallet-root-address]))})
+10 -10
View File
@@ -6,7 +6,7 @@
(rf/defn send-contact-update
[{:keys [db]}]
(let [{:keys [name preferred-name display-name address]} (:profile/profile db)]
(let [{:keys [name preferred-name display-name address]} (:multiaccount db)]
{:json-rpc/call [{:method "wakuext_sendContactUpdates"
:params [(or preferred-name display-name name) ""]
:on-success #(log/debug "sent contact update")}]}))
@@ -16,7 +16,7 @@
{:events [:multiaccounts.ui/update-name]}
[{:keys [db] :as cofx} raw-multiaccounts-from-status-go]
(let [{:keys [key-uid name preferred-name
display-name]} (:profile/profile db)
display-name]} (:multiaccount db)
account (some #(and (= (:key-uid %) key-uid) %) raw-multiaccounts-from-status-go)]
(when-let [new-name (and account (or preferred-name display-name name))]
(rf/merge cofx
@@ -30,7 +30,7 @@
[{:keys [db] :as cofx}
setting setting-value
{:keys [dont-sync? on-success] :or {on-success #()}}]
(let [current-multiaccount (:profile/profile db)]
(let [current-multiaccount (:multiaccount db)]
(if (empty? current-multiaccount)
;; NOTE: this should never happen, but if it does this is a critical error
;; and it is better to crash than risk having an unstable state
@@ -40,15 +40,15 @@
(rf/merge
cofx
{:db (if setting-value
(assoc-in db [:profile/profile setting] setting-value)
(update db :profile/profile dissoc setting))
(assoc-in db [:multiaccount setting] setting-value)
(update db :multiaccount dissoc setting))
:json-rpc/call
[{:method "settings_saveSetting"
:params [setting setting-value]
:on-success on-success}]}
(when (#{:name :preferred-name} setting)
(constantly {:profile/get-profiles-overview #(rf/dispatch [:multiaccounts.ui/update-name %])}))
(constantly {:setup/open-multiaccounts #(rf/dispatch [:multiaccounts.ui/update-name %])}))
(when (and (not dont-sync?) (#{:name :preferred-name} setting))
(send-contact-update))))))
@@ -73,7 +73,7 @@
(rf/defn optimistic
[{:keys [db] :as cofx} setting setting-value]
(let [current-multiaccount (:profile/profile db)
(let [current-multiaccount (:multiaccount db)
setting-value (if (= :currency setting)
(keyword setting-value)
setting-value)
@@ -97,8 +97,8 @@
(assoc db :stickers/recent-stickers recent-stickers-from-remote))
db)]
{:db (if setting-value
(assoc-in db [:profile/profile setting] setting-value)
(update db :profile/profile dissoc setting))}))
(assoc-in db [:multiaccount setting] setting-value)
(update db :multiaccount dissoc setting))}))
(rf/defn set-many-js
[cofx settings-js]
@@ -119,7 +119,7 @@
{:events [::toggle-opensea-nfts-visiblity]}
[cofx visible?]
(rf/merge cofx
{:db (assoc-in (:db cofx) [:profile/profile :opensea-enabled?] visible?)
{:db (assoc-in (:db cofx) [:multiaccount :opensea-enabled?] visible?)
;; need to add fully qualified namespace to counter circular deps
:dispatch [:status-im.wallet.core/fetch-collectibles-collection]}
(multiaccount-update :opensea-enabled? visible? {})))
@@ -6,27 +6,27 @@
;;TODO this test case actually shows that we are doing a needless rpc call when
;;there is no changes, but it is an edge case that shouldn't really happen
(let [efx (multiaccounts.update/multiaccount-update
{:db {:profile/profile {:not-empty "would throw an error if was empty"}}}
{:db {:multiaccount {:not-empty "would throw an error if was empty"}}}
nil
nil
{})
json-rpc (into #{} (map :method (:json-rpc/call efx)))]
(is (json-rpc "settings_saveSetting"))
(is (= (get-in efx [:db :profile/profile]) {:not-empty "would throw an error if was empty"}))))
(is (= (get-in efx [:db :multiaccount]) {:not-empty "would throw an error if was empty"}))))
(deftest test-clean-seed-phrase
(let [efx (multiaccounts.update/clean-seed-phrase
{:db {:profile/profile {:mnemonic "lalalala"}}}
{:db {:multiaccount {:mnemonic "lalalala"}}}
{})
json-rpc (into #{} (map :method (:json-rpc/call efx)))]
(is (json-rpc "settings_saveSetting"))
(is (nil? (get-in efx [:db :profile/profile :mnemonic])))))
(is (nil? (get-in efx [:db :multiaccount :mnemonic])))))
(deftest test-update-multiaccount-account-name
(let [cofx {:db {:profile/profile {:key-uid 1
:name "name"
:preferred-name "preferred-name"
:display-name "display-name"}}}
(let [cofx {:db {:multiaccount {:key-uid 1
:name "name"
:preferred-name "preferred-name"
:display-name "display-name"}}}
raw-multiaccounts-from-status-go [{:key-uid 1 :name "old-name"}]]
(testing "wrong account"
(is (nil? (multiaccounts.update/update-multiaccount-account-name cofx []))))
@@ -46,11 +46,11 @@
"preferred-name"))
(is (new-account-name=
(multiaccounts.update/update-multiaccount-account-name
(update-in cofx [:db :profile/profile] dissoc :preferred-name)
(update-in cofx [:db :multiaccount] dissoc :preferred-name)
raw-multiaccounts-from-status-go)
"display-name"))
(is (new-account-name=
(multiaccounts.update/update-multiaccount-account-name
(update-in cofx [:db :profile/profile] dissoc :preferred-name :display-name)
(update-in cofx [:db :multiaccount] dissoc :preferred-name :display-name)
raw-multiaccounts-from-status-go)
"name"))))))
+4 -4
View File
@@ -123,7 +123,7 @@
:db {:networks/manage {:url {:value "wrong"}
:chain {:value "1"}
:name {:value "empty"}}
:profile/profile {}}})))))
:multiaccount {}}})))))
(deftest save-valid-network
(testing "save a valid network"
@@ -133,7 +133,7 @@
:chain {:value :mainnet}
:symbol {:value "symbol"}
:name {:value "valid"}}
:profile/profile {}
:multiaccount {}
:networks/networks {"random2"
{:id "random2"
:name "network-name"
@@ -156,7 +156,7 @@
:chain {:value :custom}
:name {:value "valid"}
:network-id {:value 1}}
:profile/profile {}
:multiaccount {}
:networks/networks {"random"
{:id "random"
:name "network-name"
@@ -176,7 +176,7 @@
:name {:value "valid"}
:symbol {:value "symbol"}
:network-id {:value 5}}
:profile/profile {}
:multiaccount {}
:networks/networks {"randomid"
{:id "randomid"
:name "network-name"
+1 -1
View File
@@ -13,7 +13,7 @@
{:db (assoc db :network-status (if is-connected? :online :offline))}
(when (and is-connected?
(or (not= (count (get-in db [:wallet :accounts]))
(count (get db :profile/wallet-accounts)))
(count (get db :multiaccount/accounts)))
(wallet/has-empty-balances? db)))
(wallet/update-balances nil nil))))
+5 -5
View File
@@ -102,7 +102,7 @@
(defn current-fleet-key
[db]
(keyword (get-in db
[:profile/profile :fleet]
[:multiaccount :fleet]
config/fleet)))
(defn get-current-fleet
@@ -116,9 +116,9 @@
(some #(string/includes? (str %) "waku") ks)))
(defn get-multiaccount-node-config
[{:keys [profile/profile :networks/networks :networks/current-network]
[{:keys [multiaccount :networks/networks :networks/current-network]
:as db}]
(let [wakuv2-config (get profile :wakuv2-config {})
(let [wakuv2-config (get multiaccount :wakuv2-config {})
current-fleet-key (current-fleet-key db)
current-fleet (get-current-fleet db)
wakuv2-enabled (wakuv2-enabled? current-fleet)
@@ -127,7 +127,7 @@
{:keys [installation-id log-level
waku-bloom-filter-mode
custom-bootnodes custom-bootnodes-enabled?]}
profile
multiaccount
use-custom-bootnodes (get custom-bootnodes-enabled? current-network)]
(cond-> (get-in networks [current-network :config])
:always
@@ -212,7 +212,7 @@ app-db"
(rf/defn prepare-new-config
"Use this function to apply settings to the current account node config"
[{:keys [db]} {:keys [on-success]}]
(let [key-uid (get-in db [:profile/profile :key-uid])]
(let [key-uid (get-in db [:multiaccount :key-uid])]
{::prepare-new-config [key-uid
(get-new-config db)
#(re-frame/dispatch
+2 -2
View File
@@ -110,12 +110,12 @@
(= view-id :chat)))
(defn show-message-pn?
[{{:keys [app-state profile/profile]} :db :as cofx}
[{{:keys [app-state multiaccount]} :db :as cofx}
notification]
(let [chat-id (get-in notification [:body :chat :id])
notification-author (get-in notification [:notificationAuthor :id])]
(and
(not= notification-author (:public-key profile))
(not= notification-author (:public-key multiaccount))
(or (= app-state "background")
(not (foreground-chat? cofx chat-id))))))
+2 -2
View File
@@ -91,7 +91,7 @@
"Set the name of the device"
{:events [:pairing.ui/set-name-pressed]}
[{:keys [db]} installation-name]
(let [our-installation-id (get-in db [:profile/profile :installation-id])]
(let [our-installation-id (get-in db [:multiaccount :installation-id])]
{:pairing/set-installation-metadata [our-installation-id
{:name installation-name
:deviceType utils.platform/os}]}))
@@ -195,7 +195,7 @@
(rf/defn send-installation-messages
{:events [:pairing.ui/synchronize-installation-pressed]}
[{:keys [db]}]
(let [multiaccount (:profile/profile db)
(let [multiaccount (:multiaccount db)
{:keys [name preferred-name]} multiaccount]
{:json-rpc/call [{:method "wakuext_syncDevices"
:params [(or preferred-name name)]
+2 -2
View File
@@ -57,7 +57,7 @@
(rf/defn enter-two-random-words
{:events [:my-profile/enter-two-random-words]}
[{:keys [db]}]
(let [{:keys [mnemonic]} (:profile/profile db)
(let [{:keys [mnemonic]} (:multiaccount db)
shuffled-mnemonic (shuffle (map-indexed vector (string/split mnemonic #" ")))]
{:db (assoc db
:my-profile/seed
@@ -88,7 +88,7 @@
(rf/defn show-profile
{:events [:chat.ui/show-profile]}
[{:keys [db]} identity ens-name]
(let [my-public-key (get-in db [:profile/profile :public-key])]
(let [my-public-key (get-in db [:multiaccount :public-key])]
(if (not= my-public-key identity)
{:db (-> db
(assoc :contacts/identity identity)
+2 -2
View File
@@ -42,8 +42,8 @@
(navigation/navigate-back)))
(defn own-public-key?
[{:keys [profile/profile]} public-key]
(= (:public-key profile) public-key))
[{:keys [multiaccount]} public-key]
(= (:public-key multiaccount) public-key))
(rf/defn handle-private-chat
[{:keys [db] :as cofx} {:keys [chat-id]}]

Some files were not shown because too many files have changed in this diff Show More