Merge remote-tracking branch 'origin/develop' into yushi/fix-sign-tx
# Conflicts: # packages/yoroi-extension/package-lock.json
# Conflicts: # packages/yoroi-extension/package-lock.json
name: SonarQube Checks
on:
# Trigger analysis when pushing to your main branches, and when creating a pull request.
push:
branches:
- main
- master
- develop
- 'releases/**'
pull_request:
types: [opened, synchronize, reopened]
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
with:
# Disabling shallow clone is recommended for improving relevancy of reporting
fetch-depth: 0
- name: SonarQube Scan
uses: sonarsource/[email protected]
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
if: github.event.review && (github.event.review.state == 'approved' || contains(github.event.review.body, '/check') || contains(github.event.review.body, '/release-check'))
runs-on: ubuntu-latest
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
- name: Read .nvmrc
run: echo ::set-output name=NVMRC::$(cat .nvmrc)
id: nvm
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_OUTPUT
- name: Setup node
uses: actions/[email protected]
uses: actions/[email protected]
with:
node-version: '${{ steps.nvm.outputs.NVMRC }}'
- name: Cache node modules
# https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows
uses: actions/[email protected]
uses: actions/[email protected]
env:
cache-name: cache-yoroi-extension-node-modules
with:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: npm install
run: |
. install-all.sh
- name: tests
run: |
npm run test
matrix:
browser: ['chrome', 'firefox']
fail-fast: false
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
- name: Forcefully update the Chrome browser
if: matrix.browser=='chrome'
run: brew update && brew upgrade --cask google-chrome
- name: Forcefully install Firefox for Developers browser
if: matrix.browser=='firefox'
run: |
brew update
brew tap homebrew/cask-versions && brew install --cask firefox-developer-edition
echo "FIREFOX_DEV=/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox-bin" >> $GITHUB_ENV
- name: Read .nvmrc
run: echo ::set-output name=NVMRC::$(cat .nvmrc)
id: nvm
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_OUTPUT
- name: Setup node
uses: actions/[email protected]
uses: actions/[email protected]
with:
node-version: '${{ steps.nvm.outputs.NVMRC }}'
- name: Cache extension node modules
# https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows
uses: actions/[email protected]
uses: actions/[email protected]
env:
cache-name: cache-yoroi-extension-node-modules
with:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Cache connector node modules
# https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows
uses: actions/[email protected]
uses: actions/[email protected]
env:
cache-name: cache-yoroi-connector-node-modules
with:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: npm install
run: |
. install-all.sh
- name: Build the test version
working-directory: ./packages/yoroi-extension
run: npm run test:build
- name: Create the report's folder
working-directory: ./packages/yoroi-extension
run: |
mkdir reports
touch ./reports/cucumberReports.json
- name: Run dapp connector tests
working-directory: ./packages/yoroi-extension
env:
MAILSAC_API_KEY: ${{ secrets.MAILSAC_API_KEY }}
run: npm run test:run:e2e:dApp:${{ matrix.browser }}
- name: Archive tests screenshots and logs
if: ${{ failure() }}
uses: actions/[email protected]
with:
name: testRunsData_${{ matrix.browser }}
name: testRunsData_E2E_tests_dApp_${{ matrix.browser }}
path: ./packages/yoroi-extension/testRunsData_${{ matrix.browser }}
Trezor_Model_T_emulator:
matrix:
browser: ['chrome', 'firefox']
fail-fast: false
steps:
- name: Forcefully update the Chrome browser
if: matrix.browser=='chrome'
- uses: actions/[email protected]
- name: Read .nvmrc
run: echo ::set-output name=NVMRC::$(cat .nvmrc)
id: nvm
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_OUTPUT
- name: Setup node
uses: actions/[email protected]
with:
node-version: '${{ steps.nvm.outputs.NVMRC }}'
- name: Cache extension node modules
# https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows
uses: actions/[email protected]
run: |
. install-all.sh
- name: Build the test version using emulators
- name: Build the test version
working-directory: ./packages/yoroi-extension
run: npm run test:build
if: ${{ failure() }}
uses: actions/[email protected]
with:
name: testRunsData_${{ matrix.browser }}
name: testRunsData_Trezor_Model_T_emulator_${{ matrix.browser }}
path: ./packages/yoroi-extension/testRunsData_${{ matrix.browser }}
E2E_smoke_tests:
matrix:
browser: ['chrome', 'firefox']
fail-fast: false
steps:
- uses: actions/[email protected]
- uses: actions/[email protected]
- name: Forcefully update the Chrome browser
if: matrix.browser=='chrome'
run: |
echo "FIREFOX_DEV=/opt/firefoxdev/firefox-bin" >> $GITHUB_ENV
- name: Read .nvmrc
run: echo ::set-output name=NVMRC::$(cat .nvmrc)
id: nvm
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_OUTPUT
- name: Setup node
uses: actions/[email protected]
uses: actions/[email protected]
with:
node-version: '${{ steps.nvm.outputs.NVMRC }}'
- name: Cache extension node modules
# https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows
uses: actions/[email protected]
uses: actions/[email protected]
env:
cache-name: cache-yoroi-extension-node-modules
with:
- name: Cache connector node modules
# https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows
| Firefox | Chrome | Edge |
|---|----|----|
| [<img src="https://pbs.twimg.com/profile_images/1399715004010532871/H_xS5LMU_400x400.jpg" width="48">](https://addons.mozilla.org/en-US/firefox/addon/yoroi/) | [<img src="https://pbs.twimg.com/profile_images/1037025533182193664/aCWlGSZF_400x400.jpg" width="48">](https://chrome.google.com/webstore/detail/yoroi/ffnbelfdoeiohenkjibnmadjiehjhajb) | [<img src="https://pbs.twimg.com/profile_images/1314301428995743750/xnhDug3t_400x400.jpg" width="48">](https://microsoftedge.microsoft.com/addons/detail/yoroi/akoiaibnepcedcplijmiamnaigbepmcb) |
| [<img src="https://img.icons8.com/external-those-icons-flat-those-icons/96/external-Firefox-logos-and-brands-those-icons-flat-those-icons.png" width="50">](https://addons.mozilla.org/en-US/firefox/addon/yoroi/) | [<img src="https://img.icons8.com/fluency/96/chrome.png" width="60">](https://chrome.google.com/webstore/detail/yoroi/ffnbelfdoeiohenkjibnmadjiehjhajb) | [<img src="https://img.icons8.com/color/64/ms-edge-new.png" width="60">](https://microsoftedge.microsoft.com/addons/detail/yoroi/akoiaibnepcedcplijmiamnaigbepmcb) |
Looking for Yoroi Mobile? See [here](https://github.com/Emurgo/yoroi-mobile)
import { Bech32Prefix } from "../../yoroi-extension/app/config/stringConfig";
import { bytesToHex, hexToBytes } from "./coreUtils";
const cardanoAccessBtnRow = document.querySelector("#request-button-row");
const cardanoAuthCheck = document.querySelector("#check-identification");
const cardanoAccessBtn = document.querySelector("#request-access");
const connectionStatus = document.querySelector("#connection-status");
const walletPlateSpan = document.querySelector("#wallet-plate");
const walletIconSpan = document.querySelector("#wallet-icon");
const getUnUsedAddresses = document.querySelector("#get-unused-addresses");
const getUsedAddresses = document.querySelector("#get-used-addresses");
const getChangeAddress = document.querySelector("#get-change-address");
const getRewardAddresses = document.querySelector("#get-reward-addresses");
const getAccountBalance = document.querySelector("#get-balance");
const isEnabledBtn = document.querySelector("#is-enabled");
const getUtxos = document.querySelector("#get-utxos");
const submitTx = document.querySelector("#submit-tx");
const signTx = document.querySelector("#sign-tx");
const showUtxos = document.querySelector("#show-utxos");
const createTx = document.querySelector("#create-tx");
const getCollateralUtxos = document.querySelector("#get-collateral-utxos");
const signData = document.querySelector("#sign-data");
const alertEl = document.querySelector("#alert");
const spinner = document.querySelector("#spinner");
const utxosContainer = document.querySelector("#utxos");
const getNFTs = document.getElementById("nfts");
const getNetworkId = document.getElementById("get-network-id");
const get = (selector) => document.querySelector(selector);
const getAll = (selector) => document.querySelectorAll(selector);
const cardanoAccessBtnRow = get("#request-button-row");
const cardanoAuthCheck = get("#check-identification");
const cardanoAccessBtn = get("#request-access");
const connectionStatus = get("#connection-status");
const walletPlateSpan = get("#wallet-plate");
const walletIconSpan = get("#wallet-icon");
const getUnUsedAddresses = get("#get-unused-addresses");
const getUsedAddresses = get("#get-used-addresses");
const getChangeAddress = get("#get-change-address");
const getRewardAddresses = get("#get-reward-addresses");
const getAccountBalance = get("#get-balance");
const isEnabledBtn = get("#is-enabled");
const getUtxos = get("#get-utxos");
const submitTx = get("#submit-tx");
const signTx = get("#sign-tx");
const showUtxos = get("#show-utxos");
const getCollateralUtxos = get("#get-collateral-utxos");
const signData = get("#sign-data");
const alertEl = get("#alert");
const spinner = get("#spinner");
const utxosContainer = get("#utxos");
const getNFTs = get("#nfts");
const getNetworkId = get("#get-network-id");
let accessGranted = false;
let cardanoApi;
function alertError(text) {
toggleSpinner("hide");
alertEl.className = "alert alert-danger overflow-scroll";
alertEl.className = "alert alert-danger overflow-auto";
alertEl.innerHTML = text;
}
function alertSuccess(text) {
alertEl.className = "alert alert-success overflow-scroll";
alertEl.className = "alert alert-success overflow-auto";
alertEl.innerHTML = text;
}
let utxosHTML = "";
for (let idx in utxos) {
const utxo = utxos[idx];
const amountInADA = Number(utxo.amount) / 1000000;
const numOfAssets = utxo.assets.length;
utxosHTML += `
<li id='${idx}' class="utxo-item list-group-item d-flex justify-content-between align-items-center ${
selectedUtxoIdx == idx && "bg-primary text-white"
}" style='cursor: pointer;'>
<p id='${idx}' class='mb-0'>${utxo.utxo_id.slice(0, 50)}</p>
<span class="badge bg-primary rounded-pill">${utxo.amount}</span>
<p id='${idx}' class='mb-0'>${utxo.utxo_id.slice(0, 25)}</p>
<div>
${numOfAssets ? `<span class="badge bg-primary rounded-pill">${utxo.assets.length} Assets</span>` : ''}
<span class="badge bg-primary rounded-pill">${amountInADA} ADA</span>
</div>
</li>
`;
}
utxosHTML += `
<input class="w-100 mt-3 p-1" placeholder="Receiver addresss..." type="text" id="create-tx-receiver" />
<button id="create-tx" class="btn btn-light mt-3 w-100">[Experimental] Create Tx</button>
`;
utxosContainer.innerHTML = utxosHTML;
"mb-5"
);
// Add select utxo handler for each list item
document.querySelectorAll(".utxo-item").forEach((el) => {
getAll(".utxo-item").forEach((el) => {
el.addEventListener("click", selectUtxo);
});
// Add event handler for create tx button
document
.querySelector("#create-tx")
.addEventListener("click", createTxHandler);
get("#create-tx").addEventListener("click", createTxHandler);
}
function createTxHandler(e) {
const selectedUtxo = utxos[selectedUtxoIdx];
if (!selectedUtxo) {
alertError("Failed to select a random utxo from the available list!");
alertError("No utxo selected");
return;
}
expectedPolicyId,
});
let receiver = get('#create-tx-receiver').value || selectedUtxo.receiver;
const outputHex = bytesToHex(
CardanoWasm.TransactionOutput.new(
CardanoWasm.Address.from_bech32(selectedUtxo.receiver),
CardanoWasm.Address.from_bech32(receiver),
CardanoWasm.Value.new(CardanoWasm.BigNum.from_str("1000000"))
).to_bytes()
);
const includeOutputs = [];
const includeTargets = [];
let targetAddress = selectedUtxo.receiver;
let targetAddress = receiver;
let targetDataHash = null;
/****** FLAGS ******/
console.log("[createTx] Including asset:", asset);
txReq.includeTargets.push({
// do not specify value, the connector will use minimum value
address: selectedUtxo.receiver,
address: receiver,
assets: {
[asset.assetId]: "1",
},
address = addressToCbor(address);
}
const payload = document.querySelector("#sign-data-payload").value;
const payload = get("#sign-data-payload").value;
let payloadHex;
if (payload.startsWith("0x")) {
payloadHex = Buffer.from(payload.replace("^0x", ""), "hex").toString("hex");
);
}
window.onload = () => {
const onload = () => {
if (typeof window.cardano === "undefined") {
alertError("Cardano API not found");
} else {
);
}
};
setTimeout(onload, 100);
)
)}
/>
<Route
path={ROUTES.NFTS.ROOT}
component={(props) => (
)
)}
/>
<Route
exact
path={ROUTES.WALLETS.ADD}
path={ROUTES.SWITCH}
component={(props) => <WalletSwitch {...props} stores={stores} actions={actions} />}
/>
<Route
exact
path={ROUTES.REVAMP.CATALYST_VOTING}
component={(props) => <VotingPage {...props} stores={stores} actions={actions} />}
/>
<Redirect to={ROUTES.MY_WALLETS} />
</Switch>
</Suspense>
path={ROUTES.WALLETS.CATALYST_VOTING}
component={(props) => <VotingPage {...props} stores={stores} actions={actions} />}
/>
<Route
exact
path={ROUTES.REVAMP.TRANSFER}
component={(props) => <Transfer {...props} stores={stores} actions={actions} />}
/>
</Switch>
);
"NetworkId": 250,
"TokenId": 6,
},
Object {
"Digest": 6.262633522161549e-167,
"Identifier": "",
"IsDefault": true,
"IsNFT": false,
"Metadata": Object {
"assetName": "",
"longName": null,
"numberOfDecimals": 6,
"policyId": "",
"ticker": "TADA",
"type": "Cardano",
},
"NetworkId": 350,
"TokenId": 7,
},
],
},
Object {
"NetworkId": 250,
"TokenId": 6,
},
Object {
"Digest": 6.262633522161549e-167,
"Identifier": "",
"IsDefault": true,
"IsNFT": false,
"Metadata": Object {
"assetName": "",
"longName": null,
"numberOfDecimals": 6,
"policyId": "",
"ticker": "TADA",
"type": "Cardano",
},
"NetworkId": 350,
"TokenId": 7,
},
],
},
Object {
"NetworkId": 250,
"TokenId": 6,
},
Object {
"Digest": 6.262633522161549e-167,
"Identifier": "",
"IsDefault": true,
"IsNFT": false,
"Metadata": Object {
"assetName": "",
"longName": null,
"numberOfDecimals": 6,
"policyId": "",
"ticker": "TADA",
"type": "Cardano",
},
"NetworkId": 350,
"TokenId": 7,
},
],
},
Object {
"NetworkId": 250,
"TokenId": 6,
},
Object {
"Digest": 6.262633522161549e-167,
"Identifier": "",
"IsDefault": true,
"IsNFT": false,
"Metadata": Object {
"assetName": "",
"longName": null,
"numberOfDecimals": 6,
"policyId": "",
"ticker": "TADA",
"type": "Cardano",
},
"NetworkId": 350,
"TokenId": 7,
},
],
},
Object {
"NetworkId": 250,
"TokenId": 6,
},
Object {
"Digest": 6.262633522161549e-167,
"Identifier": "",
"IsDefault": true,
"IsNFT": false,
"Metadata": Object {
"assetName": "",
"longName": null,
"numberOfDecimals": 6,
"policyId": "",
"ticker": "TADA",
"type": "Cardano",
},
"NetworkId": 350,
"TokenId": 7,
},
],
},
Object {
Fork: CardanoForks.Haskell,
}: NetworkRow),
CardanoPreviewTestnet: ({
NetworkId: 3_00,
NetworkId: 3_50,
NetworkName: 'Cardano Preview Testnet',
Backend: {
BackendService: environment.isTest()
Object.freeze({
StartAt: 0,
ChainNetworkId: '0',
ByronNetworkId: 1,
GenesisDate: '1654041600000',
ByronNetworkId: 2,
GenesisDate: '1666656000000',
SlotsPerEpoch: 21600,
SlotDuration: 20,
}),
Object.freeze({
StartAt: 0,
SlotsPerEpoch: 432000,
SlotsPerEpoch: 86400,
SlotDuration: 1,
PerEpochPercentageReward: 69344,
LinearFee: {
"Name": "CardanoScan",
"NetworkId": 250,
},
Object {
"Endpoints": Object {
"address": "https://preview.cardanoscan.io/address/",
"pool": "https://preview.cardanoscan.io/pool/",
"stakeAddress": "https://preview.cardanoscan.io/stakeKey/",
"token": "https://preview.cardanoscan.io/token/",
"transaction": "https://preview.cardanoscan.io/transaction/",
},
"ExplorerId": 550,
"IsBackup": true,
"Name": "CardanoScan",
"NetworkId": 350,
},
Object {
"Endpoints": Object {
"address": "https://adastat.net/address/",
"NetworkId": 250,
"NetworkName": "Cardano Preprod Testnet",
},
Object {
"Backend": Object {
"BackendService": "https://preview-backend.emurgornd.com",
"TokenInfoService": "https://stage-cdn.yoroiwallet.com",
"WebSocket": "wss://preview-backend.emurgornd.com:443",
},
"BaseConfig": Array [
Object {
"ByronNetworkId": 2,
"ChainNetworkId": "0",
"GenesisDate": "1666656000000",
"SlotDuration": 20,
"SlotsPerEpoch": 21600,
"StartAt": 0,
},
Object {
"CoinsPerUtxoWord": "34482",
"KeyDeposit": "2000000",
"LinearFee": Object {
"coefficient": "44",
"constant": "155381",
},
"MinimumUtxoVal": "1000000",
"PerEpochPercentageReward": 69344,
"PoolDeposit": "500000000",
"SlotDuration": 1,
"SlotsPerEpoch": 86400,
"StartAt": 0,
},
],
"CoinType": 2147485463,
"Fork": 0,
"NetworkId": 350,
"NetworkName": "Cardano Preview Testnet",
},
],
"PreferredExplorer": Array [],
"PriceData": Array [],
"NetworkId": 250,
"TokenId": 6,
},
Object {
"Digest": 6.262633522161549e-167,
"Identifier": "",
"IsDefault": true,
"IsNFT": false,
"Metadata": Object {
"assetName": "",
"longName": null,
"numberOfDecimals": 6,
"policyId": "",
"ticker": "TADA",
"type": "Cardano",
},
"NetworkId": 350,
"TokenId": 7,
},
],
"TokenList": Array [],
"Transaction": Array [],
"NetworkId": 250,
"TokenId": 6,
},
Object {
"Digest": 6.262633522161549e-167,
"Identifier": "",
"IsDefault": true,
"IsNFT": false,
"Metadata": Object {
"assetName": "",
"longName": null,
"numberOfDecimals": 6,
"policyId": "",
"ticker": "TADA",
"type": "Cardano",
},
"NetworkId": 350,
"TokenId": 7,
},
],
},
Object {
CATALYST_ROUND_INFO: networkForLocalStorage + '-CATALYST_ROUND_INFO',
// ========== CONNECTOR ========== //
ERGO_CONNECTOR_WHITELIST: 'connector_whitelist',
SELECTED_WALLET: 'SELECTED_WALLET',
};
export type SetCustomUserThemeRequest = {|
export type WalletsNavigation = {|
ergo: number[],
cardano: number[],
quickAccess: number[],
|}
/**
unsetUserTheme: void => Promise<void> = () => removeLocalItem(storageKeys.THEME);
// ========== Select Wallet ========== //
getSelectedWalletId: void => number | null = () => {
const id = localStorage.getItem(storageKeys.SELECTED_WALLET);
if (!id) return null
if (isNaN(Number(id))) throw new Error(`Invalid wallet Id: ${id}`);
return Number(id)
}
setSelectedWalletId: number => void = (id) => {
localStorage.setItem(storageKeys.SELECTED_WALLET, id.toString())
}
// ========== Custom User Theme ========== //
getCustomUserTheme: void => Promise<?string> = () => getLocalItem(storageKeys.CUSTOM_THEME);
if(Array.isArray(result)) return {
cardano: [],
ergo: [],
quickAccess: [],
}
return result
<svg width="24" height="24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#6B7384" stroke-width="1.5" stroke-linejoin="round" d="M12 17l-5.87785252 3.0901699 1.12256994-6.5450849-4.75528258-4.63525494 6.5716389-.95491503L12 2l2.9389263 5.95491503 6.5716389.95491503-4.7552826 4.63525494 1.1225699 6.5450849z"/>
</g>
</svg>
\ No newline at end of file
<svg width="24" height="24">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h24v24H0z"/>
<path stroke="#6B7384" stroke-width="1.5" fill="#6B7384" stroke-linejoin="round" d="M12 17l-5.87785252 3.0901699 1.12256994-6.5450849-4.75528258-4.63525494 6.5716389-.95491503L12 2l2.9389263 5.95491503 6.5716389.95491503-4.7552826 4.63525494 1.1225699 6.5450849z"/>
</g>
</svg>
\ No newline at end of file
theme.name === 'classic' ? { shrink: true, ...InputLabelProps } : { ...InputLabelProps }
}
InputProps={{
disableUnderline: revamp,
...(theme.name === 'classic' ? { notched: false } : {}),
endAdornment:
type === 'password' ? (
{Boolean(error) === true ? <ErrorIcon /> : done === true ? <DoneIcon /> : null}
</InputAdornment>
),
disableUnderline: revamp,
placeholder: placeholder != null ? placeholder : '',
}}
{...props}
type Props = {|
+currentTheme: Theme,
+selectTheme: ({| theme: string |}) => PossiblyAsync<void>,
+onSubmit: (theme: string) => PossiblyAsync<void>,
+onExternalLinkClick: MouseEvent => void,
+switchToFirstWallet: void => void,
|};
const NEW_THEME = THEMES.YOROI_REVAMP
render(): Node {
const {
currentTheme,
selectTheme,
onSubmit,
onExternalLinkClick,
} = this.props;
const { intl } = this.context;
value={currentTheme === NEW_THEME ? NEW_THEME : OLD_THEME}
onChange={(e) => {
const theme = e.target.value === NEW_THEME ? NEW_THEME : THEMES.YOROI_MODERN
selectTheme({ theme })
onSubmit(theme)
}}
sx={{
display: 'flex',
row
value={currentTheme}
onChange={(e) => {
selectTheme({ theme: e.target.value })
onSubmit(e.target.value)
}}
>
<Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', marginRight: '24px' }}>
// @flow
import { Component } from 'react';
import type { Node } from 'react';
import { observer } from 'mobx-react';
import styles from './NavDropdownContentRevamp.scss';
import { intlShape } from 'react-intl';
import type { $npm$ReactIntl$IntlFormat } from 'react-intl';
import globalMessages from '../../i18n/global-messages';
import { Button } from '@mui/material';
type Props = {|
+openWalletInfoDialog: void => void,
+contentComponents?: ?Node,
+walletsCount?: number,
|};
@observer
export default class NavDropdownContentRevamp extends Component<Props> {
static contextTypes: {| intl: $npm$ReactIntl$IntlFormat |} = {
intl: intlShape.isRequired,
};
static defaultProps: {| contentComponents: void, walletsCount: void |} = {
contentComponents: undefined,
walletsCount: undefined,
};
render(): Node {
const { contentComponents, walletsCount, openWalletInfoDialog } = this.props;
const { intl } = this.context;
return (
<div className={styles.wrapper}>
<div className={styles.card}>
{contentComponents}
<div className={styles.footer}>
<Button sx={{ width: '100%' }} onClick={() => openWalletInfoDialog()}>
{intl.formatMessage(globalMessages.allWalletsLabel)}{' '}
{walletsCount != null ? <span> ({walletsCount})</span> : null}
</Button>
</div>
</div>
</div>
);
}
}
.wrapper {
position: absolute;
padding-top: 33px;
top: 30px;
width: 100%;
right: 0;
z-index: 1;
}
.card {
overflow: hidden;
border-radius: 8px;
background-color: #FFF;
width: 100%;
border: 1px solid #eaedf2;
box-shadow: 0 10px 30px 0 rgba(24, 26, 30, 0.12);
}
.footer {
border-top: 1px solid #dce0e9;
display: flex;
justify-content: center;
button {
padding: 20px 0;
color: var(--yoroi-palette-secondary-300);
text-transform: uppercase;
font-size: 14px;
text-align: center;
font-weight: 500;
}
}
// @flow
import React, { Component } from 'react';
import type { Node, ElementRef } from 'react';
import { observer } from 'mobx-react';
import styles from './NavDropdownRevamp.scss';
import NavDropdownContentRevamp from './NavDropdownContentRevamp';
type Props = {|
+headerComponent?: ?Node,
+contentComponents?: ?Node,
+walletsCount?: number,
+openWalletInfoDialog: void => void,
|};
type State = {|
isExpanded: boolean,
|};
@observer
export default class NavDropdownRevamp extends Component<Props, State> {
static defaultProps: {| contentComponents: void, headerComponent: void, walletsCount: void |} = {
headerComponent: undefined,
contentComponents: undefined,
walletsCount: undefined,
};
state: State = {
isExpanded: false,
};
buttonRef: ?ElementRef<*>;
constructor(props: Props) {
super(props);
this.buttonRef = React.createRef();
}
toggleExpansion: void => void = () => {
this.setState(prevState => ({ isExpanded: !prevState.isExpanded }));
};
render(): Node {
const { headerComponent, contentComponents, walletsCount, openWalletInfoDialog } = this.props;
const { isExpanded } = this.state;
return (
<div
className={styles.wrapper}
onMouseEnter={this.toggleExpansion}
onMouseLeave={this.toggleExpansion}
>
<div className={styles.component}>{headerComponent}</div>
{isExpanded ? (
<NavDropdownContentRevamp
contentComponents={contentComponents}
walletsCount={walletsCount}
openWalletInfoDialog={openWalletInfoDialog}
/>
) : null}
</div>
);
}
}
- fix error: Class extends value undefined is not a constructor or null
4468: Backport fixes for 4465 to release/cardano-node-1.35.x branch r=coot a=coot # Description Cherry pick commits from #4467. # Checklist - Branch - [ ] Commit sequence broadly makes sense - [ ] Commits have useful messages - [ ] The documentation has been properly updated - [ ] New tests are added if needed and existing tests are updated - [ ] Any changes affecting Consensus packages must have an entry in the appropriate `changelog.d` directory created using [`scriv`](https://github.com/input-output-hk/scriv). If in doubt, see the [Consensus release process](../ouroboros-consensus/docs/ReleaseProcess.md). - [ ] Any changes in the Consensus API (every exposed function, type or module) that has changed its name, has been deleted, has been moved, or altered in some other significant way must leave behind a `DEPRECATED` warning that notifies downstream consumers. If deprecating a whole module, remember to add it to `./scripts/ci/check-stylish.sh` as otherwise `stylish-haskell` would un-deprecate it. - [ ] If this branch changes Network and has any consequences for downstream repositories or end users, said changes must be documented in [`interface-CHANGELOG.md`](../docs/interface-CHANGELOG.md) - [ ] If serialization changes, user-facing consequences (e.g. replay from genesis) are confirmed to be intentional. - Pull Request - [ ] Self-reviewed the diff - [ ] Useful pull request description at least containing the following information: - What does this PR change? - Why these changes were needed? - How does this affect downstream repositories and/or end-users? - Which ticket does this PR close (if any)? If it does, is it [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)? - [ ] Reviewer requested Co-authored-by: Karl Knutsson <[email protected]> Co-authored-by: Marcin Szamotulski <[email protected]>
Make it an Either String Filepath so the left case signifies a builtin.
chore: document preview usage in readme
test: concurrent users metrics
Signed-off-by: Chris Gianelloni <[email protected]>
Co-authored-by: Michael Peyton Jones <[email protected]>
- fix cannot find name 'PgConnectionCreator' error