From 42e12415e3fcb153701f2f694ae826fc118ac6d6 Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Fri, 26 May 2023 18:58:22 +0500 Subject: [PATCH 01/15] added hive-uri package --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 6f733de9bc..5d9f5ee83c 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,7 @@ "dns.js": "^1.0.1", "domain-browser": "^1.1.1", "events": "^1.0.0", + "hive-uri": "^0.2.4", "hivesigner": "^3.2.7", "https-browserify": "~0.0.0", "intl": "^1.2.5", From 53b7446261b37c5518db85a67cf29ffc60722153 Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Fri, 26 May 2023 20:39:09 +0500 Subject: [PATCH 02/15] added hive uri scan in qr scanner --- src/components/qrModal/qrModalView.tsx | 65 +++++++++++++++++--------- src/utils/hive-uri.ts | 4 ++ 2 files changed, 47 insertions(+), 22 deletions(-) create mode 100644 src/utils/hive-uri.ts diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index de51ec5808..115a11846a 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -11,6 +11,8 @@ import { toggleQRModal } from '../../redux/actions/uiAction'; import { deepLinkParser } from '../../utils/deepLinkParser'; import RootNavigation from '../../navigation/rootNavigation'; import getWindowDimensions from '../../utils/getWindowDimensions'; +import { isHiveUri } from '../../utils/hive-uri'; +const hiveuri = require('hive-uri'); export interface QRModalProps {} @@ -29,9 +31,9 @@ export const QRModal = ({}: QRModalProps) => { useEffect(() => { if (isVisibleQRModal) { requestCameraPermission(); - sheetModalRef.current.show(); + sheetModalRef?.current?.show(); } else { - sheetModalRef.current.hide(); + sheetModalRef?.current?.hide(); } }, [isVisibleQRModal]); @@ -97,7 +99,22 @@ export const QRModal = ({}: QRModalProps) => { const onSuccess = (e) => { setIsScannerActive(false); - _handleDeepLink(e.data); + if (isHiveUri(e.data)) { + _handleHiveUri(e.data); + } else { + _handleDeepLink(e.data); + } + }; + + const _handleHiveUri = (uri: string) => { + try { + const parsed = hiveuri.decode(uri); + setIsScannerActive(false); + _onClose(); + Alert.alert('parsed uri ', JSON.stringify(parsed)); + } catch (err) { + _showInvalidAlert(); + } }; const _handleDeepLink = async (url) => { @@ -110,27 +127,31 @@ export const QRModal = ({}: QRModalProps) => { _onClose(); RootNavigation.navigate(deepLinkData); } else { - Alert.alert( - intl.formatMessage({ id: 'qr.unsupported_alert_title' }), - intl.formatMessage({ id: 'qr.unsupported_alert_desc' }), - [ - { - text: 'Close', - onPress: () => { - _onClose(); - }, - style: 'cancel', + _showInvalidAlert(); + } + }; + + const _showInvalidAlert = () => { + Alert.alert( + intl.formatMessage({ id: 'qr.unsupported_alert_title' }), + intl.formatMessage({ id: 'qr.unsupported_alert_desc' }), + [ + { + text: 'Close', + onPress: () => { + _onClose(); }, - { - text: 'Rescan', - onPress: () => { - setIsScannerActive(true); - scannerRef.current?.reactivate(); - }, + style: 'cancel', + }, + { + text: 'Rescan', + onPress: () => { + setIsScannerActive(true); + scannerRef.current?.reactivate(); }, - ], - ); - } + }, + ], + ); }; return ( diff --git a/src/utils/hive-uri.ts b/src/utils/hive-uri.ts new file mode 100644 index 0000000000..07bf4bf5b2 --- /dev/null +++ b/src/utils/hive-uri.ts @@ -0,0 +1,4 @@ +export const isHiveUri = (uri: string) => { + let trimUri = uri.trim(); + return trimUri.startsWith('hive://'); +}; From 9598039fbfccfc6f2034fc0a97318c6e08a3e658 Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Thu, 6 Jul 2023 22:56:52 +0500 Subject: [PATCH 03/15] added vote and transfer operation support in hive uri scanner function --- src/components/qrModal/qrModalView.tsx | 190 +++++++++++++++++++++++-- src/config/locales/en-US.json | 5 +- 2 files changed, 186 insertions(+), 9 deletions(-) diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index 115a11846a..20af2f3a44 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react'; -import { ActivityIndicator, Alert, PermissionsAndroid, Platform, Text, View } from 'react-native'; +import { ActivityIndicator, Alert, PermissionsAndroid, Platform, View } from 'react-native'; import ActionSheet from 'react-native-actions-sheet'; import EStyleSheet from 'react-native-extended-stylesheet'; import QRCodeScanner from 'react-native-qrcode-scanner'; @@ -7,21 +7,32 @@ import { useIntl } from 'react-intl'; import { check, request, PERMISSIONS, RESULTS, openSettings } from 'react-native-permissions'; import styles from './qrModalStyles'; import { useAppDispatch, useAppSelector } from '../../hooks'; -import { toggleQRModal } from '../../redux/actions/uiAction'; +import { + setRcOffer, + showActionModal, + toastNotification, + toggleQRModal, +} from '../../redux/actions/uiAction'; import { deepLinkParser } from '../../utils/deepLinkParser'; import RootNavigation from '../../navigation/rootNavigation'; import getWindowDimensions from '../../utils/getWindowDimensions'; import { isHiveUri } from '../../utils/hive-uri'; -const hiveuri = require('hive-uri'); - -export interface QRModalProps {} +import { transferToken, vote } from '../../providers/hive/dhive'; +import { useUserActivityMutation } from '../../providers/queries'; +import { PointActivityIds } from '../../providers/ecency/ecency.types'; +import bugsnagInstance from '../../config/bugsnag'; +interface QRModalProps {} +const hiveuri = require('hive-uri'); const screenHeight = getWindowDimensions().height; + export const QRModal = ({}: QRModalProps) => { + const userActivityMutation = useUserActivityMutation(); const dispatch = useAppDispatch(); const intl = useIntl(); const isVisibleQRModal = useAppSelector((state) => state.ui.isVisibleQRModal); const currentAccount = useAppSelector((state) => state.account.currentAccount); + const pinCode = useAppSelector((state) => state.application.pin); const [isScannerActive, setIsScannerActive] = useState(true); const [isProcessing, setIsProcessing] = useState(false); @@ -108,18 +119,181 @@ export const QRModal = ({}: QRModalProps) => { const _handleHiveUri = (uri: string) => { try { - const parsed = hiveuri.decode(uri); + // const uri = + // 'hive://sign/op/WyJ0cmFuc2ZlciIseyJmcm9tIjoiZGVtby5jb20iLCJ0byI6ImFsaXNleWFsdmkiLCJhbW91bnQiOiIwLjAwMiBISVZFIiwibWVtbyI6InRlc3RpbmcgaGl2ZSB1cmkifV0.'; + // const uri = + // 'hive://sign/op/WyJ2b3RlIix7InZvdGVyIjoiZGVtby5jb20iLCJhdXRob3IiOiJhbGlzZXlhbHZpIiwicGVybWxpbmsiOiJkZW1vLXBvc3QiLCJ3ZWlnaHQiOiIxMDAifV0.'; + const { params, tx } = hiveuri.decode(uri); setIsScannerActive(false); _onClose(); - Alert.alert('parsed uri ', JSON.stringify(parsed)); + // console.log('parsedHiveUri : ', JSON.stringify(tx, null, 2), params); + // Alert.alert('parsed uri ', JSON.stringify(tx)); + if (tx.operations && tx.operations.length === 1) { + _handleHiveUriOperation(tx.operations[0]); + } else { + Alert.alert( + 'Error while parsing operation', + 'looka like operation object is not encoded properly ', + ); + } } catch (err) { _showInvalidAlert(); } }; + const _handleHiveUriOperation = (hiveUriOperation: any) => { + if (hiveUriOperation && hiveUriOperation.length > 0) { + switch (hiveUriOperation[0]) { + case 'vote': + _handleVoteOperation(hiveUriOperation[1]); + break; + case 'transfer': + _handleTransferOperation(hiveUriOperation[1]); + break; + default: + Alert.alert('Unsupported Operation!', 'This operation is not currently supported'); + } + } + }; + + const _handleVoteOperation = (operation: any) => { + const { voter, author, permlink, weight } = operation; + + dispatch( + showActionModal({ + title: intl.formatMessage({ + id: 'qr.confirmTransaction', + }), + body: JSON.stringify(operation), + buttons: [ + { + text: intl.formatMessage({ + id: 'qr.cancel', + }), + onPress: () => {}, + style: 'cancel', + }, + { + text: intl.formatMessage({ + id: 'qr.approve', + }), + onPress: () => { + vote(currentAccount, pinCode, author, permlink, parseInt(weight)) + .then((response) => { + console.log('Vote response: ', response); + // record user points + userActivityMutation.mutate({ + pointsTy: PointActivityIds.VOTE, + transactionId: response.id, + }); + + if (!response || !response.id) { + dispatch( + toastNotification( + intl.formatMessage( + { id: 'alert.something_wrong_msg' }, + { + message: intl.formatMessage({ + id: 'alert.invalid_response', + }), + }, + ), + ), + ); + + return; + } + dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); + }) + .catch((err) => { + if ( + err && + err.response && + err.response.jse_shortmsg && + err.response.jse_shortmsg.includes('wait to transact') + ) { + // when RC is not enough, offer boosting account + // setIsVoted(false); + dispatch(setRcOffer(true)); + } else if ( + err && + err.jse_shortmsg && + err.jse_shortmsg.includes('wait to transact') + ) { + // when RC is not enough, offer boosting account + // setIsVoted(false); + dispatch(setRcOffer(true)); + } else { + // // when voting with same percent or other errors + let errMsg = ''; + if (err.message && err.message.indexOf(':') > 0) { + errMsg = err.message.split(': ')[1]; + } else { + errMsg = err.jse_shortmsg || err.error_description || err.message; + } + dispatch( + toastNotification( + intl.formatMessage( + { id: 'alert.something_wrong_msg' }, + { message: errMsg }, + ), + ), + ); + } + }); + }, + }, + ], + }), + ); + }; + + const _handleTransferOperation = (operation: any) => { + const { from, to, amount, memo } = operation; + const data = { + from, + destination: to, + amount, + memo, + }; + + dispatch( + showActionModal({ + title: intl.formatMessage({ + id: 'qr.confirmTransaction', + }), + body: JSON.stringify(operation), + buttons: [ + { + text: intl.formatMessage({ + id: 'qr.cancel', + }), + onPress: () => {}, + style: 'cancel', + }, + { + text: intl.formatMessage({ + id: 'qr.approve', + }), + onPress: () => { + transferToken(currentAccount, pinCode, data) + .then(() => { + dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); + }) + .catch((err) => { + bugsnagInstance.notify(err); + dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); + }); + }, + }, + ], + }), + ); + }; + const _handleDeepLink = async (url) => { setIsProcessing(true); - const deepLinkData = await deepLinkParser(url, currentAccount); + const deepLinkData = await deepLinkParser(url); const { name, params, key } = deepLinkData || {}; setIsProcessing(false); if (name && params && key) { diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index 908c27648d..663823ba03 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -1014,7 +1014,10 @@ "open": "Open URL", "detected_url": "Detected URL", "unsupported_alert_title": "Unsupported URL!", - "unsupported_alert_desc": "Please scan a valid ecency url." + "unsupported_alert_desc": "Please scan a valid ecency url.", + "confirmTransaction": "Confirm transaction", + "approve": "Approve", + "cancel": "Cancel" }, "history": { "edit": "Edit History", From 2450ab84e79e923ab9d27b9bd4f6f3ce98af2377 Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Mon, 17 Jul 2023 21:07:42 +0500 Subject: [PATCH 04/15] wip --- ios/Podfile.lock | 4 +- src/components/qrModal/qrModalView.tsx | 191 ++----------------------- src/providers/hive/dhive.js | 53 +++++++ yarn.lock | 8 +- 4 files changed, 74 insertions(+), 182 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6932f5f94f..538344ec19 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -573,7 +573,7 @@ PODS: - React-Core - RNCPushNotificationIOS (1.10.1): - React-Core - - RNDeviceInfo (10.4.0): + - RNDeviceInfo (10.8.0): - React-Core - RNFastImage (8.6.3): - React-Core @@ -1078,7 +1078,7 @@ SPEC CHECKSUMS: RNCAsyncStorage: 8616bd5a58af409453ea4e1b246521bb76578d60 RNCClipboard: 3f0451a8100393908bea5c5c5b16f96d45f30bfc RNCPushNotificationIOS: 87b8d16d3ede4532745e05b03c42cff33a36cc45 - RNDeviceInfo: 749f2e049dcd79e2e44f134f66b73a06951b5066 + RNDeviceInfo: 5795b418ed3451ebcaf39384e6cf51f60cb931c9 RNFastImage: 5c9c9fed9c076e521b3f509fe79e790418a544e8 RNFBAnalytics: ea1421b49a0bf5e5bb7274df4a330713d0e85d2e RNFBApp: e4439717c23252458da2b41b81b4b475c86f90c4 diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index 20af2f3a44..13fb2482fe 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -7,27 +7,18 @@ import { useIntl } from 'react-intl'; import { check, request, PERMISSIONS, RESULTS, openSettings } from 'react-native-permissions'; import styles from './qrModalStyles'; import { useAppDispatch, useAppSelector } from '../../hooks'; -import { - setRcOffer, - showActionModal, - toastNotification, - toggleQRModal, -} from '../../redux/actions/uiAction'; +import { toastNotification, toggleQRModal } from '../../redux/actions/uiAction'; import { deepLinkParser } from '../../utils/deepLinkParser'; import RootNavigation from '../../navigation/rootNavigation'; import getWindowDimensions from '../../utils/getWindowDimensions'; import { isHiveUri } from '../../utils/hive-uri'; -import { transferToken, vote } from '../../providers/hive/dhive'; -import { useUserActivityMutation } from '../../providers/queries'; -import { PointActivityIds } from '../../providers/ecency/ecency.types'; +import { handleHiveUriOperation } from '../../providers/hive/dhive'; import bugsnagInstance from '../../config/bugsnag'; interface QRModalProps {} -const hiveuri = require('hive-uri'); const screenHeight = getWindowDimensions().height; export const QRModal = ({}: QRModalProps) => { - const userActivityMutation = useUserActivityMutation(); const dispatch = useAppDispatch(); const intl = useIntl(); const isVisibleQRModal = useAppSelector((state) => state.ui.isVisibleQRModal); @@ -117,180 +108,28 @@ export const QRModal = ({}: QRModalProps) => { } }; - const _handleHiveUri = (uri: string) => { + const _handleHiveUri = () => { try { - // const uri = - // 'hive://sign/op/WyJ0cmFuc2ZlciIseyJmcm9tIjoiZGVtby5jb20iLCJ0byI6ImFsaXNleWFsdmkiLCJhbW91bnQiOiIwLjAwMiBISVZFIiwibWVtbyI6InRlc3RpbmcgaGl2ZSB1cmkifV0.'; - // const uri = - // 'hive://sign/op/WyJ2b3RlIix7InZvdGVyIjoiZGVtby5jb20iLCJhdXRob3IiOiJhbGlzZXlhbHZpIiwicGVybWxpbmsiOiJkZW1vLXBvc3QiLCJ3ZWlnaHQiOiIxMDAifV0.'; - const { params, tx } = hiveuri.decode(uri); + const uri = + 'hive://sign/op/WyJ0cmFuc2ZlciIseyJmcm9tIjoiZGVtby5jb20iLCJ0byI6ImFsaXNleWFsdmkiLCJhbW91bnQiOiIwLjAwMiBISVZFIiwibWVtbyI6InRlc3RpbmcgaGl2ZSB1cmkifV0.'; + setIsScannerActive(false); _onClose(); - // console.log('parsedHiveUri : ', JSON.stringify(tx, null, 2), params); + handleHiveUriOperation(currentAccount, pinCode, uri) + .then(() => { + dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); + }) + .catch((err) => { + bugsnagInstance.notify(err); + dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); + }); + // console.log('parsedHiveUri : ', JSON.stringify(tx, null, 2)); // Alert.alert('parsed uri ', JSON.stringify(tx)); - if (tx.operations && tx.operations.length === 1) { - _handleHiveUriOperation(tx.operations[0]); - } else { - Alert.alert( - 'Error while parsing operation', - 'looka like operation object is not encoded properly ', - ); - } } catch (err) { _showInvalidAlert(); } }; - const _handleHiveUriOperation = (hiveUriOperation: any) => { - if (hiveUriOperation && hiveUriOperation.length > 0) { - switch (hiveUriOperation[0]) { - case 'vote': - _handleVoteOperation(hiveUriOperation[1]); - break; - case 'transfer': - _handleTransferOperation(hiveUriOperation[1]); - break; - default: - Alert.alert('Unsupported Operation!', 'This operation is not currently supported'); - } - } - }; - - const _handleVoteOperation = (operation: any) => { - const { voter, author, permlink, weight } = operation; - - dispatch( - showActionModal({ - title: intl.formatMessage({ - id: 'qr.confirmTransaction', - }), - body: JSON.stringify(operation), - buttons: [ - { - text: intl.formatMessage({ - id: 'qr.cancel', - }), - onPress: () => {}, - style: 'cancel', - }, - { - text: intl.formatMessage({ - id: 'qr.approve', - }), - onPress: () => { - vote(currentAccount, pinCode, author, permlink, parseInt(weight)) - .then((response) => { - console.log('Vote response: ', response); - // record user points - userActivityMutation.mutate({ - pointsTy: PointActivityIds.VOTE, - transactionId: response.id, - }); - - if (!response || !response.id) { - dispatch( - toastNotification( - intl.formatMessage( - { id: 'alert.something_wrong_msg' }, - { - message: intl.formatMessage({ - id: 'alert.invalid_response', - }), - }, - ), - ), - ); - - return; - } - dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); - }) - .catch((err) => { - if ( - err && - err.response && - err.response.jse_shortmsg && - err.response.jse_shortmsg.includes('wait to transact') - ) { - // when RC is not enough, offer boosting account - // setIsVoted(false); - dispatch(setRcOffer(true)); - } else if ( - err && - err.jse_shortmsg && - err.jse_shortmsg.includes('wait to transact') - ) { - // when RC is not enough, offer boosting account - // setIsVoted(false); - dispatch(setRcOffer(true)); - } else { - // // when voting with same percent or other errors - let errMsg = ''; - if (err.message && err.message.indexOf(':') > 0) { - errMsg = err.message.split(': ')[1]; - } else { - errMsg = err.jse_shortmsg || err.error_description || err.message; - } - dispatch( - toastNotification( - intl.formatMessage( - { id: 'alert.something_wrong_msg' }, - { message: errMsg }, - ), - ), - ); - } - }); - }, - }, - ], - }), - ); - }; - - const _handleTransferOperation = (operation: any) => { - const { from, to, amount, memo } = operation; - const data = { - from, - destination: to, - amount, - memo, - }; - - dispatch( - showActionModal({ - title: intl.formatMessage({ - id: 'qr.confirmTransaction', - }), - body: JSON.stringify(operation), - buttons: [ - { - text: intl.formatMessage({ - id: 'qr.cancel', - }), - onPress: () => {}, - style: 'cancel', - }, - { - text: intl.formatMessage({ - id: 'qr.approve', - }), - onPress: () => { - transferToken(currentAccount, pinCode, data) - .then(() => { - dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); - }) - .catch((err) => { - bugsnagInstance.notify(err); - dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); - }); - }, - }, - ], - }), - ); - }; - const _handleDeepLink = async (url) => { setIsProcessing(true); const deepLinkData = await deepLinkParser(url); diff --git a/src/providers/hive/dhive.js b/src/providers/hive/dhive.js index 7615260c1e..608b0ee171 100644 --- a/src/providers/hive/dhive.js +++ b/src/providers/hive/dhive.js @@ -44,6 +44,7 @@ import bugsnagInstance from '../../config/bugsnag'; import bugsnapInstance from '../../config/bugsnag'; import { makeJsonMetadataReply } from '../../utils/editor'; +const hiveuri = require('hive-uri'); global.Buffer = global.Buffer || require('buffer').Buffer; const DEFAULT_SERVER = SERVER_LIST; @@ -2075,3 +2076,55 @@ export const votingPower = (account) => { return percentage / 100; }; /* eslint-enable */ + +export const handleHiveUriOperation = async ( + currentAccount: any, + pin: any, + hiveUri: string, +): Promise => { + try { + const digitPinCode = getDigitPinCode(pin); + const key = getAnyPrivateKey(currentAccount.local, digitPinCode); + const privateKey = PrivateKey.fromString(key); + + const { head_block_number, head_block_id, time } = await getDynamicGlobalProperties(); + const ref_block_num = head_block_number & 0xffff; + const ref_block_prefix = Buffer.from(head_block_id, 'hex').readUInt32LE(4); + const expireTime = 60 * 1000; + const chainId = Buffer.from( + 'beeab0de00000000000000000000000000000000000000000000000000000000', + 'hex', + ); + const expiration = new Date(new Date(`${time}Z`).getTime() + expireTime) + .toISOString() + .slice(0, -5); + const extensions = []; + const parsed = hiveuri.decode(hiveUri); + // console.log('parsed.tx : ', JSON.stringify(parsed.tx, null, 2)); + // resolve the decoded tx and params to a signable tx + let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, { + // e.g. from a get_dynamic_global_properties call + ref_block_num, + ref_block_prefix, + expiration, + // accounts we are able to sign for + signers: currentAccount.name, + // selected signer if none is asked for by the params + preferred_signer: currentAccount.name, + }); + + // console.log('tx : ', JSON.stringify(tx, null, 2)); + const transaction = await cryptoUtils.signTransaction(tx, privateKey, chainId); + const trxId = generateTrxId(transaction); + const resultHive = await client.broadcast.call('broadcast_transaction', [transaction]); + const result = Object.assign({ id: trxId }, resultHive); + // console.log('result : ', JSON.stringify(result, null, 2)); + return result; + } catch (err) { + bugsnagInstance.notify(err, (event) => { + event.context = 'handle-hive-uri-operations'; + event.setMetaData('hiveUri', hiveUri); + }); + throw err; + } +}; diff --git a/yarn.lock b/yarn.lock index 875c7049b6..162aef3c38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8998,10 +8998,10 @@ react-native-date-picker@^4.2.0: dependencies: prop-types "^15.8.1" -react-native-device-info@^10.2.1: - version "10.4.0" - resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-10.4.0.tgz#9d90706641941d7da8ab7b316a37d3111eb231e4" - integrity sha512-Z37e0HtpBvfkPRgv4xN7lXpvmJyzjwCXSFTXEkw6m2UgnnIsWlOD02Avu4hJXBlIMMazaW3ZLKal3o9h3AYvCw== +react-native-device-info@^10.7.0: + version "10.8.0" + resolved "https://registry.yarnpkg.com/react-native-device-info/-/react-native-device-info-10.8.0.tgz#c331c94d65542f86bac197f223ee064c7e10a9ec" + integrity sha512-DE4/X82ZVhdcnR1Y21iTP46WSSJA/rHK3lmeqWfGGq1RKLwXTIdxmfbZZnYwryqJ+esrw2l4ND19qlgxDGby8A== react-native-draggable-flatlist@^4.0.1: version "4.0.1" From 8947fe45398e4074b89140d87c8dc542971d45fa Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Mon, 17 Jul 2023 22:49:21 +0500 Subject: [PATCH 05/15] wip --- src/components/qrModal/qrModalView.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index 13fb2482fe..8ef604dc5f 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -108,11 +108,8 @@ export const QRModal = ({}: QRModalProps) => { } }; - const _handleHiveUri = () => { + const _handleHiveUri = (uri: string) => { try { - const uri = - 'hive://sign/op/WyJ0cmFuc2ZlciIseyJmcm9tIjoiZGVtby5jb20iLCJ0byI6ImFsaXNleWFsdmkiLCJhbW91bnQiOiIwLjAwMiBISVZFIiwibWVtbyI6InRlc3RpbmcgaGl2ZSB1cmkifV0.'; - setIsScannerActive(false); _onClose(); handleHiveUriOperation(currentAccount, pinCode, uri) @@ -123,8 +120,6 @@ export const QRModal = ({}: QRModalProps) => { bugsnagInstance.notify(err); dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); }); - // console.log('parsedHiveUri : ', JSON.stringify(tx, null, 2)); - // Alert.alert('parsed uri ', JSON.stringify(tx)); } catch (err) { _showInvalidAlert(); } From 262b91055dfce7ac1c68d8d743f5766ba5b6ee51 Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Wed, 19 Jul 2023 02:55:27 +0500 Subject: [PATCH 06/15] post process tx to avoid stringified ref block nums --- src/providers/hive/dhive.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/providers/hive/dhive.js b/src/providers/hive/dhive.js index 608b0ee171..2d656cce9d 100644 --- a/src/providers/hive/dhive.js +++ b/src/providers/hive/dhive.js @@ -2099,13 +2099,12 @@ export const handleHiveUriOperation = async ( .toISOString() .slice(0, -5); const extensions = []; - const parsed = hiveuri.decode(hiveUri); - // console.log('parsed.tx : ', JSON.stringify(parsed.tx, null, 2)); + + + const parsed = hiveuri.decode(hiveUri) // resolve the decoded tx and params to a signable tx let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, { - // e.g. from a get_dynamic_global_properties call - ref_block_num, - ref_block_prefix, + expiration, // accounts we are able to sign for signers: currentAccount.name, @@ -2113,8 +2112,14 @@ export const handleHiveUriOperation = async ( preferred_signer: currentAccount.name, }); - // console.log('tx : ', JSON.stringify(tx, null, 2)); - const transaction = await cryptoUtils.signTransaction(tx, privateKey, chainId); + + //inject raw ref_block_num and ref_block_prefex to avoid string converstion by hiveuri.resolveTransaction + // e.g. from a get_dynamic_global_properties call + tx.ref_block_num = ref_block_num; + tx.ref_block_prefix = ref_block_prefix; + + + const transaction = cryptoUtils.signTransaction(tx, privateKey, chainId); const trxId = generateTrxId(transaction); const resultHive = await client.broadcast.call('broadcast_transaction', [transaction]); const result = Object.assign({ id: trxId }, resultHive); From 2a7a24fe6209beb7fff25ba459afb9479e544819 Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Wed, 9 Aug 2023 16:17:40 +0500 Subject: [PATCH 07/15] added action modal with transaction info --- .../container/actionModalContainer.tsx | 1 + .../actionModal/view/actionModalView.tsx | 3 +- src/components/qrModal/qrModalStyles.ts | 26 +++++++ src/components/qrModal/qrModalView.tsx | 71 ++++++++++++++++--- src/providers/hive/dhive.js | 2 +- 5 files changed, 90 insertions(+), 13 deletions(-) diff --git a/src/components/actionModal/container/actionModalContainer.tsx b/src/components/actionModal/container/actionModalContainer.tsx index ebb9a28253..c428e01653 100644 --- a/src/components/actionModal/container/actionModalContainer.tsx +++ b/src/components/actionModal/container/actionModalContainer.tsx @@ -17,6 +17,7 @@ export interface ActionModalData { headerImage?: Source; onClosed: () => void; headerContent?: React.ReactNode; + bodyContent?: React.ReactNode; } const ActionModalContainer = ({ navigation }) => { diff --git a/src/components/actionModal/view/actionModalView.tsx b/src/components/actionModal/view/actionModalView.tsx index d130b1e6c5..e20d96be5c 100644 --- a/src/components/actionModal/view/actionModalView.tsx +++ b/src/components/actionModal/view/actionModalView.tsx @@ -38,7 +38,7 @@ const ActionModalView = ({ onClose, data }: ActionModalViewProps, ref) => { return null; } - const { title, body, buttons, headerImage, para, headerContent } = data; + const { title, body, buttons, headerImage, para, headerContent, bodyContent } = data; const _renderContent = ( @@ -49,6 +49,7 @@ const ActionModalView = ({ onClose, data }: ActionModalViewProps, ref) => { {title} + {bodyContent && bodyContent} {!!body && ( <> {body} diff --git a/src/components/qrModal/qrModalStyles.ts b/src/components/qrModal/qrModalStyles.ts index 72fb12026c..11507a4950 100644 --- a/src/components/qrModal/qrModalStyles.ts +++ b/src/components/qrModal/qrModalStyles.ts @@ -1,3 +1,4 @@ +import { TextStyle, ViewStyle } from 'react-native'; import EStyleSheet from 'react-native-extended-stylesheet'; export default EStyleSheet.create({ @@ -36,4 +37,29 @@ export default EStyleSheet.create({ alignItems: 'center', }, activityIndicator: {}, + transactionBodyContainer: { + borderWidth: 1, + borderColor: '$borderColor', + borderRadius: 8, + padding: 8, + marginVertical: 10, + } as ViewStyle, + transactionRow: { + borderColor: 'red', + flexDirection: 'row', + alignItems: 'center', + marginVertical: 4, + } as ViewStyle, + transactionItem1: { + color: '$primaryBlack', + fontSize: 16, + fontWeight: '700', + flex: 1, + } as TextStyle, + transactionItem2: { + color: '$primaryBlack', + fontSize: 16, + fontWeight: '400', + flex: 1, + } as TextStyle, }); diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index 8ef604dc5f..b81aad11d6 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useState } from 'react'; -import { ActivityIndicator, Alert, PermissionsAndroid, Platform, View } from 'react-native'; +import { ActivityIndicator, Alert, PermissionsAndroid, Platform, View, Text } from 'react-native'; import ActionSheet from 'react-native-actions-sheet'; import EStyleSheet from 'react-native-extended-stylesheet'; import QRCodeScanner from 'react-native-qrcode-scanner'; @@ -7,16 +7,18 @@ import { useIntl } from 'react-intl'; import { check, request, PERMISSIONS, RESULTS, openSettings } from 'react-native-permissions'; import styles from './qrModalStyles'; import { useAppDispatch, useAppSelector } from '../../hooks'; -import { toastNotification, toggleQRModal } from '../../redux/actions/uiAction'; +import { showActionModal, toastNotification, toggleQRModal } from '../../redux/actions/uiAction'; import { deepLinkParser } from '../../utils/deepLinkParser'; import RootNavigation from '../../navigation/rootNavigation'; import getWindowDimensions from '../../utils/getWindowDimensions'; import { isHiveUri } from '../../utils/hive-uri'; import { handleHiveUriOperation } from '../../providers/hive/dhive'; import bugsnagInstance from '../../config/bugsnag'; +import { get, isArray } from 'lodash'; -interface QRModalProps {} +const hiveuri = require('hive-uri'); const screenHeight = getWindowDimensions().height; +interface QRModalProps {} export const QRModal = ({}: QRModalProps) => { const dispatch = useAppDispatch(); @@ -112,14 +114,45 @@ export const QRModal = ({}: QRModalProps) => { try { setIsScannerActive(false); _onClose(); - handleHiveUriOperation(currentAccount, pinCode, uri) - .then(() => { - dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); - }) - .catch((err) => { - bugsnagInstance.notify(err); - dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); - }); + const parsed = hiveuri.decode(uri); + // resolve the decoded tx and params to a signable tx + let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, { + signers: currentAccount.name, + preferred_signer: currentAccount.name, + }); + const operations = get(tx, 'operations', []); + dispatch( + showActionModal({ + title: intl.formatMessage({ + id: 'qr.confirmTransaction', + }), + bodyContent: _checkOpsArray(operations) ? _renderActionModalBody(operations[0]) : null, + buttons: [ + { + text: intl.formatMessage({ + id: 'qr.cancel', + }), + onPress: () => {}, + style: 'cancel', + }, + { + text: intl.formatMessage({ + id: 'qr.approve', + }), + onPress: () => { + handleHiveUriOperation(currentAccount, pinCode, uri) + .then(() => { + dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); + }) + .catch((err) => { + bugsnagInstance.notify(err); + dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); + }); + }, + }, + ], + }), + ); } catch (err) { _showInvalidAlert(); } @@ -139,6 +172,22 @@ export const QRModal = ({}: QRModalProps) => { } }; + // check operation array is valid and is a single operation array + const _checkOpsArray = (ops) => { + return ops && isArray(ops) && ops.length === 1 && isArray(ops[0]) && ops[0].length === 2; + }; + + const _renderTransactionInfoRow = (item: any) => ( + + {item[0]} + {item[1]} + + ); + const _renderActionModalBody = (operations: any) => ( + + {Object.entries(operations[1]).map((item) => _renderTransactionInfoRow(item))} + + ); const _showInvalidAlert = () => { Alert.alert( intl.formatMessage({ id: 'qr.unsupported_alert_title' }), diff --git a/src/providers/hive/dhive.js b/src/providers/hive/dhive.js index a9796995ab..8ea88d7111 100644 --- a/src/providers/hive/dhive.js +++ b/src/providers/hive/dhive.js @@ -2123,7 +2123,7 @@ export const handleHiveUriOperation = async ( tx.ref_block_num = ref_block_num; tx.ref_block_prefix = ref_block_prefix; - + // console.log('tx : ', JSON.stringify(tx, null, 2)); const transaction = cryptoUtils.signTransaction(tx, privateKey, chainId); const trxId = generateTrxId(transaction); const resultHive = await client.broadcast.call('broadcast_transaction', [transaction]); From ac37a948376325d9c45ab4f09cf4c0076fc36a7b Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Thu, 10 Aug 2023 20:35:14 +0500 Subject: [PATCH 08/15] show login alert if logged out user tries to scan hive uri --- src/components/qrModal/qrModalView.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index b81aad11d6..f6e83ab571 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -15,6 +15,7 @@ import { isHiveUri } from '../../utils/hive-uri'; import { handleHiveUriOperation } from '../../providers/hive/dhive'; import bugsnagInstance from '../../config/bugsnag'; import { get, isArray } from 'lodash'; +import showLoginAlert from '../../utils/showLoginAlert'; const hiveuri = require('hive-uri'); const screenHeight = getWindowDimensions().height; @@ -26,6 +27,7 @@ export const QRModal = ({}: QRModalProps) => { const isVisibleQRModal = useAppSelector((state) => state.ui.isVisibleQRModal); const currentAccount = useAppSelector((state) => state.account.currentAccount); const pinCode = useAppSelector((state) => state.application.pin); + const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn); const [isScannerActive, setIsScannerActive] = useState(true); const [isProcessing, setIsProcessing] = useState(false); @@ -114,6 +116,10 @@ export const QRModal = ({}: QRModalProps) => { try { setIsScannerActive(false); _onClose(); + if (!isLoggedIn) { + showLoginAlert({ intl }); + return; + } const parsed = hiveuri.decode(uri); // resolve the decoded tx and params to a signable tx let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, { From 722c61dfbc14d86a64780a18a6329e0ae894a8fa Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Thu, 10 Aug 2023 22:04:34 +0500 Subject: [PATCH 09/15] * added webview modal component * handle hive uri in webviewModal if user is logged in through hivesigner --- src/components/index.tsx | 2 + src/components/qrModal/qrModalView.tsx | 21 +++++++- src/components/webViewModal/webViewModal.tsx | 50 +++++++++++++++++++ .../webViewModal/webViewModalStyles.ts | 8 +++ src/redux/actions/uiAction.ts | 16 ++++++ src/redux/constants/constants.js | 2 + src/redux/reducers/uiReducer.ts | 18 +++++++ .../children/applicationScreen.tsx | 2 + 8 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 src/components/webViewModal/webViewModal.tsx create mode 100644 src/components/webViewModal/webViewModalStyles.ts diff --git a/src/components/index.tsx b/src/components/index.tsx index 6d7618936f..05eb5b7905 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -102,6 +102,7 @@ import BeneficiarySelectionContent from './beneficiarySelectionContent/beneficia import TransferAccountSelector from './transferAccountSelector/transferAccountSelector'; import TransferAmountInputSection from './transferAmountInputSection/transferAmountInputSection'; import TextBoxWithCopy from './textBoxWithCopy/textBoxWithCopy'; +import WebViewModal from './webViewModal/webViewModal'; // Basic UI Elements import { @@ -251,4 +252,5 @@ export { TransferAccountSelector, TransferAmountInputSection, TextBoxWithCopy, + WebViewModal, }; diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index f6e83ab571..b137802f27 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -7,7 +7,12 @@ import { useIntl } from 'react-intl'; import { check, request, PERMISSIONS, RESULTS, openSettings } from 'react-native-permissions'; import styles from './qrModalStyles'; import { useAppDispatch, useAppSelector } from '../../hooks'; -import { showActionModal, toastNotification, toggleQRModal } from '../../redux/actions/uiAction'; +import { + showActionModal, + showWebViewModal, + toastNotification, + toggleQRModal, +} from '../../redux/actions/uiAction'; import { deepLinkParser } from '../../utils/deepLinkParser'; import RootNavigation from '../../navigation/rootNavigation'; import getWindowDimensions from '../../utils/getWindowDimensions'; @@ -16,6 +21,8 @@ import { handleHiveUriOperation } from '../../providers/hive/dhive'; import bugsnagInstance from '../../config/bugsnag'; import { get, isArray } from 'lodash'; import showLoginAlert from '../../utils/showLoginAlert'; +import authType from '../../constants/authType'; +import { delay } from '../../utils/editor'; const hiveuri = require('hive-uri'); const screenHeight = getWindowDimensions().height; @@ -112,7 +119,7 @@ export const QRModal = ({}: QRModalProps) => { } }; - const _handleHiveUri = (uri: string) => { + const _handleHiveUri = async (uri: string) => { try { setIsScannerActive(false); _onClose(); @@ -120,6 +127,16 @@ export const QRModal = ({}: QRModalProps) => { showLoginAlert({ intl }); return; } + if (get(currentAccount, 'local.authType') === authType.STEEM_CONNECT) { + await delay(500); // NOTE: it's required to avoid modal mis fire + dispatch( + showWebViewModal({ + uri: uri, + }), + ); + + return; + } const parsed = hiveuri.decode(uri); // resolve the decoded tx and params to a signable tx let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, { diff --git a/src/components/webViewModal/webViewModal.tsx b/src/components/webViewModal/webViewModal.tsx new file mode 100644 index 0000000000..aca0ecd2af --- /dev/null +++ b/src/components/webViewModal/webViewModal.tsx @@ -0,0 +1,50 @@ +import React, { useEffect, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { useAppDispatch, useAppSelector } from '../../hooks'; +import { hideWebViewModal } from '../../redux/actions/uiAction'; +import WebView from 'react-native-webview'; +import { hsOptions } from '../../constants/hsOptions'; +import { Modal } from '..'; +import styles from './webViewModalStyles'; + +interface QRModalProps {} +interface WebViewModalData { + uri: string; +} + +export const WebViewModal = ({}: QRModalProps) => { + const dispatch = useAppDispatch(); + const intl = useIntl(); + const isVisibleWebViewModal = useAppSelector((state) => state.ui.isVisibleWebViewModal); + const webViewModalData: WebViewModalData = useAppSelector((state) => state.ui.webViewModalData); + + const [hiveSignerModal, setHiveSignerModal] = useState(false); + + useEffect(() => { + if (isVisibleWebViewModal) { + setHiveSignerModal(true); + } else { + setHiveSignerModal(false); + } + }, [isVisibleWebViewModal]); + + const _onClose = () => { + dispatch(hideWebViewModal()); + }; + + return ( + + {webViewModalData && ( + + )} + + ); +}; + +export default WebViewModal; diff --git a/src/components/webViewModal/webViewModalStyles.ts b/src/components/webViewModal/webViewModalStyles.ts new file mode 100644 index 0000000000..200facecd2 --- /dev/null +++ b/src/components/webViewModal/webViewModalStyles.ts @@ -0,0 +1,8 @@ +import EStyleSheet from 'react-native-extended-stylesheet'; + +export default EStyleSheet.create({ + container: { + flex: 1, + backgroundColor: '$primaryBackgroundColor', + }, +}); diff --git a/src/redux/actions/uiAction.ts b/src/redux/actions/uiAction.ts index 014e9ca9ce..f9497a2ef9 100644 --- a/src/redux/actions/uiAction.ts +++ b/src/redux/actions/uiAction.ts @@ -15,6 +15,8 @@ import { SET_LOCKED_ORIENTATION, LOGOUT, LOGOUT_DONE, + SHOW_WEBVIEW_MODAL, + HIDE_WEBVIEW_MODAL, } from '../constants/constants'; export const updateActiveBottomTab = (payload: string) => ({ @@ -72,6 +74,20 @@ export const toggleQRModal = (payload: boolean) => ({ type: TOGGLE_QR_MODAL, }); +export const showWebViewModal = (payload: any) => ({ + payload: { + isVisibleWebViewModal: new Date().getTime(), + webViewModalData: { + ...payload, + }, + }, + type: SHOW_WEBVIEW_MODAL, +}); + +export const hideWebViewModal = () => ({ + type: HIDE_WEBVIEW_MODAL, +}); + export const setDeviceOrientation = (payload: string) => ({ payload, type: SET_DEVICE_ORIENTATION, diff --git a/src/redux/constants/constants.js b/src/redux/constants/constants.js index 3433645a45..ebf572151a 100644 --- a/src/redux/constants/constants.js +++ b/src/redux/constants/constants.js @@ -59,6 +59,8 @@ export const HIDE_POSTS_THUMBNAILS = 'HIDE_POSTS_THUMBNAILS'; export const RC_OFFER = 'RC_OFFER'; export const TOGGLE_ACCOUNTS_BOTTOM_SHEET = 'TOGGLE_ACCOUNTS_BOTTOM_SHEET'; export const TOGGLE_QR_MODAL = 'TOGGLE_QR_MODAL'; +export const SHOW_WEBVIEW_MODAL = 'SHOW_WEBVIEW_MODAL'; +export const HIDE_WEBVIEW_MODAL = 'HIDE_WEBVIEW_MODAL'; export const SHOW_ACTION_MODAL = 'SHOW_ACTION_MODAL'; export const HIDE_ACTION_MODAL = 'HIDE_ACTION_MODAL'; export const SET_AVATAR_CACHE_STAMP = 'SET_AVATAR_CACHE_STAMP'; diff --git a/src/redux/reducers/uiReducer.ts b/src/redux/reducers/uiReducer.ts index b7cbd765b2..9bc1077d51 100644 --- a/src/redux/reducers/uiReducer.ts +++ b/src/redux/reducers/uiReducer.ts @@ -15,6 +15,8 @@ import { HIDE_REPLY_MODAL, LOGOUT, LOGOUT_DONE, + SHOW_WEBVIEW_MODAL, + HIDE_WEBVIEW_MODAL, } from '../constants/constants'; import { orientations } from '../constants/orientationsConstants'; @@ -28,6 +30,8 @@ interface UiState { avatarCacheStamp: number; profileModalUsername: string; isVisibleQRModal: boolean; + webViewModalData: any; + isVisibleWebViewModal: boolean; deviceOrientation: string; lockedOrientation: string; replyModalVisible: boolean; @@ -45,6 +49,8 @@ const initialState: UiState = { avatarCacheStamp: 0, profileModalUsername: '', isVisibleQRModal: false, + isVisibleWebViewModal: false, + webViewModalData: null, deviceOrientation: orientations.PORTRAIT, lockedOrientation: orientations.PORTRAIT, replyModalPost: null, @@ -117,6 +123,18 @@ export default function (state = initialState, action): UiState { ...state, isVisibleQRModal: action.payload, }; + case SHOW_WEBVIEW_MODAL: + return { + ...state, + isVisibleWebViewModal: action.payload.isVisibleWebViewModal, + webViewModalData: action.payload.webViewModalData, + }; + case HIDE_WEBVIEW_MODAL: + return { + ...state, + isVisibleWebViewModal: false, + webViewModalData: null, + }; case SET_DEVICE_ORIENTATION: return { ...state, diff --git a/src/screens/application/children/applicationScreen.tsx b/src/screens/application/children/applicationScreen.tsx index 6a99580172..ed8f5fbbca 100644 --- a/src/screens/application/children/applicationScreen.tsx +++ b/src/screens/application/children/applicationScreen.tsx @@ -26,6 +26,7 @@ import { QuickProfileModal, QRModal, QuickReplyModal, + WebViewModal, } from '../../../components'; // Themes (Styles) @@ -129,6 +130,7 @@ const ApplicationScreen = ({ foregroundNotificationData }) => { + {isShowToastNotification && ( Date: Fri, 11 Aug 2023 19:55:18 +0500 Subject: [PATCH 10/15] show pinCode screen if pin is set during hive uri transaction process --- src/components/qrModal/qrModalView.tsx | 109 ++++++++++++++----------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index b137802f27..7b8a3bbe6d 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -23,6 +23,7 @@ import { get, isArray } from 'lodash'; import showLoginAlert from '../../utils/showLoginAlert'; import authType from '../../constants/authType'; import { delay } from '../../utils/editor'; +import ROUTES from '../../../src/constants/routeNames'; const hiveuri = require('hive-uri'); const screenHeight = getWindowDimensions().height; @@ -34,6 +35,7 @@ export const QRModal = ({}: QRModalProps) => { const isVisibleQRModal = useAppSelector((state) => state.ui.isVisibleQRModal); const currentAccount = useAppSelector((state) => state.account.currentAccount); const pinCode = useAppSelector((state) => state.application.pin); + const isPinCodeOpen = useAppSelector((state) => state.application.isPinCodeOpen); const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn); const [isScannerActive, setIsScannerActive] = useState(true); @@ -127,58 +129,71 @@ export const QRModal = ({}: QRModalProps) => { showLoginAlert({ intl }); return; } - if (get(currentAccount, 'local.authType') === authType.STEEM_CONNECT) { - await delay(500); // NOTE: it's required to avoid modal mis fire - dispatch( - showWebViewModal({ - uri: uri, - }), - ); - - return; + if (isPinCodeOpen) { + RootNavigation.navigate({ + name: ROUTES.SCREENS.PINCODE, + params: { + callback: () => _handleHiveUriTransaction(uri), + }, + }); + } else { + _handleHiveUriTransaction(uri); } - const parsed = hiveuri.decode(uri); - // resolve the decoded tx and params to a signable tx - let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, { - signers: currentAccount.name, - preferred_signer: currentAccount.name, - }); - const operations = get(tx, 'operations', []); + } catch (err) { + _showInvalidAlert(); + } + }; + + const _handleHiveUriTransaction = async (uri: string) => { + if (get(currentAccount, 'local.authType') === authType.STEEM_CONNECT) { + await delay(500); // NOTE: it's required to avoid modal mis fire dispatch( - showActionModal({ - title: intl.formatMessage({ - id: 'qr.confirmTransaction', - }), - bodyContent: _checkOpsArray(operations) ? _renderActionModalBody(operations[0]) : null, - buttons: [ - { - text: intl.formatMessage({ - id: 'qr.cancel', - }), - onPress: () => {}, - style: 'cancel', - }, - { - text: intl.formatMessage({ - id: 'qr.approve', - }), - onPress: () => { - handleHiveUriOperation(currentAccount, pinCode, uri) - .then(() => { - dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); - }) - .catch((err) => { - bugsnagInstance.notify(err); - dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); - }); - }, - }, - ], + showWebViewModal({ + uri: uri, }), ); - } catch (err) { - _showInvalidAlert(); + return; } + + const parsed = hiveuri.decode(uri); + // resolve the decoded tx and params to a signable tx + let { tx, signer } = hiveuri.resolveTransaction(parsed.tx, parsed.params, { + signers: currentAccount.name, + preferred_signer: currentAccount.name, + }); + const operations = get(tx, 'operations', []); + dispatch( + showActionModal({ + title: intl.formatMessage({ + id: 'qr.confirmTransaction', + }), + bodyContent: _checkOpsArray(operations) ? _renderActionModalBody(operations[0]) : null, + buttons: [ + { + text: intl.formatMessage({ + id: 'qr.cancel', + }), + onPress: () => {}, + style: 'cancel', + }, + { + text: intl.formatMessage({ + id: 'qr.approve', + }), + onPress: () => { + handleHiveUriOperation(currentAccount, pinCode, uri) + .then(() => { + dispatch(toastNotification(intl.formatMessage({ id: 'alert.successful' }))); + }) + .catch((err) => { + bugsnagInstance.notify(err); + dispatch(toastNotification(intl.formatMessage({ id: 'alert.key_warning' }))); + }); + }, + }, + ], + }), + ); }; const _handleDeepLink = async (url) => { From fe9bf9192cb498b3e066d527ddeddbefa27e662e Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Fri, 11 Aug 2023 20:33:13 +0500 Subject: [PATCH 11/15] increased transaction details container width --- src/components/qrModal/qrModalStyles.ts | 4 +++- src/components/qrModal/qrModalView.tsx | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/qrModal/qrModalStyles.ts b/src/components/qrModal/qrModalStyles.ts index 11507a4950..f73c79cdc3 100644 --- a/src/components/qrModal/qrModalStyles.ts +++ b/src/components/qrModal/qrModalStyles.ts @@ -1,5 +1,7 @@ import { TextStyle, ViewStyle } from 'react-native'; import EStyleSheet from 'react-native-extended-stylesheet'; +import getWindowDimensions from '../../utils/getWindowDimensions'; +const { width: SCREEN_WIDTH } = getWindowDimensions(); export default EStyleSheet.create({ sheetContent: { @@ -43,9 +45,9 @@ export default EStyleSheet.create({ borderRadius: 8, padding: 8, marginVertical: 10, + width: SCREEN_WIDTH - 64, } as ViewStyle, transactionRow: { - borderColor: 'red', flexDirection: 'row', alignItems: 'center', marginVertical: 4, diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index 7b8a3bbe6d..d3e067775a 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -217,8 +217,12 @@ export const QRModal = ({}: QRModalProps) => { const _renderTransactionInfoRow = (item: any) => ( - {item[0]} - {item[1]} + + {item[0]} + + + {item[1]} + ); const _renderActionModalBody = (operations: any) => ( From 55069cd74a95726f9120972531399d6958f7f334 Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Tue, 29 Aug 2023 21:11:53 +0500 Subject: [PATCH 12/15] show multiple ops not supported array alert for array operation --- src/components/qrModal/qrModalView.tsx | 11 +++++++++++ src/config/locales/en-US.json | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index d3e067775a..dc4f57fe1f 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -162,6 +162,17 @@ export const QRModal = ({}: QRModalProps) => { preferred_signer: currentAccount.name, }); const operations = get(tx, 'operations', []); + if (!_checkOpsArray(operations)) { + Alert.alert( + intl.formatMessage({ + id: 'qr.multi_array_ops_alert', + }), + intl.formatMessage({ + id: 'qr.multi_array_ops_aler_desct', + }), + ); + return; + } dispatch( showActionModal({ title: intl.formatMessage({ diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index 60839eb859..1ad02c36f8 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -1034,7 +1034,9 @@ "unsupported_alert_desc": "Please scan a valid ecency url.", "confirmTransaction": "Confirm transaction", "approve": "Approve", - "cancel": "Cancel" + "cancel": "Cancel", + "multi_array_ops_alert": "Multiple operations array detected!", + "multi_array_ops_aler_desct": "Ecency currently do not multiple operations array" }, "history": { "edit": "Edit History", From d7a7a347fa4c8f3c6f72cbaa9498c1056c909dae Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Tue, 29 Aug 2023 21:16:33 +0500 Subject: [PATCH 13/15] capitalize first letters of labels --- src/components/qrModal/qrModalStyles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/qrModal/qrModalStyles.ts b/src/components/qrModal/qrModalStyles.ts index f73c79cdc3..1bca82695e 100644 --- a/src/components/qrModal/qrModalStyles.ts +++ b/src/components/qrModal/qrModalStyles.ts @@ -57,6 +57,7 @@ export default EStyleSheet.create({ fontSize: 16, fontWeight: '700', flex: 1, + textTransform: 'capitalize', } as TextStyle, transactionItem2: { color: '$primaryBlack', From 0d22a37d966b51d44ff3704fe9e0c4ac1a12971d Mon Sep 17 00:00:00 2001 From: Sadaqat Ali <48380998+aliseyalvi@users.noreply.github.com> Date: Tue, 29 Aug 2023 21:54:23 +0500 Subject: [PATCH 14/15] show operation name in transaction details --- src/components/qrModal/qrModalStyles.ts | 18 +++++++++++++++++- src/components/qrModal/qrModalView.tsx | 7 ++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/components/qrModal/qrModalStyles.ts b/src/components/qrModal/qrModalStyles.ts index 1bca82695e..5ce8675faa 100644 --- a/src/components/qrModal/qrModalStyles.ts +++ b/src/components/qrModal/qrModalStyles.ts @@ -43,7 +43,7 @@ export default EStyleSheet.create({ borderWidth: 1, borderColor: '$borderColor', borderRadius: 8, - padding: 8, + // padding: 8, marginVertical: 10, width: SCREEN_WIDTH - 64, } as ViewStyle, @@ -52,6 +52,22 @@ export default EStyleSheet.create({ alignItems: 'center', marginVertical: 4, } as ViewStyle, + transactionHeadingContainer: { + borderBottomWidth: 1, + borderColor: '$borderColor', + height: 36, + paddingHorizontal: 8, + justifyContent: 'center', + } as ViewStyle, + transactionHeading: { + color: '$primaryBlack', + fontSize: 18, + fontWeight: '700', + textTransform: 'capitalize', + } as TextStyle, + transactionItemsContainer: { + padding: 8, + } as ViewStyle, transactionItem1: { color: '$primaryBlack', fontSize: 16, diff --git a/src/components/qrModal/qrModalView.tsx b/src/components/qrModal/qrModalView.tsx index dc4f57fe1f..31b48be4e1 100644 --- a/src/components/qrModal/qrModalView.tsx +++ b/src/components/qrModal/qrModalView.tsx @@ -238,7 +238,12 @@ export const QRModal = ({}: QRModalProps) => { ); const _renderActionModalBody = (operations: any) => ( - {Object.entries(operations[1]).map((item) => _renderTransactionInfoRow(item))} + + {operations[0]} + + + {Object.entries(operations[1]).map((item) => _renderTransactionInfoRow(item))} + ); const _showInvalidAlert = () => { From f80699f44b916208e4a12e78bdf3f8b2f189de0a Mon Sep 17 00:00:00 2001 From: Nouman Tahir Date: Thu, 31 Aug 2023 11:01:19 +0500 Subject: [PATCH 15/15] alert message update --- src/config/locales/en-US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/locales/en-US.json b/src/config/locales/en-US.json index 1ad02c36f8..4658c940c1 100644 --- a/src/config/locales/en-US.json +++ b/src/config/locales/en-US.json @@ -1036,7 +1036,7 @@ "approve": "Approve", "cancel": "Cancel", "multi_array_ops_alert": "Multiple operations array detected!", - "multi_array_ops_aler_desct": "Ecency currently do not multiple operations array" + "multi_array_ops_aler_desct": "Ecency does not support signing multiple operations, yet" }, "history": { "edit": "Edit History",