diff --git a/.eslintrc b/.eslintrc index da0eb9a106..86dc9407bf 100644 --- a/.eslintrc +++ b/.eslintrc @@ -3,6 +3,7 @@ "parser": "babel-eslint", "plugins": ["jsx-a11y", "jest", "flowtype"], "rules": { + "comma-dangle": ["error", "never"], "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], "react/forbid-prop-types": [1, { "forbid": ["object", "any"] }], "class-methods-use-this": 0, diff --git a/.gitignore b/.gitignore index daa0b33c86..b29bf90b5c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ build_webpack/ build/ yarn-error.log .env* +/.yalc +yalc.lock \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..f5d169ffdb --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "bracketSpacing": true, + "jsxBracketSameLine": true, + "printWidth": 80, + "singleQuote": true, + "trailingComma": "none", + "tabWidth": 2, + "useTabs": false, + "semi": false + } \ No newline at end of file diff --git a/package.json b/package.json index ecf0675d22..3a554d0c71 100644 --- a/package.json +++ b/package.json @@ -73,8 +73,10 @@ "redux-actions": "^2.6.5", "redux-thunk": "^2.3.0", "reselect": "^4.0.0", + "safe-widgets": "https://github.com/Altoros/safe-widgets.git#d384b24", "semver": "^7.1.1", "squarelink": "^1.1.4", + "styled-components": "^4.4.1", "web3": "1.2.4", "web3connect": "^1.0.0-beta.23" }, diff --git a/src/logic/safe/transactions/send.js b/src/logic/safe/transactions/send.js index 7653eb5272..502f64ddae 100644 --- a/src/logic/safe/transactions/send.js +++ b/src/logic/safe/transactions/send.js @@ -4,6 +4,7 @@ import { getWeb3 } from '~/logic/wallets/getWeb3' import { type Operation } from '~/logic/safe/transactions' export const CALL = 0 +export const DELEGATE_CALL = 1 export const TX_TYPE_EXECUTION = 'execution' export const TX_TYPE_CONFIRMATION = 'confirmation' @@ -19,7 +20,7 @@ export const getApprovalTransaction = async ( gasPrice: number, gasToken: string, refundReceiver: string, - sender: string, + sender: string ) => { const txHash = await safeInstance.getTransactionHash( to, @@ -33,8 +34,8 @@ export const getApprovalTransaction = async ( refundReceiver, nonce, { - from: sender, - }, + from: sender + } ) try { @@ -61,7 +62,7 @@ export const getExecutionTransaction = async ( gasToken: string, refundReceiver: string, sender: string, - sigs: string, + sigs: string ) => { try { const web3 = getWeb3() @@ -77,7 +78,7 @@ export const getExecutionTransaction = async ( gasPrice, gasToken, refundReceiver, - sigs, + sigs ) } catch (err) { console.error(`Error while creating transaction: ${err}`) diff --git a/src/routes/safe/components/Apps/index.jsx b/src/routes/safe/components/Apps/index.jsx new file mode 100644 index 0000000000..f1b879df75 --- /dev/null +++ b/src/routes/safe/components/Apps/index.jsx @@ -0,0 +1,137 @@ +// @flow +import React, { useState, useEffect, useCallback } from 'react' +import styled from 'styled-components' + +import sendTransactions from './sendTransactions' + +const AppsWrapper = styled.div` + margin: 15px; +` + +const operations = { + SEND_TRANSACTIONS: 'sendTransactions', + GET_TRANSACTIONS: 'getTransactions', + ON_SAFE_INFO: 'onSafeInfo', + ON_TX_UPDATE: 'onTransactionUpdate' +} + +type Props = { + web3: any, + safeAddress: String, + network: String, + createTransaction: any +} + +function Apps({ web3, safeAddress, network, createTransaction }: Props) { + const iframeUrl = 'http://localhost:3002' + + const [selectedApp, setSelectedApp] = useState(null) + const [appIsLoading, setAppIsLoading] = useState(false) + const [iframeEl, seIframeEl] = useState(null) + + const sendMessageToIframe = (messageId, data) => { + iframeEl.contentWindow.postMessage({ messageId, data }, iframeUrl) + } + + const handleIframeMessage = async data => { + if (!data || !data.messageId) { + console.warn('iframe: message without messageId') + return + } + + switch (data.messageId) { + case operations.SEND_TRANSACTIONS: { + const txHash = await sendTransactions( + web3, + createTransaction, + safeAddress, + data.data + ) + + if (txHash) { + sendMessageToIframe(operations.ON_TX_UPDATE, { + txHash, + status: 'pending' + }) + } + + break + } + case operations.GET_TRANSACTIONS: + break + + default: { + console.warn(`Iframe:${data.messageId} unkown`) + break + } + } + } + + const iframeRef = useCallback(node => { + if (node !== null) { + seIframeEl(node) + } + }, []) + + useEffect(() => { + const onIframeMessage = async ({ origin, data }) => { + if (origin !== iframeUrl) { + return + } + + handleIframeMessage(data) + } + window.addEventListener('message', onIframeMessage) + + return () => { + window.removeEventListener('message', onIframeMessage) + } + }, []) + + useEffect(() => { + const onIframeLoaded = () => { + setAppIsLoading(false) + sendMessageToIframe(operations.ON_SAFE_INFO, { + safeAddress, + network + }) + } + + if (!iframeEl) { + return + } + + iframeEl.addEventListener('load', onIframeLoaded) + + return () => { + iframeEl.removeEventListener('load', onIframeLoaded) + } + }, [iframeEl]) + + const selectApp = () => { + setAppIsLoading(true) + setSelectedApp({ url: `${iframeUrl}?id=2` }) + } + + return ( + + + + {selectedApp && appIsLoading && 'Loading app...'} + + {selectedApp && ( +