diff --git a/.detoxrc.js b/.detoxrc.js index 619038f58f..e8d24d4c97 100644 --- a/.detoxrc.js +++ b/.detoxrc.js @@ -1,37 +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" + testRunner: 'jest', + testRegex: '\\.visual\\.js$', + 'runner-config': 'visual-test/config.json', + devices: { + simulator: { + type: 'ios.simulator', + device: { + type: 'iPhone 11 Pro', + }, }, - "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" + apps: { + 'ios.release': { + name: 'StatusIm', + type: 'ios.app', + binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/StatusIm.app', + build: 'make release-ios', }, - "ios.sim.debug": { - "device": "simulator", - "app": "ios.debug" - } - } -} + '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', + }, + }, +}; diff --git a/.eslintrc.js b/.eslintrc.js index 78ef8071d2..40c6dcd05f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,4 @@ module.exports = { - root: true, - extends: '@react-native-community', - }; + root: true, + extends: '@react-native-community', +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..52455c5e04 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +* +!*.js +!*/ +*.clj-kondo +*.shadow-cljs +modules +result diff --git a/.prettierrc.js b/.prettierrc.js index 701869e460..3e061010f1 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,7 +1,9 @@ module.exports = { - bracketSpacing: false, - bracketSameLine: true, - singleQuote: true, - trailingComma: 'all', - tabWidth: 4, + arrowParens: 'always', + printWidth: 120, + semi: true, + singleQuote: true, + tabWidth: 2, + trailingComma: 'all', + useTabs: false, }; diff --git a/Makefile b/Makefile index 648c05f93f..716b897115 100644 --- a/Makefile +++ b/Makefile @@ -306,14 +306,16 @@ lint: ##@test Run code style checks @sh scripts/lint-re-frame-in-quo-components.sh && \ clj-kondo --config .clj-kondo/config.edn --cache false --lint src && \ ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \ - zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES + zprint '{:search-config? true}' -sfc $$ALL_CLOJURE_FILES && \ + yarn prettier # NOTE: We run the linter twice because of https://github.com/kkinnear/zprint/issues/271 lint-fix: export TARGET := default lint-fix: ##@test Run code style checks and fix issues ALL_CLOJURE_FILES=$(call find_all_clojure_files) && \ zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \ - zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES + zprint '{:search-config? true}' -sw $$ALL_CLOJURE_FILES && \ + yarn prettier shadow-server: export TARGET := clojure diff --git a/babel.config.js b/babel.config.js index 5f48dceaa3..8d2592cd21 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,24 +1,19 @@ module.exports = { - - "presets": [ - "module:metro-react-native-babel-preset" - ], - "plugins": [ - "react-native-reanimated/plugin" - ], - "env": { - "test": { - "presets": [ - '@babel/preset-react', - [ - '@babel/preset-env', - { - targets: { - node: '14', - }, - }, - ], - ], - } - } -} \ No newline at end of file + presets: ['module:metro-react-native-babel-preset'], + plugins: ['react-native-reanimated/plugin'], + env: { + test: { + presets: [ + '@babel/preset-react', + [ + '@babel/preset-env', + { + targets: { + node: '14', + }, + }, + ], + ], + }, + }, +}; diff --git a/index.js b/index.js index fc718b7182..bc4004a365 100644 --- a/index.js +++ b/index.js @@ -1,2 +1,2 @@ -import "node-libs-react-native/globals"; -import "./result/index.js"; +import 'node-libs-react-native/globals'; +import './result/index.js'; diff --git a/metro.config.js b/metro.config.js index d4ec8aa4de..55ce3a7533 100644 --- a/metro.config.js +++ b/metro.config.js @@ -5,15 +5,15 @@ * @format */ module.exports = { - transformer: { - getTransformOptions: async () => ({ - transform: { - experimentalImportSupport: false, - inlineRequires: true, - }, - }), - }, - resolver: { - extraNodeModules: require('node-libs-react-native'), - }, + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, + }, + }), + }, + resolver: { + extraNodeModules: require('node-libs-react-native'), + }, }; diff --git a/modules/react-native-status/nodejs/bindings.js b/modules/react-native-status/nodejs/bindings.js index ba707eb100..072c96855d 100644 --- a/modules/react-native-status/nodejs/bindings.js +++ b/modules/react-native-status/nodejs/bindings.js @@ -1,6 +1,5 @@ var binary = require('@mapbox/node-pre-gyp'); var path = require('path'); -var binding_path = binary.find(path.resolve(path.join(__dirname,'./../../../package.json'))); +var binding_path = binary.find(path.resolve(path.join(__dirname, './../../../package.json'))); module.exports = require(binding_path); - diff --git a/package.json b/package.json index 91a1283d23..f35fd0e49a 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "app:compile:android": "shadow-cljs compile android", "app:watch": "shadow-cljs watch android", "app:packager": "react-native start --host 0.0.0.0 --port 8081", - "app:android": "react-native run-android" + "app:android": "react-native run-android", + "prettier": "prettier --write ." }, "dependencies": { "@babel/preset-typescript": "^7.17.12", @@ -95,6 +96,7 @@ "jest-image-snapshot": "^5.1.0", "nodemon": "^2.0.16", "nyc": "^14.1.1", + "prettier": "^2.8.8", "process": "0.11.10", "react-test-renderer": "18.0.0", "rn-snoopy": "git+https://github.com/status-im/rn-snoopy.git#refs/tags/v2.0.2-status", diff --git a/react-native.config.js b/react-native.config.js index 61a28e5ee9..f7b8b901ac 100644 --- a/react-native.config.js +++ b/react-native.config.js @@ -1,31 +1,31 @@ module.exports = { - dependencies: { - 'react-native-config': { - platforms: { - ios: null, - }, - }, - 'react-native-dialogs': { - platforms: { - android: null, - ios: null, - }, - }, - 'react-native-image-resizer': { - platforms: { - ios: null, - }, - }, - 'react-native-status-keycard': { - platforms: { - android: null, - ios: null, - }, - }, - '@react-native-community/blur': { - platforms: { - android: null, - }, - }, + dependencies: { + 'react-native-config': { + platforms: { + ios: null, + }, }, + 'react-native-dialogs': { + platforms: { + android: null, + ios: null, + }, + }, + 'react-native-image-resizer': { + platforms: { + ios: null, + }, + }, + 'react-native-status-keycard': { + platforms: { + android: null, + ios: null, + }, + }, + '@react-native-community/blur': { + platforms: { + android: null, + }, + }, + }, }; diff --git a/resources/js/provider.js b/resources/js/provider.js index 3c48158713..05dee91637 100644 --- a/resources/js/provider.js +++ b/resources/js/provider.js @@ -1,376 +1,372 @@ (function () { - if (typeof EthereumProvider === "undefined") { - var callbackId = 0; - var callbacks = {}; + if (typeof EthereumProvider === 'undefined') { + var callbackId = 0; + var callbacks = {}; - var bridgeSend = function (data) { - ReactNativeWebView.postMessage(JSON.stringify(data)); - } + var bridgeSend = function (data) { + ReactNativeWebView.postMessage(JSON.stringify(data)); + }; - var history = window.history; - var pushState = history.pushState; - history.pushState = function (state) { - setTimeout(function () { - bridgeSend({ - type: 'history-state-changed', - navState: {url: location.href, title: document.title} - }); - }, 100); - return pushState.apply(history, arguments); - }; + var history = window.history; + var pushState = history.pushState; + history.pushState = function (state) { + setTimeout(function () { + bridgeSend({ + type: 'history-state-changed', + navState: { url: location.href, title: document.title }, + }); + }, 100); + return pushState.apply(history, arguments); + }; - function sendAPIrequest(permission, params) { - var messageId = callbackId++; - var params = params || {}; + function sendAPIrequest(permission, params) { + var messageId = callbackId++; + var params = params || {}; - bridgeSend({ - type: 'api-request', - permission: permission, - messageId: messageId, - params: params - }); + bridgeSend({ + type: 'api-request', + permission: permission, + messageId: messageId, + params: params, + }); - return new Promise(function (resolve, reject) { - params['resolve'] = resolve; - params['reject'] = reject; - callbacks[messageId] = params; - }); - } - - function qrCodeResponse(data, callback) { - var result = data.data; - var regex = new RegExp(callback.regex); - if (!result) { - if (callback.reject) { - callback.reject(new Error("Cancelled")); - } - } else if (regex.test(result)) { - if (callback.resolve) { - callback.resolve(result); - } - } else { - if (callback.reject) { - callback.reject(new Error("Doesn't match")); - } - } - } - - function Unauthorized() { - this.name = "Unauthorized"; - this.id = 4100; - this.code = 4100; - this.message = "The requested method and/or account has not been authorized by the user."; - } - - Unauthorized.prototype = Object.create(Error.prototype); - - function UserRejectedRequest() { - this.name = "UserRejectedRequest"; - this.id = 4001; - this.code = 4001; - this.message = "The user rejected the request."; - } - - UserRejectedRequest.prototype = Object.create(Error.prototype); - ReactNativeWebView.onMessage = function (message) { - data = JSON.parse(message); - var id = data.messageId; - var callback = callbacks[id]; - - if (callback) { - if (data.type === "api-response") { - if (data.permission == 'qr-code') { - qrCodeResponse(data, callback); - } else if (data.isAllowed) { - if (data.permission == 'web3') { - var selectedAddress = data.data[0] - window.statusAppcurrentAccountAddress = selectedAddress; - // Set deprecated metamask fields - window.ethereum.selectedAddress = selectedAddress; - window.ethereum.emit("accountsChanged", data.data); - } - callback.resolve(data.data); - } else { - callback.reject(new UserRejectedRequest()); - } - } else if (data.type === "web3-send-async-callback") { - if (callback.beta) { - if (data.error) { - if (data.error.code == 4100) - callback.reject(new Unauthorized()); - else - callback.reject(data.error); - } else { - if (window.statusAppDebug) { - console.log("resolve " + callback.method + " :" + JSON.stringify(data.result.result)); - } - callback.resolve(data.result.result); - } - } else if (callback.results) { - callback.results.push(data.error || data.result); - if (callback.results.length == callback.num) - callback.callback(undefined, callback.results); - } else { - callback.callback(data.error, data.result); - } - } - } - }; - - function web3SuccessResponse(payload, result) { - return { - id: payload.id, - jsonrpc: "2.0", - method: payload.method, - result: result - }; - } - - function web3ErrorResponse(payload, error) { - return { - id: payload.id, - jsonrpc: "2.0", - method: payload.method, - error: error - }; - } - - function getSyncResponse(payload) { - if (payload.method == "eth_accounts" && (typeof window.statusAppcurrentAccountAddress !== "undefined")) { - return web3SuccessResponse(payload, [window.statusAppcurrentAccountAddress]) - } else if (payload.method == "eth_coinbase" && (typeof window.statusAppcurrentAccountAddress !== "undefined")) { - return web3SuccessResponse(payload, window.statusAppcurrentAccountAddress) - } else if (payload.method == "net_version") { - return web3SuccessResponse(payload, window.statusAppNetworkId) - } else if (payload.method == "eth_chainId") { - return web3SuccessResponse(payload, "0x" + Number(window.statusAppNetworkId).toString(16)) - } else if (payload.method == "eth_uninstallFilter") { - return web3SuccessResponse(payload, true); - } else { - return null; - } - } - - var StatusAPI = function () { - }; - - StatusAPI.prototype.getContactCode = function () { - return sendAPIrequest('contact-code'); - }; - - var EthereumProvider = function () { - }; - - EthereumProvider.prototype.isStatus = true; - EthereumProvider.prototype.status = new StatusAPI(); - EthereumProvider.prototype.isConnected = function () { - return true; - }; - // Set legacy metamask fields https://docs.metamask.io/guide/ethereum-provider.html#legacy-api - EthereumProvider.prototype.networkVersion = window.statusAppNetworkId; - EthereumProvider.prototype.chainId = "0x" + Number(window.statusAppNetworkId).toString(16); - - EthereumProvider.prototype._events = {}; - - EthereumProvider.prototype.on = function (name, listener) { - if (!this._events[name]) { - this._events[name] = []; - } - this._events[name].push(listener); - } - - EthereumProvider.prototype.removeListener = function (name, listenerToRemove) { - if (!this._events[name]) { - return - } - - const filterListeners = (listener) => listener !== listenerToRemove; - this._events[name] = this._events[name].filter(filterListeners); - } - - EthereumProvider.prototype.removeAllListeners = function () { - this._events = {}; - } - - EthereumProvider.prototype.emit = function (name, data) { - if (!this._events[name]) { - return - } - this._events[name].forEach(cb => { - // Fixes: https://github.com/status-im/status-mobile/issues/13642 - // Metamask also errors on the same issue, but it's using https://github.com/MetaMask/safe-event-emitter and therefore the dapp still works - try { - cb(data) - } catch (e) { - setTimeout((() => { - throw e - })) - } - }); - } - EthereumProvider.prototype.enable = function () { - if (window.statusAppDebug) { - console.log("enable"); - } - return sendAPIrequest('web3'); - }; - - EthereumProvider.prototype.scanQRCode = function (regex) { - return sendAPIrequest('qr-code', {regex: regex}); - }; - - EthereumProvider.prototype.request = function (requestArguments) { - if (window.statusAppDebug) { - console.log("request: " + JSON.stringify(requestArguments)); - } - if (!requestArguments) { - return Promise.reject(new Error('Request is not valid.')); - } - var method = requestArguments.method; - if (!method) { - return Promise.reject(new Error('Request is not valid.')); - } - - if (method === 'eth_requestAccounts') { - return sendAPIrequest('web3'); - } - - var syncResponse = getSyncResponse({method: method}); - if (syncResponse) { - return new Promise(function (resolve, reject) { - if (window.statusAppDebug) { - console.log("resolved sync method: " + method + ", result: " + JSON.stringify(syncResponse.result)); - } - resolve(syncResponse.result); - }); - } - - var messageId = callbackId++; - var payload = { - id: messageId, - jsonrpc: "2.0", - method: method, - params: requestArguments.params - }; - - bridgeSend({ - type: 'web3-send-async-read-only', - messageId: messageId, - payload: payload - }); - - return new Promise(function (resolve, reject) { - callbacks[messageId] = { - beta: true, - method: method, - resolve: resolve, - reject: reject - }; - }); - }; - - // (DEPRECATED) Support for legacy send method - EthereumProvider.prototype.send = function (param1, param2) { - // reference: https://docs.metamask.io/guide/ethereum-provider.html#legacy-methods - if (typeof param1 == 'object') { - //maybe ways of: - //1.ethereum.send(payload: JsonRpcRequest, callback: JsonRpcCallback): void; - //2.ethereum.send(payload: JsonRpcRequest): unknown; - if (window.statusAppDebug) { - console.log("send (legacy), payload: " + JSON.stringify(param1) + ", callback: " + param2); - } - - var syncResponse = getSyncResponse(param1); - if(syncResponse){ - if(param2){ - param2(null, syncResponse); - return; - }else - return syncResponse; - } - - this.request(param1).then(result => { - if (param2) { - param2(null, web3SuccessResponse(param1, result)); - } - }, reason => { - if (window.statusAppDebug) { - console.log("send (legacy) failed. payload: " + JSON.stringify(param1) + ", reason: " + reason) - } - if (param2) { - param2(reason, web3ErrorResponse(param1, reason)) - } - }); - } else if (typeof param1 == 'string') { - var method = param1; - var params = param2; - if (window.statusAppDebug) { - console.log("send (legacy), method: " + method + ", params: " + JSON.stringify(params)); - } - return this.request({method: method, params: params}); - } else { - throw new Error("unsupported call to send, param1: " + param1 + ", param2: " + param2) - } - } - - // (DEPRECATED) Support for legacy sendSync method - EthereumProvider.prototype.sendSync = function (payload) { - if (window.statusAppDebug) { - console.log("sendSync (legacy)" + JSON.stringify(payload)); - } - if (payload.method == "eth_uninstallFilter") { - this.sendAsync(payload, function (res, err) { - }) - } - var syncResponse = getSyncResponse(payload); - if (syncResponse) { - return syncResponse; - } else { - return web3SuccessResponse(payload, null); - } - }; - - // (DEPRECATED) Support for legacy sendAsync method - EthereumProvider.prototype.sendAsync = function (payload, callback) { - if (window.statusAppDebug) { - console.log("sendAsync (legacy)" + JSON.stringify(payload)); - } - if (!payload) { - return new Error('Request is not valid.'); - } - if (payload.method == 'eth_requestAccounts') { - return sendAPIrequest('web3'); - } - var syncResponse = getSyncResponse(payload); - if (syncResponse && callback) { - callback(null, syncResponse); - } else { - var messageId = callbackId++; - - if (Array.isArray(payload)) { - callbacks[messageId] = { - num: payload.length, - results: [], - callback: callback - }; - for (var i in payload) { - bridgeSend({ - type: 'web3-send-async-read-only', - messageId: messageId, - payload: payload[i] - }); - } - } else { - callbacks[messageId] = {callback: callback}; - bridgeSend({ - type: 'web3-send-async-read-only', - messageId: messageId, - payload: payload - }); - } - } - }; + return new Promise(function (resolve, reject) { + params['resolve'] = resolve; + params['reject'] = reject; + callbacks[messageId] = params; + }); } - window.ethereum = new EthereumProvider(); + function qrCodeResponse(data, callback) { + var result = data.data; + var regex = new RegExp(callback.regex); + if (!result) { + if (callback.reject) { + callback.reject(new Error('Cancelled')); + } + } else if (regex.test(result)) { + if (callback.resolve) { + callback.resolve(result); + } + } else { + if (callback.reject) { + callback.reject(new Error("Doesn't match")); + } + } + } + + function Unauthorized() { + this.name = 'Unauthorized'; + this.id = 4100; + this.code = 4100; + this.message = 'The requested method and/or account has not been authorized by the user.'; + } + + Unauthorized.prototype = Object.create(Error.prototype); + + function UserRejectedRequest() { + this.name = 'UserRejectedRequest'; + this.id = 4001; + this.code = 4001; + this.message = 'The user rejected the request.'; + } + + UserRejectedRequest.prototype = Object.create(Error.prototype); + ReactNativeWebView.onMessage = function (message) { + data = JSON.parse(message); + var id = data.messageId; + var callback = callbacks[id]; + + if (callback) { + if (data.type === 'api-response') { + if (data.permission == 'qr-code') { + qrCodeResponse(data, callback); + } else if (data.isAllowed) { + if (data.permission == 'web3') { + var selectedAddress = data.data[0]; + window.statusAppcurrentAccountAddress = selectedAddress; + // Set deprecated metamask fields + window.ethereum.selectedAddress = selectedAddress; + window.ethereum.emit('accountsChanged', data.data); + } + callback.resolve(data.data); + } else { + callback.reject(new UserRejectedRequest()); + } + } else if (data.type === 'web3-send-async-callback') { + if (callback.beta) { + if (data.error) { + if (data.error.code == 4100) callback.reject(new Unauthorized()); + else callback.reject(data.error); + } else { + if (window.statusAppDebug) { + console.log('resolve ' + callback.method + ' :' + JSON.stringify(data.result.result)); + } + callback.resolve(data.result.result); + } + } else if (callback.results) { + callback.results.push(data.error || data.result); + if (callback.results.length == callback.num) callback.callback(undefined, callback.results); + } else { + callback.callback(data.error, data.result); + } + } + } + }; + + function web3SuccessResponse(payload, result) { + return { + id: payload.id, + jsonrpc: '2.0', + method: payload.method, + result: result, + }; + } + + function web3ErrorResponse(payload, error) { + return { + id: payload.id, + jsonrpc: '2.0', + method: payload.method, + error: error, + }; + } + + function getSyncResponse(payload) { + if (payload.method == 'eth_accounts' && typeof window.statusAppcurrentAccountAddress !== 'undefined') { + return web3SuccessResponse(payload, [window.statusAppcurrentAccountAddress]); + } else if (payload.method == 'eth_coinbase' && typeof window.statusAppcurrentAccountAddress !== 'undefined') { + return web3SuccessResponse(payload, window.statusAppcurrentAccountAddress); + } else if (payload.method == 'net_version') { + return web3SuccessResponse(payload, window.statusAppNetworkId); + } else if (payload.method == 'eth_chainId') { + return web3SuccessResponse(payload, '0x' + Number(window.statusAppNetworkId).toString(16)); + } else if (payload.method == 'eth_uninstallFilter') { + return web3SuccessResponse(payload, true); + } else { + return null; + } + } + + var StatusAPI = function () {}; + + StatusAPI.prototype.getContactCode = function () { + return sendAPIrequest('contact-code'); + }; + + var EthereumProvider = function () {}; + + EthereumProvider.prototype.isStatus = true; + EthereumProvider.prototype.status = new StatusAPI(); + EthereumProvider.prototype.isConnected = function () { + return true; + }; + // Set legacy metamask fields https://docs.metamask.io/guide/ethereum-provider.html#legacy-api + EthereumProvider.prototype.networkVersion = window.statusAppNetworkId; + EthereumProvider.prototype.chainId = '0x' + Number(window.statusAppNetworkId).toString(16); + + EthereumProvider.prototype._events = {}; + + EthereumProvider.prototype.on = function (name, listener) { + if (!this._events[name]) { + this._events[name] = []; + } + this._events[name].push(listener); + }; + + EthereumProvider.prototype.removeListener = function (name, listenerToRemove) { + if (!this._events[name]) { + return; + } + + const filterListeners = (listener) => listener !== listenerToRemove; + this._events[name] = this._events[name].filter(filterListeners); + }; + + EthereumProvider.prototype.removeAllListeners = function () { + this._events = {}; + }; + + EthereumProvider.prototype.emit = function (name, data) { + if (!this._events[name]) { + return; + } + this._events[name].forEach((cb) => { + // Fixes: https://github.com/status-im/status-mobile/issues/13642 + // Metamask also errors on the same issue, but it's using https://github.com/MetaMask/safe-event-emitter and therefore the dapp still works + try { + cb(data); + } catch (e) { + setTimeout(() => { + throw e; + }); + } + }); + }; + EthereumProvider.prototype.enable = function () { + if (window.statusAppDebug) { + console.log('enable'); + } + return sendAPIrequest('web3'); + }; + + EthereumProvider.prototype.scanQRCode = function (regex) { + return sendAPIrequest('qr-code', { regex: regex }); + }; + + EthereumProvider.prototype.request = function (requestArguments) { + if (window.statusAppDebug) { + console.log('request: ' + JSON.stringify(requestArguments)); + } + if (!requestArguments) { + return Promise.reject(new Error('Request is not valid.')); + } + var method = requestArguments.method; + if (!method) { + return Promise.reject(new Error('Request is not valid.')); + } + + if (method === 'eth_requestAccounts') { + return sendAPIrequest('web3'); + } + + var syncResponse = getSyncResponse({ method: method }); + if (syncResponse) { + return new Promise(function (resolve, reject) { + if (window.statusAppDebug) { + console.log('resolved sync method: ' + method + ', result: ' + JSON.stringify(syncResponse.result)); + } + resolve(syncResponse.result); + }); + } + + var messageId = callbackId++; + var payload = { + id: messageId, + jsonrpc: '2.0', + method: method, + params: requestArguments.params, + }; + + bridgeSend({ + type: 'web3-send-async-read-only', + messageId: messageId, + payload: payload, + }); + + return new Promise(function (resolve, reject) { + callbacks[messageId] = { + beta: true, + method: method, + resolve: resolve, + reject: reject, + }; + }); + }; + + // (DEPRECATED) Support for legacy send method + EthereumProvider.prototype.send = function (param1, param2) { + // reference: https://docs.metamask.io/guide/ethereum-provider.html#legacy-methods + if (typeof param1 == 'object') { + //maybe ways of: + //1.ethereum.send(payload: JsonRpcRequest, callback: JsonRpcCallback): void; + //2.ethereum.send(payload: JsonRpcRequest): unknown; + if (window.statusAppDebug) { + console.log('send (legacy), payload: ' + JSON.stringify(param1) + ', callback: ' + param2); + } + + var syncResponse = getSyncResponse(param1); + if (syncResponse) { + if (param2) { + param2(null, syncResponse); + return; + } else return syncResponse; + } + + this.request(param1).then( + (result) => { + if (param2) { + param2(null, web3SuccessResponse(param1, result)); + } + }, + (reason) => { + if (window.statusAppDebug) { + console.log('send (legacy) failed. payload: ' + JSON.stringify(param1) + ', reason: ' + reason); + } + if (param2) { + param2(reason, web3ErrorResponse(param1, reason)); + } + }, + ); + } else if (typeof param1 == 'string') { + var method = param1; + var params = param2; + if (window.statusAppDebug) { + console.log('send (legacy), method: ' + method + ', params: ' + JSON.stringify(params)); + } + return this.request({ method: method, params: params }); + } else { + throw new Error('unsupported call to send, param1: ' + param1 + ', param2: ' + param2); + } + }; + + // (DEPRECATED) Support for legacy sendSync method + EthereumProvider.prototype.sendSync = function (payload) { + if (window.statusAppDebug) { + console.log('sendSync (legacy)' + JSON.stringify(payload)); + } + if (payload.method == 'eth_uninstallFilter') { + this.sendAsync(payload, function (res, err) {}); + } + var syncResponse = getSyncResponse(payload); + if (syncResponse) { + return syncResponse; + } else { + return web3SuccessResponse(payload, null); + } + }; + + // (DEPRECATED) Support for legacy sendAsync method + EthereumProvider.prototype.sendAsync = function (payload, callback) { + if (window.statusAppDebug) { + console.log('sendAsync (legacy)' + JSON.stringify(payload)); + } + if (!payload) { + return new Error('Request is not valid.'); + } + if (payload.method == 'eth_requestAccounts') { + return sendAPIrequest('web3'); + } + var syncResponse = getSyncResponse(payload); + if (syncResponse && callback) { + callback(null, syncResponse); + } else { + var messageId = callbackId++; + + if (Array.isArray(payload)) { + callbacks[messageId] = { + num: payload.length, + results: [], + callback: callback, + }; + for (var i in payload) { + bridgeSend({ + type: 'web3-send-async-read-only', + messageId: messageId, + payload: payload[i], + }); + } + } else { + callbacks[messageId] = { callback: callback }; + bridgeSend({ + type: 'web3-send-async-read-only', + messageId: messageId, + payload: payload, + }); + } + } + }; + } + + window.ethereum = new EthereumProvider(); })(); diff --git a/src/js/worklets/bottom_sheet.js b/src/js/worklets/bottom_sheet.js index a5761716ee..074b6a91ca 100644 --- a/src/js/worklets/bottom_sheet.js +++ b/src/js/worklets/bottom_sheet.js @@ -1,15 +1,15 @@ -import {useDerivedValue, runOnJS as reaRunOnJS, runOnJS} from "react-native-reanimated"; +import { useDerivedValue, runOnJS as reaRunOnJS, runOnJS } from 'react-native-reanimated'; export function useTranslateY(initialTranslationY, bottomSheetDy, panY) { return useDerivedValue(() => { - return initialTranslationY - (bottomSheetDy.value - panY.value) - }) + return initialTranslationY - (bottomSheetDy.value - panY.value); + }); } export function useBackgroundOpacity(translateY, backgroundHeight, windowHeight, opacity) { return useDerivedValue(() => { - const calculatedOpacity = ((translateY.value - windowHeight) / -backgroundHeight) * opacity + const calculatedOpacity = ((translateY.value - windowHeight) / -backgroundHeight) * opacity; - return Math.max(Math.min(calculatedOpacity, opacity), 0) - }) + return Math.max(Math.min(calculatedOpacity, opacity), 0); + }); } diff --git a/src/js/worklets/core.js b/src/js/worklets/core.js index 9575b3732c..b0083781c9 100644 --- a/src/js/worklets/core.js +++ b/src/js/worklets/core.js @@ -7,70 +7,64 @@ import { useDerivedValue, interpolate } from 'react-native-reanimated'; // 2. remove keys with nil value, else useAnimatedStyle will throw an error // https://github.com/status-im/status-mobile/issues/14756 export function applyAnimationsToStyle(animations, style) { - return function() { - 'worklet' + return function () { + 'worklet'; - var animatedStyle = {} + var animatedStyle = {}; // Normal Style for (var key in style) { - if (key == "transform") { - var transforms = style[key]; - var filteredTransforms = [] + if (key == 'transform') { + var transforms = style[key]; + var filteredTransforms = []; - for (var transform of transforms) { - var transformKey = Object.keys(transform)[0]; - var transformValue = transform[transformKey]; - if(transformValue !== null) { - filteredTransforms.push( - {[transformKey.replace(/-./g, x=>x[1].toUpperCase())]: transformValue} - ); - } - } + for (var transform of transforms) { + var transformKey = Object.keys(transform)[0]; + var transformValue = transform[transformKey]; + if (transformValue !== null) { + filteredTransforms.push({ [transformKey.replace(/-./g, (x) => x[1].toUpperCase())]: transformValue }); + } + } - animatedStyle[key] = filteredTransforms; + animatedStyle[key] = filteredTransforms; } else { - var value = style[key]; - if (value !== null) { - animatedStyle[key.replace(/-./g, x=>x[1].toUpperCase())] = value; - } + var value = style[key]; + if (value !== null) { + animatedStyle[key.replace(/-./g, (x) => x[1].toUpperCase())] = value; + } } } // Animations for (var key in animations) { - if (key == "transform") { + if (key == 'transform') { var transforms = animations[key]; - var animatedTransforms = [] + var animatedTransforms = []; for (var transform of transforms) { var transformKey = Object.keys(transform)[0]; - var transformValue = transform[transformKey].value; - if (transformValue !== null) { - animatedTransforms.push( - {[transformKey.replace(/-./g, x=>x[1].toUpperCase())]: transformValue} - ); - } + var transformValue = transform[transformKey].value; + if (transformValue !== null) { + animatedTransforms.push({ [transformKey.replace(/-./g, (x) => x[1].toUpperCase())]: transformValue }); + } } animatedStyle[key] = animatedTransforms; } else { - var animatedValue = animations[key].value; - if (animatedValue !== null) { - animatedStyle[key.replace(/-./g, x=>x[1].toUpperCase())] = animatedValue; - } + var animatedValue = animations[key].value; + if (animatedValue !== null) { + animatedStyle[key.replace(/-./g, (x) => x[1].toUpperCase())] = animatedValue; + } } } return animatedStyle; }; -}; +} export function interpolateValue(sharedValue, inputRange, outputRange, extrapolation) { - return useDerivedValue( - function () { - 'worklet' - return interpolate(sharedValue.value, inputRange, outputRange, extrapolation); - } - ); + return useDerivedValue(function () { + 'worklet'; + return interpolate(sharedValue.value, inputRange, outputRange, extrapolation); + }); } diff --git a/src/js/worklets/lightbox.js b/src/js/worklets/lightbox.js index d9e035caae..101eaa62b4 100644 --- a/src/js/worklets/lightbox.js +++ b/src/js/worklets/lightbox.js @@ -1,10 +1,8 @@ import { useDerivedValue } from 'react-native-reanimated'; export function infoLayout(input, isTop) { - return useDerivedValue( - function () { - 'worklet' - return isTop ? input.value : -input.value - } - ); + return useDerivedValue(function () { + 'worklet'; + return isTop ? input.value : -input.value; + }); } diff --git a/src/js/worklets/onboarding_carousel.js b/src/js/worklets/onboarding_carousel.js index f54ee48267..b0fea3f05c 100644 --- a/src/js/worklets/onboarding_carousel.js +++ b/src/js/worklets/onboarding_carousel.js @@ -5,43 +5,39 @@ const slideAnimationDuration = 300; const easeOut = { duration: slideAnimationDuration, easing: Easing.bezier(0, 0, 0.58, 1), -} +}; // Derived Values export function dynamicProgressBarWidth(staticProgressBarWidth, progress) { - return useDerivedValue( - function () { - 'worklet' - return staticProgressBarWidth * (progress.value || 0) / 100; - } - ); + return useDerivedValue(function () { + 'worklet'; + return (staticProgressBarWidth * (progress.value || 0)) / 100; + }); } export function carouselLeftPosition(windowWidth, progress) { - return useDerivedValue( - function () { - 'worklet' - const progressValue = progress.value; - 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); + return useDerivedValue(function () { + 'worklet'; + const progressValue = progress.value; + 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; - } - }); + return 0; + } + }); } - diff --git a/src/js/worklets/parallax.js b/src/js/worklets/parallax.js index 92ff146c8a..64c3cf8b97 100644 --- a/src/js/worklets/parallax.js +++ b/src/js/worklets/parallax.js @@ -1,10 +1,4 @@ -import { - useAnimatedStyle, - useAnimatedSensor, - withTiming, - interpolate, - SensorType -} from 'react-native-reanimated'; +import { useAnimatedStyle, useAnimatedSensor, withTiming, interpolate, SensorType } from 'react-native-reanimated'; import { Platform } from 'react-native'; const PI = Math.PI; @@ -21,34 +15,31 @@ there is a bug with the pitch and roll calculations provided directly from the s be done using the quaternions directly. */ export function sensorAnimatedImage(zIndex, offset, stretch) { - 'worklet' - const rotationSensor = useAnimatedSensor(SensorType.ROTATION, { interval: 30 }) - return useAnimatedStyle(function () { + 'worklet'; + const rotationSensor = useAnimatedSensor(SensorType.ROTATION, { interval: 30 }); + return useAnimatedStyle(function () { + const { qx, qz, qw, qy, pitch } = rotationSensor.sensor.value; - const { qx, qz, qw, qy, pitch } = rotationSensor.sensor.value; + const roll = Math.asin(-2.0 * (qx * qz - qw * qy)); - const roll = Math.asin(-2.0 * (qx * qz - qw * qy)) + const translateY = withTiming( + interpolate(pitch, Platform.OS === 'ios' ? [-PI, PI] : [PI, -PI], [ + (-offset * 3) / zIndex - offset + (offset - stretch), + (offset * 3) / zIndex - offset + (offset - stretch), + ]), + { duration: 10 }, + ); + const translateX = withTiming( + interpolate( + roll, + [1, -1], + [(-offset * 2) / zIndex - offset + (offset - stretch), (offset * 2) / zIndex - offset + (offset - stretch)], + ), + { duration: 10 }, + ); - const translateY = withTiming( - interpolate( - pitch, - Platform.OS === 'ios' ? [-PI, PI] : [PI, -PI], - [(-offset * 3) / zIndex - offset + (offset - stretch), (offset * 3) / zIndex - offset + (offset - stretch)]), - { duration: 10 } - ); - const translateX = withTiming( - interpolate( - roll, - [1, -1], - [(-offset * 2) / zIndex - offset + (offset - stretch), (offset * 2) / zIndex - offset + (offset - stretch)]), - { duration: 10 } - ); - - return { - transform: [ - { translateX }, - { translateY } - ] - } - }) + return { + transform: [{ translateX }, { translateY }], + }; + }); } diff --git a/src/js/worklets/record_audio.js b/src/js/worklets/record_audio.js index d0d46e5706..9527ddccdc 100644 --- a/src/js/worklets/record_audio.js +++ b/src/js/worklets/record_audio.js @@ -3,16 +3,14 @@ import { useDerivedValue } from 'react-native-reanimated'; const MAX_SCALE = 1.8; export function ringScale(scale, subtract) { - return useDerivedValue( - function () { - 'worklet' - const value = scale.value; - const maxDelta = MAX_SCALE - 1; - const maxDeltaDiff = 1 - maxDelta; - const maxVirtualScale = MAX_SCALE + maxDelta; - const decimals = value - Math.floor(value); - const normalizedValue = value >= maxVirtualScale ? (decimals + ((parseInt(value) - 1) * maxDeltaDiff) + 1) : value; - return (((normalizedValue - subtract) > MAX_SCALE ? normalizedValue - maxDelta : normalizedValue) - subtract); - } - ); + return useDerivedValue(function () { + 'worklet'; + const value = scale.value; + const maxDelta = MAX_SCALE - 1; + const maxDeltaDiff = 1 - maxDelta; + const maxVirtualScale = MAX_SCALE + maxDelta; + const decimals = value - Math.floor(value); + const normalizedValue = value >= maxVirtualScale ? decimals + (parseInt(value) - 1) * maxDeltaDiff + 1 : value; + return (normalizedValue - subtract > MAX_SCALE ? normalizedValue - maxDelta : normalizedValue) - subtract; + }); } diff --git a/src/js/worklets/scroll_view.js b/src/js/worklets/scroll_view.js index 60b6dff90c..91fdccd6f5 100644 --- a/src/js/worklets/scroll_view.js +++ b/src/js/worklets/scroll_view.js @@ -1,7 +1,7 @@ -import {useAnimatedScrollHandler} from "react-native-reanimated"; +import { useAnimatedScrollHandler } from 'react-native-reanimated'; export function useAnimatedScrollHandlerWorklet(scrollY) { - return useAnimatedScrollHandler((event) => { - scrollY.value = event.contentOffset.y; - }) + return useAnimatedScrollHandler((event) => { + scrollY.value = event.contentOffset.y; + }); } diff --git a/src/js/worklets/shell/bottom_tabs.js b/src/js/worklets/shell/bottom_tabs.js index f38290d17d..d222e3b3c2 100644 --- a/src/js/worklets/shell/bottom_tabs.js +++ b/src/js/worklets/shell/bottom_tabs.js @@ -1,46 +1,47 @@ import { useDerivedValue, withTiming } from 'react-native-reanimated'; import * as constants from './constants'; -export function bottomTabIconColor(stackId, selectedStackId, homeStackState, - passThrough, selectedTabColor, defaultColor, - passThroughColor) { - return useDerivedValue( - function () { - 'worklet' - var homeStackStateValue = homeStackState.value; - if (selectedStackId.value == stackId && - (homeStackStateValue == constants.OPEN_WITH_ANIMATION || - homeStackStateValue == constants.OPEN_WITHOUT_ANIMATION)){ - return selectedTabColor; - } - else if (passThrough.value){ - return passThroughColor; - } - else { - return defaultColor; - } +export function bottomTabIconColor( + stackId, + selectedStackId, + homeStackState, + passThrough, + selectedTabColor, + defaultColor, + passThroughColor, +) { + return useDerivedValue(function () { + 'worklet'; + var homeStackStateValue = homeStackState.value; + if ( + selectedStackId.value == stackId && + (homeStackStateValue == constants.OPEN_WITH_ANIMATION || homeStackStateValue == constants.OPEN_WITHOUT_ANIMATION) + ) { + return selectedTabColor; + } else if (passThrough.value) { + return passThroughColor; + } else { + return defaultColor; } - ); + }); } export function bottomTabsHeight(homeStackState, height, extendedHeight) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { + return useDerivedValue(function () { + 'worklet'; + switch (homeStackState.value) { case constants.OPEN_WITH_ANIMATION: - return withTiming(extendedHeight, constants.LINEAR_EASING); - break; + return withTiming(extendedHeight, constants.LINEAR_EASING); + break; case constants.CLOSE_WITH_ANIMATION: - return withTiming(height, constants.LINEAR_EASING); - break; + return withTiming(height, constants.LINEAR_EASING); + break; case constants.OPEN_WITHOUT_ANIMATION: - return extendedHeight; - break; + return extendedHeight; + break; case constants.CLOSE_WITHOUT_ANIMATION: - return height; - break; - } + return height; + break; } - ) + }); } diff --git a/src/js/worklets/shell/constants.js b/src/js/worklets/shell/constants.js index 6126b4b080..66edfacacf 100644 --- a/src/js/worklets/shell/constants.js +++ b/src/js/worklets/shell/constants.js @@ -19,9 +19,9 @@ export const SHELL_ANIMATION_TIME = 200; export const LINEAR_EASING = { duration: SHELL_ANIMATION_TIME, easing: Easing.bezier(0, 0, 1, 1), -} +}; export const EASE_OUT_EASING = { duration: SHELL_ANIMATION_TIME, easing: Easing.bezier(0, 0, 0.58, 1), -} +}; diff --git a/src/js/worklets/shell/floating_screen.js b/src/js/worklets/shell/floating_screen.js index 1fcc0b3286..654edea2b4 100644 --- a/src/js/worklets/shell/floating_screen.js +++ b/src/js/worklets/shell/floating_screen.js @@ -2,88 +2,89 @@ import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 're import * as constants from './constants'; // Derived Values -export function screenLeft (screenState, screenWidth, switcherCardLeftPosition) { - return useDerivedValue( - function () { - 'worklet' - switch (screenState.value) { +export function screenLeft(screenState, screenWidth, switcherCardLeftPosition) { + return useDerivedValue(function () { + 'worklet'; + switch (screenState.value) { case constants.CLOSE_SCREEN_WITH_SLIDE_ANIMATION: - return withTiming(screenWidth, constants.EASE_OUT_EASING); + return withTiming(screenWidth, constants.EASE_OUT_EASING); case constants.OPEN_SCREEN_WITH_SLIDE_ANIMATION: - return withTiming(0, constants.EASE_OUT_EASING); + return withTiming(0, constants.EASE_OUT_EASING); case constants.CLOSE_SCREEN_WITHOUT_ANIMATION: - return screenWidth; + return screenWidth; case constants.OPEN_SCREEN_WITHOUT_ANIMATION: - // Note - don't use return 0; its not working in ios - // https://github.com/software-mansion/react-native-reanimated/issues/3296#issuecomment-1573900172 - return withSequence(withTiming(-1, {duration: 0}), withTiming(0, {duration: 0})); + // Note - don't use return 0; its not working in ios + // https://github.com/software-mansion/react-native-reanimated/issues/3296#issuecomment-1573900172 + return withSequence(withTiming(-1, { duration: 0 }), withTiming(0, { duration: 0 })); case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: - return withTiming(switcherCardLeftPosition, constants.EASE_OUT_EASING); + return withTiming(switcherCardLeftPosition, constants.EASE_OUT_EASING); case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: - return withTiming(0, constants.EASE_OUT_EASING); + return withTiming(0, constants.EASE_OUT_EASING); default: - return screenWidth; - } - }); + return screenWidth; + } + }); } -export function screenTop (screenState, switcherCardTopPosition) { - return useDerivedValue( - function () { - 'worklet' - switch (screenState.value) { +export function screenTop(screenState, switcherCardTopPosition) { + return useDerivedValue(function () { + 'worklet'; + switch (screenState.value) { case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: - return withTiming(switcherCardTopPosition, constants.EASE_OUT_EASING); + return withTiming(switcherCardTopPosition, constants.EASE_OUT_EASING); case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: - return withTiming(0, constants.EASE_OUT_EASING); + return withTiming(0, constants.EASE_OUT_EASING); default: - return 0; - } - }); + return 0; + } + }); } -export function screenWidth (screenState, screenWidth, switcherCardSize) { - return useDerivedValue( - function () { - 'worklet' - switch (screenState.value) { +export function screenWidth(screenState, screenWidth, switcherCardSize) { + return useDerivedValue(function () { + 'worklet'; + switch (screenState.value) { case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: - return withTiming(switcherCardSize, constants.EASE_OUT_EASING); + return withTiming(switcherCardSize, constants.EASE_OUT_EASING); case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: - return withSequence(withTiming(switcherCardSize, {duration: 0}), withTiming(screenWidth, constants.EASE_OUT_EASING)); + return withSequence( + withTiming(switcherCardSize, { duration: 0 }), + withTiming(screenWidth, constants.EASE_OUT_EASING), + ); default: - return screenWidth; - } - }); + return screenWidth; + } + }); } -export function screenHeight (screenState, screenHeight, switcherCardSize) { - return useDerivedValue( - function () { - 'worklet' - switch (screenState.value) { +export function screenHeight(screenState, screenHeight, switcherCardSize) { + return useDerivedValue(function () { + 'worklet'; + switch (screenState.value) { case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: - return withTiming(switcherCardSize, constants.EASE_OUT_EASING); + return withTiming(switcherCardSize, constants.EASE_OUT_EASING); case constants.OPEN_SCREEN_WITH_SHELL_ANIMATION: - return withSequence(withTiming(switcherCardSize, {duration: 0}), withTiming(screenHeight, constants.EASE_OUT_EASING)); + return withSequence( + withTiming(switcherCardSize, { duration: 0 }), + withTiming(screenHeight, constants.EASE_OUT_EASING), + ); default: - return screenHeight; - } - }); + return screenHeight; + } + }); } -export function screenZIndex (screenState) { - return useDerivedValue( - function () { - 'worklet' - switch (screenState.value) { +export function screenZIndex(screenState) { + return useDerivedValue(function () { + 'worklet'; + switch (screenState.value) { case constants.CLOSE_SCREEN_WITH_SHELL_ANIMATION: case constants.CLOSE_SCREEN_WITH_SLIDE_ANIMATION: - return withDelay(constants.SHELL_ANIMATION_TIME, withTiming(-1, {duration: 0})); + return withDelay(constants.SHELL_ANIMATION_TIME, withTiming(-1, { duration: 0 })); case constants.CLOSE_SCREEN_WITHOUT_ANIMATION: - return -1; + return -1; default: - return 1; - } - }); + return 1; + } + }); } diff --git a/src/js/worklets/shell/home_stack.js b/src/js/worklets/shell/home_stack.js index 8fdfa1be14..859048faf1 100644 --- a/src/js/worklets/shell/home_stack.js +++ b/src/js/worklets/shell/home_stack.js @@ -3,126 +3,111 @@ import * as constants from './constants'; // Derived values for each stack (communities, chat, wallet, browser) export function stackOpacity(stackId, selectedStackId) { - return useDerivedValue( - function () { - 'worklet' - return selectedStackId.value == stackId ? 1 : 0; - } - ); + return useDerivedValue(function () { + 'worklet'; + return selectedStackId.value == stackId ? 1 : 0; + }); } export function stackZIndex(stackId, selectedStackId) { - return useDerivedValue( - function () { - 'worklet' - return selectedStackId.value == stackId ? 10 : 9; - } - ); + return useDerivedValue(function () { + 'worklet'; + return selectedStackId.value == stackId ? 10 : 9; + }); } // Derived values for home stack (container) export function homeStackOpacity(homeStackState) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { + return useDerivedValue(function () { + 'worklet'; + switch (homeStackState.value) { case constants.OPEN_WITH_ANIMATION: - return withTiming(1, constants.LINEAR_EASING); + return withTiming(1, constants.LINEAR_EASING); case constants.CLOSE_WITH_ANIMATION: - return withTiming(0, constants.LINEAR_EASING); + return withTiming(0, constants.LINEAR_EASING); case constants.OPEN_WITHOUT_ANIMATION: - return 1; + return 1; case constants.CLOSE_WITHOUT_ANIMATION: - return 0; - } + return 0; } - ); + }); } export function homeStackTop(homeStackState, top) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { + return useDerivedValue(function () { + 'worklet'; + switch (homeStackState.value) { case constants.OPEN_WITH_ANIMATION: - return withTiming(0, constants.LINEAR_EASING); + return withTiming(0, constants.LINEAR_EASING); case constants.CLOSE_WITH_ANIMATION: - return withTiming(top, constants.LINEAR_EASING); + return withTiming(top, constants.LINEAR_EASING); case constants.OPEN_WITHOUT_ANIMATION: - return 0; + return 0; case constants.CLOSE_WITHOUT_ANIMATION: - return top; - } + return top; } - ); + }); } export function homeStackLeft(selectedStackId, animateHomeStackLeft, homeStackState, left) { - return useDerivedValue( - function () { - 'worklet' - if (animateHomeStackLeft.value) { - var leftValue = left[selectedStackId.value]; - switch (homeStackState.value) { - case constants.OPEN_WITH_ANIMATION: - return withSequence(withTiming(leftValue, {duration: 0}), withTiming(0, constants.LINEAR_EASING)) - case constants.CLOSE_WITH_ANIMATION: - return withTiming(leftValue, constants.LINEAR_EASING); - case constants.OPEN_WITHOUT_ANIMATION: - return 0; - case constants.CLOSE_WITHOUT_ANIMATION: - return leftValue; - } - } else { - return 0; + return useDerivedValue(function () { + 'worklet'; + if (animateHomeStackLeft.value) { + var leftValue = left[selectedStackId.value]; + switch (homeStackState.value) { + case constants.OPEN_WITH_ANIMATION: + return withSequence(withTiming(leftValue, { duration: 0 }), withTiming(0, constants.LINEAR_EASING)); + case constants.CLOSE_WITH_ANIMATION: + return withTiming(leftValue, constants.LINEAR_EASING); + case constants.OPEN_WITHOUT_ANIMATION: + return 0; + case constants.CLOSE_WITHOUT_ANIMATION: + return leftValue; } + } else { + return 0; } - ); + }); } export function homeStackPointer(homeStackState) { - return useDerivedValue( - function () { - 'worklet' - var homeStackStateValue = homeStackState.value; - return (homeStackStateValue == constants.OPEN_WITH_ANIMATION || - homeStackStateValue == constants.OPEN_WITHOUT_ANIMATION) ? "auto" : "none"; - } - ); + return useDerivedValue(function () { + 'worklet'; + var homeStackStateValue = homeStackState.value; + return homeStackStateValue == constants.OPEN_WITH_ANIMATION || + homeStackStateValue == constants.OPEN_WITHOUT_ANIMATION + ? 'auto' + : 'none'; + }); } export function homeStackScale(homeStackState, minimizeScale) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { + return useDerivedValue(function () { + 'worklet'; + switch (homeStackState.value) { case constants.OPEN_WITH_ANIMATION: - return withTiming(1, constants.LINEAR_EASING); + return withTiming(1, constants.LINEAR_EASING); case constants.CLOSE_WITH_ANIMATION: - return withTiming(minimizeScale, constants.LINEAR_EASING); + return withTiming(minimizeScale, constants.LINEAR_EASING); case constants.OPEN_WITHOUT_ANIMATION: - return 1; + return 1; case constants.CLOSE_WITHOUT_ANIMATION: - return minimizeScale; - } + return minimizeScale; } - ); + }); } - export function homeStackBorderRadius(homeStackState) { - return useDerivedValue( - function () { - 'worklet' - switch (homeStackState.value) { + return useDerivedValue(function () { + 'worklet'; + switch (homeStackState.value) { case constants.OPEN_WITH_ANIMATION: - return withDelay(constants.SHELL_ANIMATION_TIME, withTiming(0, {duration: 0})); + return withDelay(constants.SHELL_ANIMATION_TIME, withTiming(0, { duration: 0 })); case constants.CLOSE_WITH_ANIMATION: case constants.CLOSE_WITHOUT_ANIMATION: - return 20; + return 20; case constants.OPEN_WITHOUT_ANIMATION: - return 0; - } + return 0; } - ); + }); } diff --git a/src/quo2/__visual_tests__/button.e2e.js b/src/quo2/__visual_tests__/button.e2e.js index ad3df3d392..7af2afee8a 100644 --- a/src/quo2/__visual_tests__/button.e2e.js +++ b/src/quo2/__visual_tests__/button.e2e.js @@ -1,44 +1,49 @@ -const waitToNavigate = duration => new Promise(resolve => setTimeout(() => resolve(), duration)); +const waitToNavigate = (duration) => new Promise((resolve) => setTimeout(() => resolve(), duration)); -const SUPER_SECRET_PASSWORD = 'password' +const SUPER_SECRET_PASSWORD = 'password'; const loginToHomePage = async () => { - if (device.getPlatform() === 'ios') { - await device.setStatusBar({ time: '12:34', dataNetwork: 'wifi', wifiBars: '3', batteryState: 'charging', batteryLevel: '100' }); - } - await device.reloadReactNative(); - await element(by.id('terms-of-service')).tap(); - await waitToNavigate(400); - await element(by.id('get-started')).tap(); - await waitToNavigate(300); - await element(by.id('generate-keys')).tap(); - await waitToNavigate(200); - await element(by.text('Next')).tap(); - await waitToNavigate(700); - await element(by.text('Next')).tap(); - await waitToNavigate(700); - await element(by.id('password-placeholder')).typeText(SUPER_SECRET_PASSWORD); - await element(by.id('confirm-password-placeholder')).typeText(SUPER_SECRET_PASSWORD); - await element(by.text('Next')).tap(); - await waitToNavigate(200); - await element(by.id("browser-stack")).tap(); - await waitToNavigate(400); - await element(by.text("Quo2.0 Preview")).tap(); - await waitToNavigate(400); -} + if (device.getPlatform() === 'ios') { + await device.setStatusBar({ + time: '12:34', + dataNetwork: 'wifi', + wifiBars: '3', + batteryState: 'charging', + batteryLevel: '100', + }); + } + await device.reloadReactNative(); + await element(by.id('terms-of-service')).tap(); + await waitToNavigate(400); + await element(by.id('get-started')).tap(); + await waitToNavigate(300); + await element(by.id('generate-keys')).tap(); + await waitToNavigate(200); + await element(by.text('Next')).tap(); + await waitToNavigate(700); + await element(by.text('Next')).tap(); + await waitToNavigate(700); + await element(by.id('password-placeholder')).typeText(SUPER_SECRET_PASSWORD); + await element(by.id('confirm-password-placeholder')).typeText(SUPER_SECRET_PASSWORD); + await element(by.text('Next')).tap(); + await waitToNavigate(200); + await element(by.id('browser-stack')).tap(); + await waitToNavigate(400); + await element(by.text('Quo2.0 Preview')).tap(); + await waitToNavigate(400); +}; describe('Default Renders', () => { - beforeAll(async () => loginToHomePage()) - beforeEach(async () => { - }); - afterEach(async () => { - await element(by.id("back-button")).tap(); - await waitToNavigate(200); - }); + beforeAll(async () => loginToHomePage()); + beforeEach(async () => {}); + afterEach(async () => { + await element(by.id('back-button')).tap(); + await waitToNavigate(200); + }); - it(`button page should match image render`, async () => { - await element(by.id(`quo2-:button`)).tap(); - await waitToNavigate(200); - const res = await jestExpect(`button`).toMatchImageSnapshot(); - }) -}); \ No newline at end of file + it(`button page should match image render`, async () => { + await element(by.id(`quo2-:button`)).tap(); + await waitToNavigate(200); + const res = await jestExpect(`button`).toMatchImageSnapshot(); + }); +}); diff --git a/test-resources/override.js b/test-resources/override.js index f0d5368393..224f9d7d97 100644 --- a/test-resources/override.js +++ b/test-resources/override.js @@ -1,6 +1,6 @@ var Mocks = require('../target/mocks/mocks.js'); var Module = require('module'); -process.env.TZ = 'Etc/UTC' +process.env.TZ = 'Etc/UTC'; const originalLoader = Module._load; @@ -9,22 +9,20 @@ const originalLoader = Module._load; */ Module._load = function hookedLoader(request, parent, isMain) { - if (request.match(/.jpeg|.jpg|.png|.mp4$/)) { - return { uri: request }; - } + if (request.match(/.jpeg|.jpg|.png|.mp4$/)) { + return { uri: request }; + } - return originalLoader(request, parent, isMain); + return originalLoader(request, parent, isMain); }; var originalRequire = Module.prototype.require; -Module.prototype.require = function(req){ - module = Mocks.mocks(req); - if (module == null) { - return originalRequire.apply(this, arguments); - } - else { - return module; - } +Module.prototype.require = function (req) { + module = Mocks.mocks(req); + if (module == null) { + return originalRequire.apply(this, arguments); + } else { + return module; + } }; - diff --git a/test/jest/jest.config.js b/test/jest/jest.config.js index 4aeaff7631..ed246bcda0 100644 --- a/test/jest/jest.config.js +++ b/test/jest/jest.config.js @@ -1,23 +1,20 @@ module.exports = { - preset: 'react-native', - setupFilesAfterEnv: [ - '@testing-library/jest-native/extend-expect', - '../test/jest/jestSetup.js', - ], - setupFiles: [], - testPathIgnorePatterns: [], - moduleNameMapper: { - '^[@./a-zA-Z0-9$_-]+\\.(png|jpg|jpeg|gif)$': - '/../node_modules/react-native/Libraries/Image/RelativeImageStub', - }, - testTimeout: 60000, - transformIgnorePatterns: [ - '/node_modules/(?!(@react-native|react-native-haptic-feedback|react-native-redash|react-native-image-crop-picker|@react-native-community|react-native-linear-gradient|react-native-background-timer|react-native|rn-emoji-keyboard|react-native-languages|react-native-shake|react-native-reanimated|react-native-redash|react-native-permissions|@react-native-community/blur|react-native-static-safe-area-insets)/).*/', - ], - globals: { - __TEST__: true, - }, - testEnvironment: 'node', - rootDir: '../../component-spec', - testMatch: ['**/*__tests__*', '**/*.component_spec.js'], + preset: 'react-native', + setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect', '../test/jest/jestSetup.js'], + setupFiles: [], + testPathIgnorePatterns: [], + moduleNameMapper: { + '^[@./a-zA-Z0-9$_-]+\\.(png|jpg|jpeg|gif)$': + '/../node_modules/react-native/Libraries/Image/RelativeImageStub', + }, + testTimeout: 60000, + transformIgnorePatterns: [ + '/node_modules/(?!(@react-native|react-native-haptic-feedback|react-native-redash|react-native-image-crop-picker|@react-native-community|react-native-linear-gradient|react-native-background-timer|react-native|rn-emoji-keyboard|react-native-languages|react-native-shake|react-native-reanimated|react-native-redash|react-native-permissions|@react-native-community/blur|react-native-static-safe-area-insets)/).*/', + ], + globals: { + __TEST__: true, + }, + testEnvironment: 'node', + rootDir: '../../component-spec', + testMatch: ['**/*__tests__*', '**/*.component_spec.js'], }; diff --git a/test/jest/jestSetup.js b/test/jest/jestSetup.js index 67aae1382a..100062c057 100644 --- a/test/jest/jestSetup.js +++ b/test/jest/jestSetup.js @@ -1,5 +1,5 @@ const WebSocket = require('ws'); -const {NativeModules} = require('react-native'); +const { NativeModules } = require('react-native'); require('@react-native-async-storage/async-storage/jest/async-storage-mock'); require('react-native-gesture-handler/jestSetup'); @@ -8,82 +8,80 @@ require('react-native-reanimated/lib/reanimated2/jestUtils').setUpTests(); jest.mock('@react-native-async-storage/async-storage', () => mockAsyncStorage); jest.mock('react-native-navigation', () => ({ - getNavigationConstants: () => ({constants: []}), - Navigation: {constants: async () => {}}, + getNavigationConstants: () => ({ constants: [] }), + Navigation: { constants: async () => {} }, })); jest.mock('react-native-background-timer', () => ({})); jest.mock('react-native-languages', () => ({ - RNLanguages: { - language: 'en', - languages: ['en'], - }, - default: { - language: 'en', - locale: 'en', - }, + RNLanguages: { + language: 'en', + languages: ['en'], + }, + default: { + language: 'en', + locale: 'en', + }, })); -jest.mock('react-native-permissions', () => - require('react-native-permissions/mock'), -); +jest.mock('react-native-permissions', () => require('react-native-permissions/mock')); jest.mock('@react-native-community/audio-toolkit', () => ({ - Recorder: jest.fn().mockImplementation(() => ({ - prepare: jest.fn(), - record: jest.fn(), - toggleRecord: jest.fn(), - pause: jest.fn(), - stop: jest.fn(), - on: jest.fn(), - })), - Player: jest.fn().mockImplementation(() => ({ - prepare: jest.fn(), - playPause: jest.fn(), - play: jest.fn(), - pause: jest.fn(), - stop: jest.fn(), - seek: jest.fn(), - on: jest.fn(), - })), - MediaStates: { - DESTROYED: -2, - ERROR: -1, - IDLE: 0, - PREPARING: 1, - PREPARED: 2, - SEEKING: 3, - PLAYING: 4, - RECORDING: 4, - PAUSED: 5, - }, - PlaybackCategories: { - Playback: 1, - Ambient: 2, - SoloAmbient: 3 - }, + Recorder: jest.fn().mockImplementation(() => ({ + prepare: jest.fn(), + record: jest.fn(), + toggleRecord: jest.fn(), + pause: jest.fn(), + stop: jest.fn(), + on: jest.fn(), + })), + Player: jest.fn().mockImplementation(() => ({ + prepare: jest.fn(), + playPause: jest.fn(), + play: jest.fn(), + pause: jest.fn(), + stop: jest.fn(), + seek: jest.fn(), + on: jest.fn(), + })), + MediaStates: { + DESTROYED: -2, + ERROR: -1, + IDLE: 0, + PREPARING: 1, + PREPARED: 2, + SEEKING: 3, + PLAYING: 4, + RECORDING: 4, + PAUSED: 5, + }, + PlaybackCategories: { + Playback: 1, + Ambient: 2, + SoloAmbient: 3, + }, })); -jest.mock("i18n-js", () => ({ - ...jest.requireActual("i18n-js"), - t: (label) => `tx:${label}` +jest.mock('i18n-js', () => ({ + ...jest.requireActual('i18n-js'), + t: (label) => `tx:${label}`, })); -jest.mock("react-native-blob-util", () => ({ - default: { - config: jest.fn().mockReturnValue({ - fetch: jest.fn() - }) - } +jest.mock('react-native-blob-util', () => ({ + default: { + config: jest.fn().mockReturnValue({ + fetch: jest.fn(), + }), + }, })); NativeModules.ReactLocalization = { - language: 'en', - locale: 'en', + language: 'en', + locale: 'en', }; global.navigator = { - userAgent: 'node', + userAgent: 'node', }; global.WebSocket = WebSocket; diff --git a/visual-test/environment.js b/visual-test/environment.js index 05f5f65935..ff7e933733 100644 --- a/visual-test/environment.js +++ b/visual-test/environment.js @@ -1,23 +1,19 @@ -const { - DetoxCircusEnvironment, - SpecReporter, - WorkerAssignReporter, - } = require('detox/runners/jest-circus'); - - class CustomDetoxEnvironment extends DetoxCircusEnvironment { - constructor(config, context) { - super(config, context); - - // Can be safely removed, if you are content with the default value (=300000ms) - this.initTimeout = 300000; - - // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level. - // This is strictly optional. - this.registerListeners({ - SpecReporter, - WorkerAssignReporter, - }); - } +const { DetoxCircusEnvironment, SpecReporter, WorkerAssignReporter } = require('detox/runners/jest-circus'); + +class CustomDetoxEnvironment extends DetoxCircusEnvironment { + constructor(config, context) { + super(config, context); + + // Can be safely removed, if you are content with the default value (=300000ms) + this.initTimeout = 300000; + + // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level. + // This is strictly optional. + this.registerListeners({ + SpecReporter, + WorkerAssignReporter, + }); } - - module.exports = CustomDetoxEnvironment; \ No newline at end of file +} + +module.exports = CustomDetoxEnvironment; diff --git a/visual-test/global-setup.js b/visual-test/global-setup.js index 8eb51a9c88..a9c5b0c028 100644 --- a/visual-test/global-setup.js +++ b/visual-test/global-setup.js @@ -28,13 +28,13 @@ function downloadTestButlerAPK() { function resolveSelectedConfiguration() { const { configurations } = require('../.detoxrc'); - const configName = process.env.DETOX_CONFIGURATION; + const configName = process.env.DETOX_CONFIGURATION; return configurations[configName]; } // TODO eventually, this should be made available by Detox more explicitly function isAndroidConfig(config) { - return [config.type, process.env.DETOX_CONFIGURATION, config.device].some(s => `${s}`.includes('android')); + return [config.type, process.env.DETOX_CONFIGURATION, config.device].some((s) => `${s}`.includes('android')); } -module.exports = globalSetup; \ No newline at end of file +module.exports = globalSetup; diff --git a/visual-test/setup.js b/visual-test/setup.js index 2ab52a0186..6bfb7ac960 100644 --- a/visual-test/setup.js +++ b/visual-test/setup.js @@ -4,12 +4,12 @@ const path = require('path'); const kebabCase = require('lodash/kebabCase'); const expect = require('expect'); - const toMatchImage = configureToMatchImageSnapshot({ - comparisonMethod: 'ssim', failureThreshold: 0.002, failureThresholdType: 'percent' + comparisonMethod: 'ssim', + failureThreshold: 0.002, + failureThresholdType: 'percent', }); - expect.extend({ toMatchImage }); expect.extend({ @@ -23,18 +23,17 @@ expect.extend({ const { testPath, currentTestName } = this; const customSnapshotsDir = path.join(path.dirname(testPath), SNAPSHOTS_DIR); - const customSnapshotIdentifier = kebabCase(`${path.basename(testPath)}-${currentTestName}-${screenName}`) + const customSnapshotIdentifier = kebabCase(`${path.basename(testPath)}-${currentTestName}-${screenName}`); const tempPath = await device.takeScreenshot(screenName); const image = fs.readFileSync(tempPath); expect(image).toMatchImage({ customSnapshotIdentifier, customSnapshotsDir }); - return { pass: true } + return { pass: true }; }, }); -global.jestExpect = expect - +global.jestExpect = expect; beforeAll(async () => { await device.launchApp(); diff --git a/yarn.lock b/yarn.lock index 65fd6f8d81..ab9436e8d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8664,6 +8664,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier@^2.8.8: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + pretty-format@^25.5.0: version "25.5.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a"