Merge branch 'resolve_with_package' of https://github.com/ethereum/remix-project into resolve_with_package

pull/5370/head
filip mertens 2 years ago
commit 5026e9a6e2
  1. 12
      apps/remix-ide/src/app/files/fileManager.ts
  2. 7
      apps/remix-ide/src/app/plugins/permission-handler-plugin.tsx
  3. 3
      apps/remix-ide/src/app/tabs/locales/en/debugger.json
  4. 16
      apps/remix-ide/src/app/tabs/locales/en/filePanel.json
  5. 4
      apps/remix-ide/src/app/tabs/locales/en/index.js
  6. 13
      apps/remix-ide/src/app/tabs/locales/en/permissionHandler.json
  7. 35
      apps/remix-ide/src/app/tabs/locales/en/solidityUnitTesting.json
  8. 2
      apps/remix-ide/src/app/tabs/locales/en/terminal.json
  9. 41
      apps/remix-ide/src/app/tabs/locales/en/udapp.json
  10. 3
      apps/remix-ide/src/app/tabs/locales/zh/debugger.json
  11. 28
      apps/remix-ide/src/app/tabs/locales/zh/filePanel.json
  12. 13
      apps/remix-ide/src/app/tabs/locales/zh/home.json
  13. 4
      apps/remix-ide/src/app/tabs/locales/zh/index.js
  14. 13
      apps/remix-ide/src/app/tabs/locales/zh/permissionHandler.json
  15. 35
      apps/remix-ide/src/app/tabs/locales/zh/solidityUnitTesting.json
  16. 2
      apps/remix-ide/src/app/tabs/locales/zh/terminal.json
  17. 41
      apps/remix-ide/src/app/tabs/locales/zh/udapp.json
  18. 10
      apps/remix-ide/src/blockchain/blockchain.js
  19. 16
      libs/remix-lib/src/execution/txRunner.ts
  20. 3
      libs/remix-lib/src/execution/txRunnerVM.ts
  21. 3
      libs/remix-lib/src/execution/txRunnerWeb3.ts
  22. 6
      libs/remix-ui/app/src/lib/remix-app/interface/index.ts
  23. 6
      libs/remix-ui/debugger-ui/src/lib/debugger-ui.tsx
  24. 6
      libs/remix-ui/modal-dialog/src/lib/types/index.ts
  25. 18
      libs/remix-ui/permission-handler/src/lib/permission-dialog.tsx
  26. 39
      libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx
  27. 22
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  28. 26
      libs/remix-ui/run-tab/src/lib/components/recorderCardUI.tsx
  29. 71
      libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx
  30. 3
      libs/remix-ui/terminal/src/lib/components/RenderKnownTransactions.tsx
  31. 3
      libs/remix-ui/terminal/src/lib/components/RenderUnknownTransactions.tsx
  32. 4
      libs/remix-ui/terminal/src/lib/remix-ui-terminal.tsx
  33. 9
      libs/remix-ui/workspace/src/lib/actions/index.ts
  34. 6
      libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx
  35. 9
      libs/remix-ui/workspace/src/lib/components/file-explorer.tsx
  36. 1
      libs/remix-ui/workspace/src/lib/contexts/index.ts
  37. 7
      libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx
  38. 50
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx
  39. 2
      libs/remix-ui/workspace/src/lib/types/index.ts
  40. 6
      libs/remix-ui/workspace/src/lib/utils/index.ts

@ -1,4 +1,5 @@
'use strict'
import { saveAs } from 'file-saver'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../../package.json'
import Registry from '../state/registry'
@ -343,6 +344,17 @@ class FileManager extends Plugin {
}
}
async download(path) {
try {
const fileName = helper.extractNameFromKey(path)
path = this.normalize(path)
const content: any = await this.readFile(path)
saveAs(new Blob([content]), fileName)
} catch (e) {
throw new Error(e)
}
}
/**
* Create a directory
* @param {string} path path of the new directory

@ -1,4 +1,5 @@
import React from 'react' // eslint-disable-line
import { FormattedMessage } from 'react-intl'
import { Plugin } from '@remixproject/engine'
import { AppModal } from '@remix-ui/app'
import { PermissionHandlerDialog, PermissionHandlerValue } from '@remix-ui/permission-handler'
@ -103,10 +104,10 @@ export class PermissionHandlerPlugin extends Plugin {
}
const modal: AppModal = {
id: 'PermissionHandler',
title: `Permission needed for ${to.displayName || to.name}`,
title: <FormattedMessage id='permissionHandler.permissionNeededFor' values={{ to: to.displayName || to.name }} />,
message: <PermissionHandlerDialog plugin={this} theme={await this.getTheme()} value={value}></PermissionHandlerDialog>,
okLabel: 'Accept',
cancelLabel: 'Decline'
okLabel: <FormattedMessage id='permissionHandler.accept' />,
cancelLabel: <FormattedMessage id='permissionHandler.decline' />
}
const result = await this.call('notification', 'modal', modal)

@ -4,5 +4,8 @@
"debugger.stopDebugging": "Stop debugging",
"debugger.startDebugging": "Start debugging",
"debugger.placeholder": "Transaction hash, should start with 0x",
"debugger.debugLocaNodeLabel": "Force using local node",
"debugger.useGeneratedSources": "Use generated sources",
"debugger.debugWithGeneratedSources": "Debug with generated sources",
"debugger.introduction": "When Debugging with a transaction hash, if the contract is verified, Remix will try to fetch the source code from Sourcify or Etherscan. Put in your Etherscan API key in the Remix settings. For supported networks, please see"
}

@ -14,7 +14,9 @@
"filePanel.workspace.download": "Download Workspace",
"filePanel.workspace.restore": "Restore Workspace Backup",
"filePanel.workspace.clone": "Clone Git Repository",
"filePanel.workspace.cloneMessage": "Please provide a valid git repository url.",
"filePanel.workspace.enterGitUrl": "Enter git repository url",
"filePanel.workspace.switch": "Switch To Workspace",
"filePanel.workspace.solghaction": "Adds a preset yml file to run solidity unit tests on github actions CI.",
"filePanel.solghaction": "Solidity Test Workflow",
"filePanel.workspace.tssoltestghaction": "Adds a preset yml file to run mocha and chai tests for solidity on github actions CI",
@ -38,5 +40,17 @@
"filePanel.createNewFolder": "Create New Folder",
"filePanel.publishToGist": "Publish all the current workspace files to a github gist",
"filePanel.uploadFile": "Load a local file into current workspace",
"filePanel.updateGist": "Update the current [gist] explorer"
"filePanel.updateGist": "Update the current [gist] explorer",
"filePanel.viewAllBranches": "View all branches",
"filePanel.createBranch": "Create branch",
"filePanel.switchBranches": "Switch branches",
"filePanel.checkoutGitBranch": "Checkout Git Branch",
"filePanel.findOrCreateABranch": "Find or create a branch.",
"filePanel.initGitRepositoryLabel": "Initialize workspace as a new git repository",
"filePanel.initGitRepositoryWarning": "Please add username and email to Remix GitHub Settings to use git features.",
"filePanel.workspaceName": "Workspace name",
"filePanel.customizeTemplate": "Customize template",
"filePanel.features": "Features",
"filePanel.upgradeability": "Upgradeability",
"filePanel.ok": "OK"
}

@ -8,6 +8,8 @@ import settingsJson from './settings.json';
import solidityJson from './solidity.json';
import terminalJson from './terminal.json';
import udappJson from './udapp.json';
import solidityUnitTestingJson from './solidityUnitTesting.json';
import permissionHandlerJson from './permissionHandler.json';
export default {
...debuggerJson,
@ -20,4 +22,6 @@ export default {
...solidityJson,
...terminalJson,
...udappJson,
...solidityUnitTestingJson,
...permissionHandlerJson,
}

@ -0,0 +1,13 @@
{
"permissionHandler.allPermissionsReset": "All permisssions have been reset.",
"permissionHandler.rememberText": "has changed and",
"permissionHandler.permissionHandlerMessage": "\"{from}\" {rememberText} would like to access to \"{method}\" of \"{to}\"`",
"permissionHandler.description": "Description",
"permissionHandler.noDescriptionProvided": "No description Provided",
"permissionHandler.makeSureYouTrustThisPlugin": "Make sure you trust this plugin before processing this call.",
"permissionHandler.rememberThisChoice": "Remember this choice",
"permissionHandler.resetAllPermissions": "Reset all Permissions",
"permissionHandler.permissionNeededFor": "Permission needed for {to}",
"permissionHandler.accept": "Accept",
"permissionHandler.decline": "Decline"
}

@ -0,0 +1,35 @@
{
"solidityUnitTesting.displayName": "Solidity unit testing",
"solidityUnitTesting.testDirectory": "Test directory",
"solidityUnitTesting.testYourSmartContract": "Test your smart contract in Solidity.",
"solidityUnitTesting.selectDirectory": "Select directory to load and generate test files.",
"solidityUnitTesting.uiPathInputTooltip": "Press 'Enter' to change the path for test files.",
"solidityUnitTesting.uiPathInputButtonTooltip": "Create a test folder",
"solidityUnitTesting.create": "Create",
"solidityUnitTesting.generateTestsButtonTooltip": "Generate a sample test file",
"solidityUnitTesting.generate": "Generate",
"solidityUnitTesting.generateTestsLinkTooltip": "Check out documentation.",
"solidityUnitTesting.howToUse": "How to use...",
"solidityUnitTesting.runButtonTitle1": "Run tests",
"solidityUnitTesting.runButtonTitle2": "Please select Solidity compiler version greater than 0.4.12.",
"solidityUnitTesting.runButtonTitle3": "No solidity file selected",
"solidityUnitTesting.runButtonTitle4": "The \"Solidity Plugin\" should be activated",
"solidityUnitTesting.runButtonTitle5": "No test file selected",
"solidityUnitTesting.stopButtonLabel1": "Stop",
"solidityUnitTesting.stopButtonLabel2": "Stopping",
"solidityUnitTesting.run": "Run",
"solidityUnitTesting.runTestsTabStopActionTooltip": "Stop running tests",
"solidityUnitTesting.selectAll": "Select all",
"solidityUnitTesting.testTabTestsExecutionStopped": "The test execution has been stopped",
"solidityUnitTesting.testTabTestsExecutionStoppedError": "The test execution has been stopped because of error(s) in your test file",
"solidityUnitTesting.progress": "Progress: {readyTestsNumber} finished (of {runningTestsNumber})",
"solidityUnitTesting.resultFor": "Result for",
"solidityUnitTesting.passed": "Passed",
"solidityUnitTesting.failed": "Failed",
"solidityUnitTesting.timeTaken": "Time Taken",
"solidityUnitTesting.errorMessage": "Error Message",
"solidityUnitTesting.assertion": "Assertion",
"solidityUnitTesting.expectedValueShouldBe": "Expected value should be",
"solidityUnitTesting.receivedValue": "Received value",
"solidityUnitTesting.skippingTheRemainingTests": "Skipping the remaining tests of the function."
}

@ -1,7 +1,9 @@
{
"terminal.listen": "listen on all transactions",
"terminal.listenTitle": "If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you",
"terminal.search": "Search with transaction hash or address",
"terminal.used": "used",
"terminal.debug": "Debug",
"terminal.welcomeText1": "Welcome to",
"terminal.welcomeText2": "Your files are stored in",
"terminal.welcomeText3": "You can use this terminal to",

@ -4,6 +4,9 @@
"udapp.account": "Account",
"udapp.value": "Value",
"udapp.contract": "Contract",
"udapp.compiledBy": "Compiled by {compilerName}",
"udapp.infoSyncCompiledContractTooltip": "Click here to import contracts compiled from an external framework.{br}This action is enabled when Remix is connected to an external{br} framework (hardhat, truffle, foundry) through remixd.",
"udapp.remixIpfsUdappTooltip": "Publishing the source code and metadata to IPFS facilitates{br} source code verification using Sourcify and will greatly foster{br} contract adoption (auditing, debugging, calling it, etc...)",
"udapp.signAMessage": "Sign a message",
"udapp.enterAMessageToSign": "Enter a message to sign",
"udapp.hash": "hash",
@ -15,10 +18,46 @@
"udapp.publishTo": "Publish to",
"udapp.or": "or",
"udapp.atAddress": "At Address",
"udapp.atAddressOptionsTitle1": "address of contract",
"udapp.atAddressOptionsTitle2": "Interact with the deployed contract - requires the .abi file or {br} compiled .sol file to be selected in the editor {br}(with the same compiler configuration)",
"udapp.atAddressOptionsTitle3": "Compile a *.sol file or select a *.abi file.",
"udapp.atAddressOptionsTitle4": "To interact with a deployed contract, either{br} enter its address and compile its source *.sol file {br}(with the same compiler settings) or select its .abi file in the editor. ",
"udapp.contractOptionsTitle1": "Please compile *.sol file to deploy or access a contract",
"udapp.contractOptionsTitle2": "Select a compiled contract to deploy or to use with At Address.",
"udapp.contractOptionsTitle3": "Select and compile *.sol file to deploy or access a contract.",
"udapp.contractOptionsTitle4": "When there is a compiled .sol file, choose the {br} contract to deploy or to use with AtAddress.'",
"udapp.checkSumWarning": "It seems you are not using a checksumed address.{br}A checksummed address is an address that contains uppercase letters, as specified in {a}.{br}Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.",
"udapp.isOverSizePrompt": "Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. {br}More info: {a}",
"udapp.thisContractMayBeAbstract": "This contract may be abstract, it may not implement an abstract parent's methods completely or it may not invoke an inherited contract's constructor correctly.",
"udapp.noCompiledContracts": "No compiled contracts",
"udapp.addressOfContract": "Address of contract",
"udapp.loadContractFromAddress": "Load contract from Address",
"udapp.deployedContracts": "Deployed Contracts",
"udapp.deployAndRunClearInstances": "Clear instances list and reset recorder",
"udapp.deployAndRunNoInstanceText": "Currently you have no contract instances to interact with.",
"udapp.transactionsRecorded": "Transactions recorded"
"udapp.transactionsRecorded": "Transactions recorded",
"udapp.transactionsCountTooltip": "The number of recorded transactions",
"udapp.transactionSaveTooltip1": "No transactions to save",
"udapp.transactionSaveTooltip2": "Save {count} transaction as scenario file",
"udapp.transactionSaveTooltip3": "Save {count} transactions as scenario file",
"udapp.infoRecorderTooltip": "Save transactions (deployed contracts and function executions) {br}and replay them in another environment e.g Transactions created {br}in Remix VM can be replayed in the Injected Provider.",
"udapp.livemodeRecorderTooltip": "If contracts are updated after recording transactions,{br} checking this box will run recorded transactions {br}with the latest copy of the compiled contracts",
"udapp.livemodeRecorderLabel": "Run transactions using the latest compilation result",
"udapp.runRecorderTooltip": "Run transaction(s) from the current scenario file",
"udapp.save": "Save",
"udapp.run": "Run",
"udapp.ok": "OK",
"udapp.alert": "Alert",
"udapp.proceed": "Proceed",
"udapp.cancel": "Cancel",
"udapp.parameters": "Parameters",
"udapp.copyParameters": "Copy encoded input parameters to clipboard",
"udapp.copyCalldata": "Copy calldata to clipboard",
"udapp.deployWithProxy": "Deploy with Proxy",
"udapp.upgradeWithProxy": "Upgrade with Proxy",
"udapp.useLastDeployedERC1967Contract": "Use last deployed ERC1967 contract",
"udapp.proxyAddressLabel": "Proxy Address",
"udapp.proxyAddressPlaceholder": "proxy address",
"udapp.proxyAddressInputTooltip": "Enter previously deployed proxy address on the selected network",
"udapp.proxyAddressTooltip": "Select this option to use the last deployed ERC1967 contract on the current network."
}

@ -4,5 +4,8 @@
"debugger.stopDebugging": "停止调试",
"debugger.startDebugging": "开始调试",
"debugger.placeholder": "交易哈希, 应该以 0x 开头",
"debugger.debugLocaNodeLabel": "强制使用本地节点",
"debugger.useGeneratedSources": "使用生成的源",
"debugger.debugWithGeneratedSources": "使用生成的源进行调试",
"debugger.introduction": "当使用交易哈希调试时, 如果该合约已被验证, Remix 会试图从 Sourcify 或 Etherscan 获取源码. 在 Remix 中设置您的 Etherscan API key. 有关受支持的网络,请参阅"
}

@ -14,13 +14,15 @@
"filePanel.workspace.download": "下载工作空间",
"filePanel.workspace.restore": "恢复工作空间",
"filePanel.workspace.clone": "克隆 Git 仓库",
"filePanel.workspace.cloneMessage": "请提供有效的 git 仓库 url。",
"filePanel.workspace.enterGitUrl": "输入 Git 仓库地址",
"filePanel.workspace.solghaction": "添加预设 yml 文件以在 github 操作 CI 上运行 solidity 单元测试。",
"filePanel.solghaction": "Solidity Test Workflow",
"filePanel.workspace.tssoltestghaction": "添加一个预设的 yml 文件以在 github 操作 CI 上运行 mocha 和 chai 测试以确保可靠性",
"filePanel.tssoltestghaction": "Mocha Chai Test Workflow",
"filePanel.workspace.slitherghaction": "添加一个预设的 yml 文件以在 github actions CI 上运行 slither 分析",
"filePanel.slitherghaction": "Slither Workflow",
"filePanel.workspace.switch": "切换工作空间",
"filePanel.workspace.solghaction": "添加预设的 yml 文件,这样就可以在 github actions CI 上运行 solidity 单元测试。",
"filePanel.solghaction": "Solidity 测试工作流",
"filePanel.workspace.tssoltestghaction": "添加一个预设的 yml 文件,这样就可以在 github actions CI 上为 solidity 运行 mocha 和 chai 测试",
"filePanel.tssoltestghaction": "Mocha Chai 测试工作流",
"filePanel.workspace.slitherghaction": "添加一个预设的 yml 文件,这样就可以在 github actions CI 上运行 slither 分析",
"filePanel.slitherghaction": "Slither 工作流",
"filePanel.newFile": "新建文件",
"filePanel.newFolder": "新建文件夹",
"filePanel.rename": "重命名",
@ -38,5 +40,17 @@
"filePanel.createNewFolder": "新建文件夹",
"filePanel.publishToGist": "将当前工作空间下所有文件发布到github gist",
"filePanel.uploadFile": "加载本地文件到当前工作空间",
"filePanel.updateGist": "更新当前 [gist] 浏览"
"filePanel.updateGist": "更新当前 [gist] 浏览",
"filePanel.viewAllBranches": "查看所有分支",
"filePanel.createBranch": "创建分支",
"filePanel.switchBranches": "切换分支",
"filePanel.checkoutGitBranch": "切换 Git 分支",
"filePanel.findOrCreateABranch": "查找或创建一个分支。",
"filePanel.initGitRepositoryLabel": "初始化工作空间为一个新的 git 仓库",
"filePanel.initGitRepositoryWarning": "请将用户名和电子邮件添加到 Remix GitHub 设置以使用 git 功能。",
"filePanel.workspaceName": "工作空间名称",
"filePanel.customizeTemplate": "自定义模板",
"filePanel.features": "特点",
"filePanel.upgradeability": "可升级性",
"filePanel.ok": "确认"
}

@ -9,9 +9,10 @@
"home.featured": "精选",
"home.jumpIntoWeb3": "迎接 WEB3",
"home.jumpIntoWeb3Text": "Remix 项目是一个丰富的工具集,任何知识水平的用户都可以在这上面进行全周期的合约开发,并且可作为以太坊教学和实验的学习实验室。",
"home.remixYouTube": "",
"home.remixYouTubeText1": "",
"home.remixYouTubeText2": "",
"home.remixYouTube": "观看学习",
"home.remixYouTubeText1": "来自 Remix 团队的视频小贴士",
"home.remixYouTubeMore": "观看",
"home.remixYouTubeText2": "Remix 有一个不断更新的视频库,其中包含大量的使用技巧。查看并订阅以获取我们最新上传的视频。",
"home.betaTesting": "BETA 测试",
"home.betaTestingText1": "我们的社区支持我们",
"home.betaTestingText2": "每次 Remix IDE 发布版本之前,你都可以参与 Beta 测试。现在就来帮我们测试并且尝鲜新功能吧!",
@ -21,15 +22,15 @@
"home.solhintPluginDesc": "Solhint 是一个用于检查 Solidity 代码的开源项目",
"home.sourcifyPluginDesc": "Solidity 合约和元数据验证服务。",
"home.unitTestPluginDesc": "在 Solidity 中为你的合约编写和运行单元测试。",
"home.dgitPluginDesc": "",
"home.dgitPluginDesc": "为你的项目添加源码控制。",
"home.getStarted": "开始使用",
"home.projectTemplates": "项目模板",
"home.blankTemplateDesc": "创建一个空的工作空间。",
"home.remixDefaultTemplateDesc": "创建一个带有样本文件的工作空间",
"home.ozerc20TemplateDesc": "通过引入 OpenZeppelin 库来创建一个 ERC20 代币。",
"home.ozerc721TemplateDesc": "通过引入 OpenZeppelin 库来创建一个 NFT 代币。",
"home.ozerc1155TemplateDesc": "",
"home.gnosisSafeMultisigTemplateDesc": "",
"home.ozerc1155TemplateDesc": "通过导入 OpenZeppelin 库来创建一个 ERC1155 代币。",
"home.gnosisSafeMultisigTemplateDesc": "使用此模板创建多重签名钱包。",
"home.zeroxErc20TemplateDesc": "通过引入 0xProject 合约来创建一个 ERC20 代币。",
"home.learn": "学习",
"home.remixBasics": "Remix 基础",

@ -8,6 +8,8 @@ import settingsJson from './settings.json';
import solidityJson from './solidity.json';
import terminalJson from './terminal.json';
import udappJson from './udapp.json';
import solidityUnitTestingJson from './solidityUnitTesting.json';
import permissionHandlerJson from './permissionHandler.json';
import enJson from '../en';
// There may have some un-translated content. Always fill in the gaps with EN JSON.
@ -23,4 +25,6 @@ export default Object.assign({}, enJson, {
...solidityJson,
...terminalJson,
...udappJson,
...solidityUnitTestingJson,
...permissionHandlerJson,
})

@ -0,0 +1,13 @@
{
"permissionHandler.allPermissionsReset": "已重置所有权限。",
"permissionHandler.rememberText": "已变更且",
"permissionHandler.permissionHandlerMessage": "\"{from}\" {rememberText}要访问 \"{to}\" 的 \"{method}\"",
"permissionHandler.description": "描述",
"permissionHandler.noDescriptionProvided": "没有提供描述",
"permissionHandler.makeSureYouTrustThisPlugin": "在处理此调用之前,请确保您信任此插件。",
"permissionHandler.rememberThisChoice": "记住此选项",
"permissionHandler.resetAllPermissions": "重置所有权限",
"permissionHandler.permissionNeededFor": "需要 {to} 的权限",
"permissionHandler.accept": "接受",
"permissionHandler.decline": "拒绝"
}

@ -0,0 +1,35 @@
{
"solidityUnitTesting.displayName": "Solidity 单元测试",
"solidityUnitTesting.testDirectory": "测试目录",
"solidityUnitTesting.testYourSmartContract": "在 Solidity 中测试您的智能合约。",
"solidityUnitTesting.selectDirectory": "选择要加载和生成测试文件的目录。",
"solidityUnitTesting.uiPathInputTooltip": "按 \"Enter\" 键更改测试文件的路径。",
"solidityUnitTesting.uiPathInputButtonTooltip": "创建一个测试目录",
"solidityUnitTesting.create": "创建",
"solidityUnitTesting.generateTestsButtonTooltip": "生成示例测试文件",
"solidityUnitTesting.generate": "生成",
"solidityUnitTesting.generateTestsLinkTooltip": "查看文档。",
"solidityUnitTesting.howToUse": "如何使用...",
"solidityUnitTesting.runButtonTitle1": "运行测试",
"solidityUnitTesting.runButtonTitle2": "请选择大于 0.4.12 的 Solidity 编译器版本。",
"solidityUnitTesting.runButtonTitle3": "未选择 solidity 文件",
"solidityUnitTesting.runButtonTitle4": "应该激活 \"Solidity Plugin\"",
"solidityUnitTesting.runButtonTitle5": "未选择测试文件",
"solidityUnitTesting.stopButtonLabel1": "停止",
"solidityUnitTesting.stopButtonLabel2": "正在停止",
"solidityUnitTesting.run": "执行",
"solidityUnitTesting.runTestsTabStopActionTooltip": "停止运行测试",
"solidityUnitTesting.selectAll": "全选",
"solidityUnitTesting.testTabTestsExecutionStopped": "测试执行已停止",
"solidityUnitTesting.testTabTestsExecutionStoppedError": "由于测试文件中的错误,测试执行已停止",
"solidityUnitTesting.progress": "进度:{readyTestsNumber} 已完成(共 {runningTestsNumber})",
"solidityUnitTesting.resultFor": "测试结果",
"solidityUnitTesting.passed": "通过",
"solidityUnitTesting.failed": "失败",
"solidityUnitTesting.timeTaken": "耗时",
"solidityUnitTesting.errorMessage": "错误信息",
"solidityUnitTesting.assertion": "断言",
"solidityUnitTesting.expectedValueShouldBe": "期望值应该是",
"solidityUnitTesting.receivedValue": "实际值",
"solidityUnitTesting.skippingTheRemainingTests": "跳过该方法的其余测试。"
}

@ -1,7 +1,9 @@
{
"terminal.listen": "监听所有交易",
"terminal.listenTitle": "如果选中,Remix 将监听在当前环境中挖掘到的所有交易,而不仅仅是您创建的交易",
"terminal.search": "按交易哈希或地址搜索",
"terminal.used": "已使用",
"terminal.debug": "调试",
"terminal.welcomeText1": "欢迎使用",
"terminal.welcomeText2": "您的文件储存在",
"terminal.welcomeText3": "您可使用此终端",

@ -4,6 +4,9 @@
"udapp.account": "账户",
"udapp.value": "以太币数量",
"udapp.contract": "合约",
"udapp.compiledBy": "由 {compilerName} 编译",
"udapp.infoSyncCompiledContractTooltip": "单击此处导入从外部框架编译的合约。{br}当 Remix 通过 remixd 连接到外部{br}框架 (hardhat、truffle、foundry) 时启用此操作。",
"udapp.remixIpfsUdappTooltip": "将源代码和元数据发布到 IPFS 有助于{br}使用 Sourcify 验证源代码,并将极大地促进{br}合约采用(审计、调试、调用等)",
"udapp.signAMessage": "给一个消息签名",
"udapp.enterAMessageToSign": "输入一个需要签名的消息",
"udapp.hash": "哈希",
@ -15,10 +18,46 @@
"udapp.publishTo": "发布到",
"udapp.or": "或",
"udapp.atAddress": "At Address",
"udapp.atAddressOptionsTitle1": "合约地址",
"udapp.atAddressOptionsTitle2": "与已部署的合约交互 - 需要在编辑器中选择 .abi 文件或{br}编译的 .sol 文件{br}(具有相同的编译器配置)",
"udapp.atAddressOptionsTitle3": "编译一个 *.sol 文件或选中一个 *.abi 文件。",
"udapp.atAddressOptionsTitle4": "要与已部署的合约进行交互,{br} 输入其地址并编译其源 *.sol 文件{br}(使用相同的编译器设置)或在编辑器中选择其 .abi 文件。",
"udapp.contractOptionsTitle1": "请编译 *.sol 文件以部署或访问合约",
"udapp.contractOptionsTitle2": "选择要部署或与 At Address 一起使用的已编译合约。",
"udapp.contractOptionsTitle3": "选择并编译 *.sol 文件以部署或访问合约。",
"udapp.contractOptionsTitle4": "当有编译的 .sol 文件时,选择 {br} 合约进行部署或与 AtAddress 一起使用。",
"udapp.checkSumWarning": "您似乎没有使用 checksumed address 。{br} checksumed address 是包含大写字母的地址,如 {a} 中所指定。{br} checksumed address 旨在帮助防止用户将交易发送到错误地址。",
"udapp.isOverSizePrompt": "合约创建初始化返回长度超过24576字节的数据。部署可能会失败。 {br}更多信息:{a}",
"udapp.thisContractMayBeAbstract": "这个合约可能是抽象的,它可能没有完全实现抽象父类的方法,或者它可能没有正确调用继承合约的构造函数。",
"udapp.noCompiledContracts": "没有已编译的合约",
"udapp.addressOfContract": "合约地址",
"udapp.loadContractFromAddress": "加载此地址的合约",
"udapp.deployedContracts": "已部署的合约",
"udapp.deployAndRunClearInstances": "清空合约实例并重置交易记录",
"udapp.deployAndRunNoInstanceText": "当前您没有可交互的合约实例.",
"udapp.transactionsRecorded": "已记录的交易"
"udapp.transactionsRecorded": "已记录的交易",
"udapp.transactionsCountTooltip": "已记录的交易数",
"udapp.transactionSaveTooltip1": "没有可保存的交易",
"udapp.transactionSaveTooltip2": "将 {count} 笔交易保存到一个场景文件",
"udapp.transactionSaveTooltip3": "将 {count} 笔交易保存到一个场景文件",
"udapp.infoRecorderTooltip": "保存交易 (合约部署和方法执行) {br}然后在另一个环境中回放,比如在 Remix VM {br}创建的交易可以在 Injected Provider 中回放。",
"udapp.livemodeRecorderTooltip": "如果记录交易后合约有更新,{br}选中这个复选框就会以最新的{br}合约编译结果执行已记录的交易",
"udapp.livemodeRecorderLabel": "用最新的编译结果执行交易",
"udapp.runRecorderTooltip": "从当前场景文件中执行交易",
"udapp.save": "保存",
"udapp.run": "执行",
"udapp.ok": "确认",
"udapp.alert": "警告",
"udapp.proceed": "继续",
"udapp.cancel": "取消",
"udapp.parameters": "参数",
"udapp.copyParameters": "复制已编码的输入参数到粘贴板",
"udapp.copyCalldata": "复制 calldata 到粘贴板",
"udapp.deployWithProxy": "使用代理部署",
"udapp.upgradeWithProxy": "使用代理升级",
"udapp.useLastDeployedERC1967Contract": "使用最新的已部署的 ERC1967 合约",
"udapp.proxyAddressLabel": "代理地址",
"udapp.proxyAddressPlaceholder": "代理地址",
"udapp.proxyAddressInputTooltip": "输入先前在所选网络上部署的代理地址",
"udapp.proxyAddressTooltip": "选择此选项可使用当前网络上最后部署的 ERC1967 合约。"
}

@ -13,7 +13,8 @@ import { execution, EventManager, helpers } from '@remix-project/remix-lib'
import { etherScanLink } from './helper'
import { logBuilder, cancelUpgradeMsg, cancelProxyMsg, addressToString } from "@remix-ui/helper"
const { txFormat, txExecution, typeConversion, txListener: Txlistener, TxRunner, TxRunnerWeb3, txHelper } = execution
const { txResultHelper: resultToRemixTx } = helpers
const { txResultHelper } = helpers
const { resultToRemixTx } = txResultHelper
const packageJson = require('../../../../package.json')
const _paq = window._paq = window._paq || [] //eslint-disable-line
@ -575,8 +576,11 @@ export class Blockchain extends Plugin {
async (error, result) => {
if (error) return reject(error)
try {
const execResult = await this.web3().eth.getExecutionResultFromSimulator(result.transactionHash)
resolve(resultToRemixTx(result, execResult))
if (this.executionContext.isVM()) {
const execResult = await this.web3().eth.getExecutionResultFromSimulator(result.transactionHash)
resolve(resultToRemixTx(result, execResult))
} else
resolve(resultToRemixTx(result))
} catch (e) {
reject(e)
}

@ -1,6 +1,16 @@
'use strict'
import { EventManager } from '../eventManager'
export type Transaction = {
from: string,
to: string,
value: string,
data: string,
gasLimit: number,
useCall: boolean,
timestamp?: number
}
export class TxRunner {
event
runAsync
@ -19,11 +29,11 @@ export class TxRunner {
this.queusTxs = []
}
rawRun (args, confirmationCb, gasEstimationForceSend, promptCb, cb) {
rawRun (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, cb) {
run(this, args, args.timestamp || Date.now(), confirmationCb, gasEstimationForceSend, promptCb, cb)
}
execute (args, confirmationCb, gasEstimationForceSend, promptCb, callback) {
execute (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, callback) {
let data = args.data
if (data.slice(0, 2) !== '0x') {
data = '0x' + data
@ -32,7 +42,7 @@ export class TxRunner {
}
}
function run (self, tx, stamp, confirmationCb, gasEstimationForceSend = null, promptCb = null, callback = null) {
function run (self, tx: Transaction, stamp, confirmationCb, gasEstimationForceSend = null, promptCb = null, callback = null) {
if (!self.runAsync && Object.keys(self.pendingTxs).length) {
return self.queusTxs.push({ tx, stamp, callback })
}

@ -6,6 +6,7 @@ import { BN, bufferToHex, Address } from 'ethereumjs-util'
import type { Account } from '@ethereumjs/util'
import { EventManager } from '../eventManager'
import { LogsManager } from './logsManager'
import type { Transaction as InternalTransaction } from './txRunner'
export type VMexecutionResult = {
result: RunTxResult,
@ -52,7 +53,7 @@ export class TxRunnerVM {
this.nextNonceForCall = 0
}
execute (args, confirmationCb, gasEstimationForceSend, promptCb, callback: VMExecutionCallBack) {
execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback: VMExecutionCallBack) {
let data = args.data
if (data.slice(0, 2) !== '0x') {
data = '0x' + data

@ -1,5 +1,6 @@
'use strict'
import { EventManager } from '../eventManager'
import type { Transaction as InternalTransaction } from './txRunner'
import Web3 from 'web3'
export class TxRunnerWeb3 {
@ -79,7 +80,7 @@ export class TxRunnerWeb3 {
}
}
execute (args, confirmationCb, gasEstimationForceSend, promptCb, callback) {
execute (args: InternalTransaction, confirmationCb, gasEstimationForceSend, promptCb, callback) {
let data = args.data
if (data.slice(0, 2) !== '0x') {
data = '0x' + data

@ -9,13 +9,13 @@ export interface AppModal {
id: string
timestamp?: number
hide?: boolean
title: string
title: string | JSX.Element
validationFn?: (value: string) => ValidationResult
// eslint-disable-next-line no-undef
message: string | JSX.Element
okLabel: string
okLabel: string | JSX.Element
okFn?: (value?:any) => void
cancelLabel: string
cancelLabel: string | JSX.Element
cancelFn?: () => void,
modalType?: ModalTypes,
defaultValue?: string

@ -375,7 +375,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return { ...prevState, opt: { ...prevState.opt, debugWithGeneratedSources: checked } }
})
}} type="checkbox" />
<label data-id="debugGeneratedSourcesLabel" className="form-check-label custom-control-label" htmlFor="debugGeneratedSourcesInput">Use generated sources (Solidity {'>='} v0.7.2)</label>
<label data-id="debugGeneratedSourcesLabel" className="form-check-label custom-control-label" htmlFor="debugGeneratedSourcesInput"><FormattedMessage id='debugger.useGeneratedSources' /> (Solidity {'>='} v0.7.2)</label>
</span>
)
return (
@ -386,7 +386,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
<div className="mt-2 mb-2 debuggerConfig custom-control custom-checkbox">
<CustomTooltip
tooltipId="debuggerGenSourceCheckbox"
tooltipText={"Debug with generated sources"}
tooltipText={<FormattedMessage id='debugger.debugWithGeneratedSources' />}
placement="top-start"
>
{customJSX}
@ -398,7 +398,7 @@ export const DebuggerUI = (props: DebuggerUIProps) => {
return { ...prevState, opt: { ...prevState.opt, debugWithLocalNode: checked } }
})
}} type="checkbox" title="Force the debugger to use the current local node" />
<label data-id="debugLocaNodeLabel" className="form-check-label custom-control-label" htmlFor="debugWithLocalNodeInput">Force using local node</label>
<label data-id="debugLocaNodeLabel" className="form-check-label custom-control-label" htmlFor="debugWithLocalNodeInput"><FormattedMessage id='debugger.debugLocaNodeLabel' /></label>
</div>
}
{ state.validationError && <span className="w-100 py-1 text-danger validationError">{state.validationError}</span> }

@ -7,13 +7,13 @@ export type ValidationResult = {
export interface ModalDialogProps {
id: string
timestamp?: number,
title?: string,
title?: string | JSX.Element,
validation?: ValidationResult
validationFn?: (value: string) => ValidationResult
message?: string | JSX.Element,
okLabel?: string,
okLabel?: string | JSX.Element,
okFn?: (value?:any) => void,
cancelLabel?: string,
cancelLabel?: string | JSX.Element,
cancelFn?: () => void,
modalClass?: string,
showCancelIcon?: boolean,

@ -1,4 +1,5 @@
import React, { ChangeEventHandler, useContext, useEffect, useRef, useState } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { PermissionHandlerProps } from '../interface'
import './permission-dialog.css'
@ -6,6 +7,7 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
const { from, to, remember, method, message, sensitiveCall } = props.value
const [feedback, setFeedback] = useState<string>('')
const theme = props.theme
const intl = useIntl()
const switchMode = (e: any) => {
props.plugin.switchMode(from, to, method, e.target.checked)
@ -16,7 +18,7 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
}
const reset = () => {
props.plugin.clear()
setFeedback('All permisssions have been reset.')
setFeedback(intl.formatMessage({ id: 'permissionHandler.allPermissionsReset' }))
}
const imgFrom = () => { return <img className={`${theme === 'dark' ? 'invert' : ''}`} alt='' id='permissionModalImagesFrom' src={from.icon} /> }
@ -32,13 +34,13 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
}
const text = () => {
return <>"{from.displayName}" {(remember ? 'has changed and' : '')} would like to access to "{method}" of "{to.displayName}"`</>
return <FormattedMessage id='permissionHandler.permissionHandlerMessage' values={{ from: from.displayName, to: to.displayName, method, rememberText: remember ? intl.formatMessage({ id: 'permissionHandler.rememberText' }) : '' }} />
}
const pluginMessage = () => {
return message
? <div>
<h6>Description</h6>
<h6><FormattedMessage id='permissionHandler.description' /></h6>
<p>{message}</p>
</div> : null
}
@ -48,19 +50,19 @@ const PermissionHandlerDialog = (props: PermissionHandlerProps) => {
<article>
<h4 data-id="permissionHandlerMessage">{text()} :</h4>
<h6>{from.displayName}</h6>
<p> {from.description || <i>No description Provided</i>}</p>
<p> {from.description || <i><FormattedMessage id='permissionHandler.noDescriptionProvided' /></i>}</p>
<h6>{to.displayName} :</h6>
<p> {to.description || <i>No description Provided</i>}</p>
<p> {to.description || <i><FormattedMessage id='permissionHandler.noDescriptionProvided' /></i>}</p>
{pluginMessage()}
{ sensitiveCall ? <p className='text-warning'><i className="fas fa-exclamation-triangle mr-2" aria-hidden="true"></i>Make sure you trust this plugin before processing this call.</p> : '' }
{ sensitiveCall ? <p className='text-warning'><i className="fas fa-exclamation-triangle mr-2" aria-hidden="true"></i><FormattedMessage id='permissionHandler.makeSureYouTrustThisPlugin' /></p> : '' }
</article>
<article className='remember'>
{ !sensitiveCall && <div className='form-check'>
{rememberSwitch()}
<label htmlFor='remember' className="form-check-label" data-id="permissionHandlerRememberChoice">Remember this choice</label>
<label htmlFor='remember' className="form-check-label" data-id="permissionHandlerRememberChoice"><FormattedMessage id='permissionHandler.rememberThisChoice' /></label>
</div>
}
<button className="btn btn-sm" onClick={reset}>Reset all Permissions</button>
<button className="btn btn-sm" onClick={reset}><FormattedMessage id='permissionHandler.resetAllPermissions' /></button>
</article>
<div>{feedback}</div>
</section>)

@ -18,12 +18,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
content: ''
})
const [atAddressOptions, setAtAddressOptions] = useState<{ title: string | JSX.Element, disabled: boolean }>({
title: 'address of contract',
title: <FormattedMessage id='udapp.atAddressOptionsTitle1' />,
disabled: true
})
const [loadedAddress, setLoadedAddress] = useState<string>('')
const [contractOptions, setContractOptions] = useState<{ title: string | JSX.Element, disabled: boolean }>({
title: 'Please compile *.sol file to deploy or access a contract',
title: <FormattedMessage id='udapp.contractOptionsTitle1' />,
disabled: true
})
const [loadedContractData, setLoadedContractData] = useState<ContractData>(null)
@ -136,12 +136,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
if (enable) {
setAtAddressOptions({
disabled: false,
title: <span className="text-start">Interact with the deployed contract - requires the .abi file or <br /> compiled .sol file to be selected in the editor <br />(with the same compiler configuration)</span>
title: <span className="text-start"><FormattedMessage id='udapp.atAddressOptionsTitle2' values={{ br: <br /> }} /></span>
})
} else {
setAtAddressOptions({
disabled: true,
title: loadedAddress ? 'Compile a *.sol file or select a *.abi file.' : <span className="text-start">To interact with a deployed contract, either<br /> enter its address and compile its source *.sol file <br />(with the same compiler settings) or select its .abi file in the editor. </span>
title: loadedAddress ? <FormattedMessage id='udapp.atAddressOptionsTitle3' /> : <span className="text-start"><FormattedMessage id='udapp.atAddressOptionsTitle4' values={{ br: <br /> }} /></span>
})
}
}
@ -150,12 +150,12 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
if (enable) {
setContractOptions({
disabled: false,
title: 'Select a compiled contract to deploy or to use with At Address.'
title: <FormattedMessage id='udapp.contractOptionsTitle2' />
})
} else {
setContractOptions({
disabled: true,
title: loadType === 'sol' ? 'Select and compile *.sol file to deploy or access a contract.' : <span className="text-start">When there is a compiled .sol file, choose the <br /> contract to deploy or to use with AtAddress.'</span>
title: loadType === 'sol' ? <FormattedMessage id='udapp.contractOptionsTitle3' /> : <span className="text-start"><FormattedMessage id='udapp.contractOptionsTitle4' values={{ br: <br /> }} /></span>
})
}
}
@ -166,20 +166,20 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
const createInstance = (selectedContract, args, deployMode?: DeployMode[]) => {
if (selectedContract.bytecodeObject.length === 0) {
return props.modal('Alert', 'This contract may be abstract, it may not implement an abstract parent\'s methods completely or it may not invoke an inherited contract\'s constructor correctly.', 'OK', () => { })
return props.modal(intl.formatMessage({ id: 'udapp.alert' }), intl.formatMessage({ id: 'udapp.thisContractMayBeAbstract' }), intl.formatMessage({ id: 'udapp.ok' }), () => { })
}
if ((selectedContract.name !== currentContract) && (selectedContract.name === 'ERC1967Proxy')) selectedContract.name = currentContract
const isProxyDeployment = (deployMode || []).find(mode => mode === 'Deploy with Proxy')
const isContractUpgrade = (deployMode || []).find(mode => mode === 'Upgrade with Proxy')
if (isProxyDeployment) {
props.modal('Deploy Implementation & Proxy (ERC1967)', deployWithProxyMsg(), 'Proceed', () => {
props.modal('Deploy Implementation & Proxy (ERC1967)', deployWithProxyMsg(), intl.formatMessage({ id: 'udapp.proceed' }), () => {
props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode)
}, 'Cancel', () => { })
}, intl.formatMessage({ id: 'udapp.cancel' }), () => { })
} else if (isContractUpgrade) {
props.modal('Deploy Implementation & Update Proxy', upgradeWithProxyMsg(), 'Proceed', () => {
props.modal('Deploy Implementation & Update Proxy', upgradeWithProxyMsg(), intl.formatMessage({ id: 'udapp.proceed' }), () => {
props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode)
}, 'Cancel', () => { })
}, intl.formatMessage({ id: 'udapp.cancel' }), () => { })
} else {
props.createInstance(loadedContractData, props.gasEstimationPrompt, props.passphrasePrompt, props.publishToStorage, props.mainnetPrompt, isOverSizePrompt, args, deployMode)
}
@ -237,17 +237,15 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
const checkSumWarning = () => {
return (
<span className="text-start">
It seems you are not using a checksumed address.
<br />A checksummed address is an address that contains uppercase letters, as specified in <a href="https://eips.ethereum.org/EIPS/eip-55" target="_blank" rel="noreferrer">EIP-55</a>.
<br />Checksummed addresses are meant to help prevent users from sending transactions to the wrong address.
<FormattedMessage id='udapp.checkSumWarning' values={{ br: <br />, a: <a href="https://eips.ethereum.org/EIPS/eip-55" target="_blank" rel="noreferrer">EIP-55</a> }} />
</span>
)
}
const isOverSizePrompt = () => {
return (
<div>Contract creation initialization returns data with length of more than 24576 bytes. The deployment will likely fails. <br />
More info: <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md" target="_blank" rel="noreferrer">eip-170</a>
<div>
<FormattedMessage id='udapp.isOverSizePrompt' values={{ br: <br />, a: <a href="https://github.com/ethereum/EIPs/blob/master/EIPS/eip-170.md" target="_blank" rel="noreferrer">eip-170</a> }} />
</div>
)
}
@ -259,7 +257,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
<label className="udapp_settingsLabel pr-1">
<FormattedMessage id='udapp.contract' />
</label>
<div className="d-flex">{compilerName && compilerName !== '' && <label style={{ maxHeight: '0.6rem', lineHeight: '1rem' }} data-id="udappCompiledBy">(Compiled by <span className="text-capitalize"> {compilerName}</span>)</label>}</div>
<div className="d-flex">{compilerName && compilerName !== '' && <label style={{ maxHeight: '0.6rem', lineHeight: '1rem' }} data-id="udappCompiledBy">(<FormattedMessage id='udapp.compiledBy' values={{ compilerName: <span className="text-capitalize"> {compilerName}</span> }} />)</label>}</div>
</div>
{props.remixdActivated ?
(<CustomTooltip
@ -267,8 +265,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
tooltipClasses="text-wrap text-left"
tooltipId="info-sync-compiled-contract"
tooltipText={<span className="text-left">
Click here to import contracts compiled from an external framework.<br/>
This action is enabled when Remix is connected to an external<br/> framework (hardhat, truffle, foundry) through remixd.
<FormattedMessage id='udapp.infoSyncCompiledContractTooltip' values={{ br: <br /> }} />
</span>}
>
<button className="btn d-flex py-0" onClick={_ => {
@ -330,7 +327,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
placement={'right'}
tooltipClasses="text-wrap text-left"
tooltipId="remixIpfsUdappTooltip"
tooltipText={<span className="text-start">Publishing the source code and metadata to IPFS facilitates<br/> source code verification using Sourcify and will greatly foster<br/> contract adoption (auditing, debugging, calling it, etc...)</span>}
tooltipText={<span className="text-start"><FormattedMessage id='udapp.remixIpfsUdappTooltip' values={{ br: <br /> }} /></span>}
>
<label
htmlFor="deployAndRunPublishToIPFS"
@ -364,7 +361,7 @@ export function ContractDropdownUI (props: ContractDropdownProps) {
placement={'top-end'}
tooltipClasses="text-wrap text-left"
tooltipId="runAndDeployAddressInputtooltip"
tooltipText={"Address of contract"}
tooltipText={<FormattedMessage id='udapp.addressOfContract' />}
>
<input
ref={atAddressValue}

@ -1,5 +1,6 @@
// eslint-disable-next-line no-use-before-define
import React, { useEffect, useRef, useState } from 'react'
import { FormattedMessage, useIntl } from 'react-intl'
import * as remixLib from '@remix-project/remix-lib'
import { ContractGUIProps } from '../types'
import { CopyToClipboard } from '@remix-ui/clipboard'
@ -26,6 +27,7 @@ export function ContractGUI (props: ContractGUIProps) {
const multiFields = useRef<Array<HTMLInputElement | null>>([])
const initializeFields = useRef<Array<HTMLInputElement | null>>([])
const basicInputRef = useRef<HTMLInputElement>()
const intl = useIntl()
useEffect(() => {
if (props.deployOption && Array.isArray(props.deployOption)) {
@ -367,7 +369,7 @@ export function ContractGUI (props: ContractGUIProps) {
</div>
<div className="d-flex udapp_group udapp_multiArg">
<CopyToClipboard
tip="Copy calldata to clipboard"
tip={intl.formatMessage({ id: 'udapp.copyCalldata' })}
icon="fa-clipboard"
direction={"bottom"}
getContent={getEncodedCall}
@ -382,7 +384,7 @@ export function ContractGUI (props: ContractGUIProps) {
</button>
</CopyToClipboard>
<CopyToClipboard
tip="Copy encoded input parameters to clipboard"
tip={intl.formatMessage({ id: 'udapp.copyParameters' })}
icon="fa-clipboard"
direction={"bottom"}
getContent={getEncodedParams}
@ -393,7 +395,7 @@ export function ContractGUI (props: ContractGUIProps) {
className="m-0 remixui_copyIcon far fa-copy"
aria-hidden="true"
></i>
<label htmlFor="copyParameters">Parameters</label>
<label htmlFor="copyParameters"><FormattedMessage id='udapp.parameters' /></label>
</button>
</CopyToClipboard>
<CustomTooltip
@ -431,7 +433,7 @@ export function ContractGUI (props: ContractGUIProps) {
data-id="contractGUIDeployWithProxyLabel"
className="m-0 form-check-label w-100 custom-control-label udapp_checkboxAlign"
>
Deploy with Proxy
<FormattedMessage id='udapp.deployWithProxy' />
</label>
</div>
<div>
@ -497,7 +499,7 @@ export function ContractGUI (props: ContractGUIProps) {
data-id="contractGUIUpgradeImplementationLabel"
className="m-0 form-check-label custom-control-label udapp_checkboxAlign"
>
Upgrade with Proxy
<FormattedMessage id='udapp.upgradeWithProxy' />
</label>
</div>
<span onClick={handleToggleUpgradeImp}>
@ -527,7 +529,7 @@ export function ContractGUI (props: ContractGUIProps) {
checked={useLastProxy}
/>
<CustomTooltip
tooltipText="Select this option to use the last deployed ERC1967 contract on the current network."
tooltipText={<FormattedMessage id='udapp.proxyAddressTooltip' />}
tooltipId="proxyAddressTooltip"
placement="auto"
tooltipClasses="text-wrap"
@ -538,7 +540,7 @@ export function ContractGUI (props: ContractGUIProps) {
className="m-0 form-check-label custom-control-label udapp_checkboxAlign"
style={{ fontSize: 12 }}
>
Use last deployed ERC1967 contract
<FormattedMessage id='udapp.useLastDeployedERC1967Contract' />
</label>
</CustomTooltip>
</div>
@ -546,10 +548,10 @@ export function ContractGUI (props: ContractGUIProps) {
!useLastProxy ?
<div className="mb-2">
<label className="mt-2 text-left d-block">
Proxy Address :
<FormattedMessage id='udapp.proxyAddressLabel' /> :
</label>
<CustomTooltip placement="right" tooltipText={'Enter previously deployed proxy address on the selected network'}>
<input style={{ height: 32 }} className="form-control udapp_input" data-id="ERC1967AddressInput" placeholder='proxy address' onChange={handleSetProxyAddress} onBlur={() => validateProxyAddress(proxyAddress) } />
<CustomTooltip placement="right" tooltipText={<FormattedMessage id='udapp.proxyAddressInputTooltip' />}>
<input style={{ height: 32 }} className="form-control udapp_input" data-id="ERC1967AddressInput" placeholder={intl.formatMessage({ id: 'udapp.proxyAddressPlaceholder' })} onChange={handleSetProxyAddress} onBlur={() => validateProxyAddress(proxyAddress) } />
</CustomTooltip>
{ proxyAddressError && <span className='text-lowercase' data-id="errorMsgProxyAddress" style={{ fontSize: '.8em' }}>{ proxyAddressError }</span> }
</div> :

@ -1,6 +1,6 @@
// eslint-disable-next-line no-use-before-define
import React, {useRef, useState, useEffect} from 'react'
import { FormattedMessage } from 'react-intl'
import { FormattedMessage, useIntl } from 'react-intl'
import { RecorderProps } from '../types'
import { CustomTooltip } from '@remix-ui/helper'
@ -8,6 +8,8 @@ export function RecorderUI (props: RecorderProps) {
const inputLive = useRef<HTMLInputElement>()
const [toggleExpander, setToggleExpander] = useState<boolean>(false)
const [enableRunButton, setEnableRunButton] = useState<boolean>(true)
const intl = useIntl()
const triggerRecordButton = () => {
props.storeScenario(props.scenarioPrompt)
}
@ -38,7 +40,7 @@ export function RecorderUI (props: RecorderProps) {
placement={'right'}
tooltipClasses="text-nowrap"
tooltipId="recordedTransactionsCounttooltip"
tooltipText={'The number of recorded transactions'}
tooltipText={<FormattedMessage id='udapp.transactionsCountTooltip' />}
>
<div className="ml-2 badge badge-pill badge-primary text-center" data-title="The number of recorded transactions">{props.count}</div>
</CustomTooltip>
@ -46,7 +48,7 @@ export function RecorderUI (props: RecorderProps) {
placement={'right'}
tooltipClasses="text-wrap"
tooltipId="info-recorder"
tooltipText={<span>Save transactions (deployed contracts and function executions) <br />and replay them in another environment e.g Transactions created <br />in Remix VM can be replayed in the Injected Provider.</span>}
tooltipText={<span><FormattedMessage id='udapp.infoRecorderTooltip' values={{ br: <br /> }} /></span>}
>
<i style={{ fontSize: 'medium' }} className={'ml-2 fal fa-info-circle align-self-center'} aria-hidden="true"></i>
</CustomTooltip>
@ -64,9 +66,11 @@ export function RecorderUI (props: RecorderProps) {
placement={'right'}
tooltipClasses="text-wrap"
tooltipId="tooltip-livemode-recorder"
tooltipText={<span>If contracts are updated after recording transactions,<br/> checking this box will run recorded transactions <br/>with the latest copy of the compiled contracts</span>}
tooltipText={<span><FormattedMessage id='udapp.livemodeRecorderTooltip' values={{ br: <br /> }} /></span>}
>
<label className="form-check-label custom-control-label" data-id="runtabLivemodeInput" htmlFor="livemode-recorder">Run transactions using the latest compilation result</label>
<label className="form-check-label custom-control-label" data-id="runtabLivemodeInput" htmlFor="livemode-recorder">
<FormattedMessage id='udapp.livemodeRecorderLabel' />
</label>
</CustomTooltip>
</div>
<div className="mb-1 mt-1 udapp_transactionActions">
@ -75,14 +79,14 @@ export function RecorderUI (props: RecorderProps) {
tooltipClasses="text-nowrap"
tooltipId="remixUdappTransactionSavetooltip"
tooltipText={
props.count === 0 ? 'No transactions to save'
: props.count === 1 ? `Save ${props.count} transaction as scenario file`
: `Save ${props.count} transactions as scenario file`
props.count === 0 ? intl.formatMessage({ id: 'udapp.transactionSaveTooltip1' })
: props.count === 1 ? intl.formatMessage({ id: 'udapp.transactionSaveTooltip2' }, { count: props.count })
: intl.formatMessage({ id: 'udapp.transactionSaveTooltip3' }, { count: props.count })
}
>
<span>
<button className="btn btn-sm btn-info savetransaction udapp_recorder" disabled={props.count === 0 ? true: false} onClick={triggerRecordButton} style={{ pointerEvents: props.count === 0 ? 'none' : 'auto' }}>
Save
<FormattedMessage id='udapp.save' />
</button>
</span>
</CustomTooltip>
@ -90,11 +94,11 @@ export function RecorderUI (props: RecorderProps) {
placement={'right'}
tooltipClasses="text-nowrap"
tooltipId="tooltip-run-recorder"
tooltipText="Run transaction(s) from the current scenario file"
tooltipText={<FormattedMessage id='udapp.runRecorderTooltip' />}
>
<span>
<button className="btn btn-sm btn-info runtransaction udapp_runTxs" data-id="runtransaction" disabled={enableRunButton} onClick={handleClickRunButton} style={{ pointerEvents: enableRunButton ? 'none' : 'auto' }}>
Run
<FormattedMessage id='udapp.run' />
</button>
</span>
</CustomTooltip>

@ -1,4 +1,5 @@
import React, { useState, useRef, useEffect, ReactElement } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import * as semver from 'semver'
import { eachOfSeries } from 'async' // eslint-disable-line
import type Web3 from 'web3'
@ -45,14 +46,16 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const { helper, testTab, initialPath } = props
const { testTabLogic } = testTab
const intl = useIntl()
const [toasterMsg, setToasterMsg] = useState<string>('')
const [disableCreateButton, setDisableCreateButton] = useState<boolean>(true)
const [disableGenerateButton, setDisableGenerateButton] = useState<boolean>(false)
const [disableStopButton, setDisableStopButton] = useState<boolean>(true)
const [disableRunButton, setDisableRunButton] = useState<boolean>(false)
const [runButtonTitle, setRunButtonTitle] = useState<string>('Run tests')
const [stopButtonLabel, setStopButtonLabel] = useState<string>('Stop')
const [runButtonTitle, setRunButtonTitle] = useState<string>(intl.formatMessage({ id: 'solidityUnitTesting.runButtonTitle1' }))
const [stopButtonLabel, setStopButtonLabel] = useState<string>(intl.formatMessage({ id: 'solidityUnitTesting.stopButtonLabel1' }))
const [checkSelectAll, setCheckSelectAll] = useState<boolean>(true)
const [testsOutput, setTestsOutput] = useState<ReactElement[]>([])
@ -170,7 +173,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (!semver.gt(truncateVersion(currentVersion), '0.4.12')) {
setDisableRunButton(true)
setRunButtonTitle('Please select Solidity compiler version greater than 0.4.12.')
setRunButtonTitle(intl.formatMessage({ id: 'solidityUnitTesting.runButtonTitle2' }))
}
})
@ -395,17 +398,17 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
<span> {test.value}</span>
{debugBtn}
</div>
<span className="text-dark">Error Message:</span>
<span className="text-dark"><FormattedMessage id='solidityUnitTesting.errorMessage' />:</span>
<span className="pb-2 text-break">"{test.errMsg}"</span>
<span className="text-dark">Assertion:</span>
<span className="text-dark"><FormattedMessage id='solidityUnitTesting.assertion' />:</span>
<div className="d-flex flex-wrap">
<span>Expected value should be</span>
<span><FormattedMessage id='solidityUnitTesting.expectedValueShouldBe' /></span>
<div className="mx-1 font-weight-bold">{method}</div>
<div>{preposition} {expected}</div>
</div>
<span className="text-dark">Received value:</span>
<span className="text-dark"><FormattedMessage id='solidityUnitTesting.receivedValue' />:</span>
<span>{test.returned}</span>
<span className="text-dark text-sm pb-2">Skipping the remaining tests of the function.</span>
<span className="text-dark text-sm pb-2"><FormattedMessage id='solidityUnitTesting.skippingTheRemainingTests' /></span>
</div>)
setTestsOutput(prevCards => ([...prevCards, testFailCard2]))
}
@ -457,10 +460,10 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
const testSummary = fileTestsResult['summary']
if (testSummary && testSummary.filename && !testSummary.rendered) {
const summaryCard: ReactElement = (<div className="d-flex alert-secondary mb-3 p-3 flex-column">
<span className="font-weight-bold">Result for {testSummary.filename}</span>
<span className="text-success">Passed: {testSummary.passed}</span>
<span className="text-danger">Failed: {testSummary.failed}</span>
<span>Time Taken: {testSummary.timeTaken}s</span>
<span className="font-weight-bold"><FormattedMessage id='solidityUnitTesting.resultFor' /> {testSummary.filename}</span>
<span className="text-success"><FormattedMessage id='solidityUnitTesting.passed' />: {testSummary.passed}</span>
<span className="text-danger"><FormattedMessage id='solidityUnitTesting.failed' />: {testSummary.failed}</span>
<span><FormattedMessage id='solidityUnitTesting.timeTaken' />: {testSummary.timeTaken}s</span>
</div>)
setTestsOutput(prevCards => ([...prevCards, summaryCard]))
fileTestsResult['summary']['rendered'] = true
@ -523,7 +526,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (_errors || hasBeenStopped.current || readyTestsNumber === runningTestsNumber) {
// All tests are ready or the operation has been canceled or there was a compilation error in one of the test files.
setDisableStopButton(true)
setStopButtonLabel('Stop')
setStopButtonLabel(intl.formatMessage({ id: 'solidityUnitTesting.stopButtonLabel1' }))
if (selectedTests.current?.length !== 0) {
setDisableRunButton(false)
}
@ -598,16 +601,16 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
if (!isSolidityActive || !selectedTests.current.length) {
setDisableRunButton(true)
if (!currentFile || (currentFile && currentFile.split('.').pop().toLowerCase() !== 'sol')) {
setRunButtonTitle('No solidity file selected')
setRunButtonTitle(intl.formatMessage({ id: 'solidityUnitTesting.runButtonTitle3' }))
} else {
setRunButtonTitle('The "Solidity Plugin" should be activated')
setRunButtonTitle(intl.formatMessage({ id: 'solidityUnitTesting.runButtonTitle4' }))
}
} else setDisableRunButton(false)
}
const stopTests = () => {
hasBeenStopped.current = true
setStopButtonLabel('Stopping')
setStopButtonLabel(intl.formatMessage({ id: 'solidityUnitTesting.stopButtonLabel2' }))
setDisableStopButton(true)
setDisableRunButton(true)
}
@ -625,12 +628,12 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
setCheckSelectAll(true)
setDisableRunButton(false)
if ((readyTestsNumber === runningTestsNumber || hasBeenStopped.current) && stopButtonLabel.trim() === 'Stop') {
setRunButtonTitle('Run tests')
setRunButtonTitle(intl.formatMessage({ id: 'solidityUnitTesting.runButtonTitle1' }))
}
} else if (!selectedTests.current.length) {
setCheckSelectAll(false)
setDisableRunButton(true)
setRunButtonTitle('No test file selected')
setRunButtonTitle(intl.formatMessage({ id: 'solidityUnitTesting.runButtonTitle5' }))
} else setCheckSelectAll(false)
}
@ -661,9 +664,9 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
<div className="px-2" id="testView">
<Toaster message={toasterMsg} />
<div className="infoBox">
<p className="text-lg"> Test your smart contract in Solidity.</p>
<p> Select directory to load and generate test files.</p>
<label>Test directory:</label>
<p className="text-lg"> <FormattedMessage id='solidityUnitTesting.testYourSmartContract' /></p>
<p> <FormattedMessage id='solidityUnitTesting.selectDirectory' /></p>
<label><FormattedMessage id='solidityUnitTesting.testDirectory' />:</label>
<div>
<div className="d-flex p-2">
<datalist id="utPathList">{
@ -676,7 +679,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
placement="top-end"
tooltipClasses="text-nowrap"
tooltipId="uiPathInputtooltip"
tooltipText={"Press 'Enter' to change the path for test files."}
tooltipText={<FormattedMessage id='solidityUnitTesting.uiPathInputTooltip' />}
>
<input
list="utPathList"
@ -695,7 +698,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
placement="top-end"
tooltipClasses="text-nowrap"
tooltipId="uiPathInputButtontooltip"
tooltipText="Create a test folder"
tooltipText={<FormattedMessage id='solidityUnitTesting.uiPathInputButtonTooltip' />}
>
<button
className="btn border ml-2"
@ -703,7 +706,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
disabled={disableCreateButton}
onClick={handleCreateFolder}
>
Create
<FormattedMessage id='solidityUnitTesting.create' />
</button>
</CustomTooltip>
</div>
@ -714,7 +717,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
<CustomTooltip
tooltipId="generateTestsButtontooltip"
tooltipClasses="text-nowrap"
tooltipText="Generate a sample test file"
tooltipText={<FormattedMessage id='solidityUnitTesting.generateTestsButtonTooltip' />}
placement={'bottom-start'}
>
<button
@ -726,17 +729,17 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
await updateForNewCurrent()
}}
>
Generate
<FormattedMessage id='solidityUnitTesting.generate' />
</button>
</CustomTooltip>
<CustomTooltip
tooltipId="generateTestsLinktooltip"
tooltipClasses="text-nowrap"
tooltipText="Check out documentation."
tooltipText={<FormattedMessage id='solidityUnitTesting.generateTestsLinkTooltip' />}
placement={'bottom-start'}
>
<a className="btn border text-decoration-none pr-0 d-flex w-50 ml-2" target="__blank" href="https://remix-ide.readthedocs.io/en/latest/unittesting.html#test-directory">
<label className="btn p-1 ml-2 m-0">How to use...</label>
<label className="btn p-1 ml-2 m-0"><FormattedMessage id='solidityUnitTesting.howToUse' /></label>
</a>
</CustomTooltip>
</div>
@ -749,7 +752,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
>
<button id="runTestsTabRunAction"data-id="testTabRunTestsTabRunAction" className="w-50 btn btn-primary" disabled={disableRunButton} onClick={runTests}>
<span className="fas fa-play ml-2"></span>
<label className="labelOnBtn btn btn-primary p-1 ml-2 m-0">Run</label>
<label className="labelOnBtn btn btn-primary p-1 ml-2 m-0"><FormattedMessage id='solidityUnitTesting.run' /></label>
</button>
</CustomTooltip>
<button id="runTestsTabStopAction" data-id="testTabRunTestsTabStopAction" className="w-50 pl-2 ml-2 btn btn-secondary" disabled={disableStopButton} onClick={stopTests}>
@ -757,7 +760,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
placement={'top-start'}
tooltipClasses="text-nowrap"
tooltipId="info-recorder"
tooltipText="Stop running tests"
tooltipText={<FormattedMessage id='solidityUnitTesting.runTestsTabStopActionTooltip' />}
>
<span>
<span className="fas fa-stop ml-2"></span>
@ -774,7 +777,7 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
checked={checkSelectAll}
onChange={() => { }} // eslint-disable-line
/>
<label className="text-nowrap pl-2 mb-0" htmlFor="checkAllTests"> Select all </label>
<label className="text-nowrap pl-2 mb-0" htmlFor="checkAllTests"> <FormattedMessage id='solidityUnitTesting.selectAll' /> </label>
</div>
<div className="testList py-2 mt-0 border-bottom">{testFiles.length ? testFiles.map((testFileObj: TestObject, index) => {
const elemId = `singleTest${testFileObj.fileName}`
@ -787,9 +790,9 @@ export const SolidityUnitTesting = (props: Record<string, any>) => { // eslint-d
})
: "No test file available"} </div>
<div className="align-items-start flex-column mt-2 mx-3 mb-0">
<span className='text-info h6' hidden={progressBarHidden}>Progress: {readyTestsNumber} finished (of {runningTestsNumber})</span>
<label className="text-warning h6" data-id="testTabTestsExecutionStopped" hidden={testsExecutionStoppedHidden}>The test execution has been stopped</label>
<label className="text-danger h6" data-id="testTabTestsExecutionStoppedError" hidden={testsExecutionStoppedErrorHidden}>The test execution has been stopped because of error(s) in your test file</label>
<span className='text-info h6' hidden={progressBarHidden}><FormattedMessage id='solidityUnitTesting.progress' values={{ readyTestsNumber, runningTestsNumber }} /></span>
<label className="text-warning h6" data-id="testTabTestsExecutionStopped" hidden={testsExecutionStoppedHidden}><FormattedMessage id='solidityUnitTesting.testTabTestsExecutionStopped' /></label>
<label className="text-danger h6" data-id="testTabTestsExecutionStoppedError" hidden={testsExecutionStoppedErrorHidden}><FormattedMessage id='solidityUnitTesting.testTabTestsExecutionStoppedError' /></label>
</div>
<div className="mx-3 mb-2 pb-4 border-primary" id="solidityUnittestsOutput" data-id="testTabSolidityUnitTestsOutput">{testsOutput}</div>
</div>

@ -1,5 +1,6 @@
import React from 'react' // eslint-disable-line
import { FormattedMessage } from 'react-intl'
import CheckTxStatus from './ChechTxStatus' // eslint-disable-line
import Context from './Context' // eslint-disable-line
import showTable from './Table'
@ -31,7 +32,7 @@ const RenderKnownTransactions = ({ tx, receipt, resolvedData, logs, index, plugi
data-shared='txLoggerDebugButton'
data-id={`txLoggerDebugButton${tx.hash}`}
onClick={(event) => debug(event, tx)}
>Debug</div>
><FormattedMessage id='terminal.debug' /></div>
</div>
<i className={`remix_ui_terminal_arrow fas ${(showTableHash.includes(tx.hash)) ? 'fa-angle-up' : 'fa-angle-down'}`}></i>
</div>

@ -1,4 +1,5 @@
import React, { useState } from 'react' // eslint-disable-line
import { FormattedMessage } from 'react-intl'
import CheckTxStatus from './ChechTxStatus' // eslint-disable-line
import Context from './Context' // eslint-disable-line
import showTable from './Table'
@ -27,7 +28,7 @@ const RenderUnKnownTransactions = ({ tx, receipt, index, plugin, showTableHash,
data-shared='txLoggerDebugButton'
data-id={`txLoggerDebugButton${tx.hash}`}
onClick={(event) => debug(event, tx)}
>Debug</div>
><FormattedMessage id='terminal.debug' /></div>
</div>
<i className = {`remix_ui_terminal_arrow fas ${(showTableHash.includes(tx.hash)) ? 'fa-angle-up' : 'fa-angle-down'}`}></i>
</div>

@ -464,11 +464,11 @@ export const RemixUiTerminal = (props: RemixUiTerminalProps) => {
id="listenNetworkCheck"
onChange={listenOnNetwork}
type="checkbox"
title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you"
title={intl.formatMessage({ id: 'terminal.listenTitle' })}
/>
<label
className="pt-1 form-check-label custom-control-label text-nowrap"
title="If checked Remix will listen on all transactions mined in the current environment and not only transactions created by you"
title={intl.formatMessage({ id: 'terminal.listenTitle' })}
htmlFor="listenNetworkCheck"
data-id="listenNetworkCheckInput"
>

@ -296,6 +296,15 @@ export const renamePath = async (oldPath: string, newPath: string) => {
}
}
export const downloadPath = async (path: string) => {
const fileManager = plugin.fileManager
try {
await fileManager.download(path)
} catch (error) {
dispatch(displayPopUp('Oops! An error ocurred while downloading.' + error))
}
}
export const copyFile = async (src: string, dest: string) => {
const fileManager = plugin.fileManager

@ -13,7 +13,7 @@ declare global {
const _paq = window._paq = window._paq || [] //eslint-disable-line
export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => {
const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, copyFileName, copyPath, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props
const { actions, createNewFile, createNewFolder, deletePath, renamePath, downloadPath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, copyFileName, copyPath, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props
const contextMenuRef = useRef(null)
const intl = useIntl()
useEffect(() => {
@ -88,6 +88,10 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) =>
deletePath(getPath())
_paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'delete'])
break
case 'Download':
downloadPath(path)
_paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'download'])
break
case 'Push changes to gist':
_paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'pushToChangesoGist'])
pushChangesToGist(path, type)

@ -174,6 +174,14 @@ export const FileExplorer = (props: FileExplorerProps) => {
}
}
const downloadPath = async (path: string) => {
try {
props.dispatchDownloadPath(path)
} catch (error) {
props.modal('Download Failed', 'Unexpected error while downloading: ' + typeof error === 'string' ? error : error.message, 'Close', async () => {})
}
}
const uploadFile = (target) => {
const parentFolder = getFocusedFolder()
const expandPath = [...new Set([...props.expandPath, parentFolder])]
@ -488,6 +496,7 @@ export const FileExplorer = (props: FileExplorerProps) => {
createNewFile={handleNewFileInput}
createNewFolder={handleNewFolderInput}
deletePath={deletePath}
downloadPath={downloadPath}
renamePath={editModeOn}
runScript={runScript}
copy={handleCopyClick}

@ -22,6 +22,7 @@ export const FileSystemContext = createContext<{
dispatchCreateNewFolder: (path: string, rootDir: string) => Promise<void>,
dispatchDeletePath: (path: string[]) => Promise<void>,
dispatchRenamePath: (oldPath: string, newPath: string) => Promise<void>,
dispatchDownloadPath: (path:string) => Promise<void>,
dispatchCopyFile: (src: string, dest: string) => Promise<void>,
dispatchCopyFolder: (src: string, dest: string) => Promise<void>,
dispatchRunScript: (path: string) => Promise<void>,

@ -6,7 +6,7 @@ import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import { FileSystemContext } from '../contexts'
import { browserReducer, browserInitialState } from '../reducers/workspace'
import { initWorkspace, fetchDirectory, removeInputField, deleteWorkspace, clearPopUp, publishToGist, createNewFile, setFocusElement, createNewFolder,
deletePath, renamePath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace,
deletePath, renamePath, downloadPath, copyFile, copyFolder, runScript, emitContextMenuEvent, handleClickFile, handleExpandPath, addInputField, createWorkspace,
fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile, handleDownloadFiles, restoreBackupZip, cloneRepository, moveFile, moveFolder,
showAllBranches, switchBranch, createNewBranch, checkoutRemoteBranch, createSolidityGithubAction, createTsSolGithubAction, createSlitherGithubAction
} from '../actions'
@ -95,6 +95,10 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
await renamePath(oldPath, newPath)
}
const dispatchDownloadPath = async (path: string) => {
await downloadPath(path)
}
const dispatchCopyFile = async (src: string, dest: string) => {
await copyFile(src, dest)
}
@ -261,6 +265,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => {
dispatchCreateNewFolder,
dispatchDeletePath,
dispatchRenamePath,
dispatchDownloadPath,
dispatchCopyFile,
dispatchCopyFolder,
dispatchRunScript,

@ -73,18 +73,18 @@ export function Workspace () {
}, [currentWorkspace])
const renameCurrentWorkspace = () => {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), renameModalMessage(), 'OK', onFinishRenameWorkspace, '')
global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), renameModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishRenameWorkspace, '')
}
const createWorkspace = () => {
global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), createModalMessage(), 'OK', onFinishCreateWorkspace, '')
global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), createModalMessage(), intl.formatMessage({ id: 'filePanel.ok' }), onFinishCreateWorkspace, '')
}
const deleteCurrentWorkspace = () => {
global.modal(
intl.formatMessage({ id: 'filePanel.workspace.delete' }),
intl.formatMessage({ id: 'filePanel.workspace.deleteConfirm' }),
'OK',
intl.formatMessage({ id: 'filePanel.ok' }),
onFinishDeleteWorkspace,
''
)
@ -94,7 +94,7 @@ export function Workspace () {
global.modal(
intl.formatMessage({ id: 'filePanel.workspace.clone' }),
cloneModalMessage(),
'OK',
intl.formatMessage({ id: 'filePanel.ok' }),
handleTypingUrl,
''
)
@ -136,7 +136,7 @@ export function Workspace () {
try {
await global.dispatchRenameWorkspace(currentWorkspace, workspaceName)
} catch (e) {
global.modal('Rename Workspace', e.message, 'OK', () => {}, '')
global.modal(intl.formatMessage({ id: 'filePanel.workspace.rename' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '')
console.error(e)
}
}
@ -162,7 +162,7 @@ export function Workspace () {
try {
await global.dispatchCreateWorkspace(workspaceName, workspaceTemplateName, opts, initGitRepo)
} catch (e) {
global.modal('Create Workspace', e.message, 'OK', () => {}, '')
global.modal(intl.formatMessage({ id: 'filePanel.workspace.create' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '')
console.error(e)
}
}
@ -171,7 +171,7 @@ export function Workspace () {
try {
await global.dispatchDeleteWorkspace(global.fs.browser.currentWorkspace)
} catch (e) {
global.modal('Delete Workspace', e.message, 'OK', () => {}, '')
global.modal(intl.formatMessage({ id: 'filePanel.workspace.delete' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '')
console.error(e)
}
}
@ -186,7 +186,7 @@ export function Workspace () {
await global.dispatchSwitchToWorkspace(name)
global.dispatchHandleExpandPath([])
} catch (e) {
global.modal('Switch To Workspace', e.message, 'OK', () => {}, '')
global.modal(intl.formatMessage({ id: 'filePanel.workspace.switch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {}, '')
console.error(e)
}
}
@ -217,7 +217,13 @@ export function Workspace () {
if (url) {
global.dispatchCloneRepository(url)
} else {
global.modal('Clone Git Repository', 'Please provide a valid git repository url.', 'OK', () => {}, '')
global.modal(
intl.formatMessage({ id: 'filePanel.workspace.clone' }),
intl.formatMessage({ id: 'filePanel.workspace.cloneMessage' }),
intl.formatMessage({ id: 'filePanel.ok' }),
() => {},
''
)
}
}
@ -255,7 +261,7 @@ export function Workspace () {
}
} catch (e) {
console.error(e)
global.modal('Checkout Git Branch', e.message, 'OK', () => {})
global.modal(intl.formatMessage({ id: 'filePanel.checkoutGitBranch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {})
}
}
@ -264,7 +270,7 @@ export function Workspace () {
await global.dispatchCreateNewBranch(branchFilter)
_paq.push(['trackEvent', 'Workspace', 'GIT', 'switch_to_new_branch'])
} catch (e) {
global.modal('Checkout Git Branch', e.message, 'OK', () => {})
global.modal(intl.formatMessage({ id: 'filePanel.checkoutGitBranch' }), e.message, intl.formatMessage({ id: 'filePanel.ok' }), () => {})
}
}
@ -291,9 +297,9 @@ export function Workspace () {
</select>
<div id="ozcustomization" data-id="ozCustomization" ref={displayOzCustomRef} style={{display: 'none'}} className="mb-2">
<label className="form-check-label d-block mb-2" style={{fontWeight: "bolder"}}>Customize template</label>
<label className="form-check-label d-block mb-2" style={{fontWeight: "bolder"}}><FormattedMessage id='filePanel.customizeTemplate' /></label>
<label id="wsName" className="form-check-label d-block mb-1">Features</label>
<label id="wsName" className="form-check-label d-block mb-1"><FormattedMessage id='filePanel.features' /></label>
<div className="mb-2">
<div className="d-flex ml-2 custom-control custom-checkbox">
<input className="custom-control-input" type="checkbox" name="feature" value="mintable" id="mintable" ref={mintableCheckboxRef} />
@ -309,7 +315,7 @@ export function Workspace () {
</div>
</div>
<label id="wsName" className="form-check-label d-block mb-1">Upgradeability</label>
<label id="wsName" className="form-check-label d-block mb-1"><FormattedMessage id='filePanel.upgradeability' /></label>
<div onChange={handleUpgradeability}>
<div className="d-flex ml-2 custom-control custom-radio">
<input className="custom-control-input" type="radio" name="upgradeability" value="transparent" id="transparent" ref={transparentRadioRef} />
@ -323,7 +329,7 @@ export function Workspace () {
</div>
<label id="wsName" className="form-check-label" style={{fontWeight: "bolder"}} >Workspace name</label>
<label id="wsName" className="form-check-label" style={{fontWeight: "bolder"}} ><FormattedMessage id='filePanel.workspaceName' /></label>
<input type="text" data-id="modalDialogCustomPromptTextCreate" defaultValue={`remixDefault_${Date.now()}`} ref={workspaceCreateInput} className="form-control" />
<div className="d-flex py-2 align-items-center custom-control custom-checkbox">
@ -342,12 +348,12 @@ export function Workspace () {
className="m-0 form-check-label custom-control-label udapp_checkboxAlign"
title="Check option to initialize workspace as a new git repository"
>
Initialize workspace as a new git repository
<FormattedMessage id='filePanel.initGitRepositoryLabel' />
</label>
</div>
{!global.fs.gitConfig.username || !global.fs.gitConfig.email ?
(
<div className='text-warning'>Please add username and email to Remix GitHub Settings to use git features.</div>)
<div className='text-warning'><FormattedMessage id='filePanel.initGitRepositoryWarning' /></div>)
:<></>
}
@ -510,6 +516,7 @@ export function Workspace () {
toast={global.toast}
dispatchDeletePath={global.dispatchDeletePath}
dispatchRenamePath={global.dispatchRenamePath}
dispatchDownloadPath={global.dispatchDownloadPath}
dispatchUploadFile={global.dispatchUploadFile}
dispatchCopyFile={global.dispatchCopyFile}
dispatchCopyFolder={global.dispatchCopyFolder}
@ -549,6 +556,7 @@ export function Workspace () {
toast={global.toast}
dispatchDeletePath={global.dispatchDeletePath}
dispatchRenamePath={global.dispatchRenamePath}
dispatchDownloadPath={global.dispatchDownloadPath}
dispatchUploadFile={global.dispatchUploadFile}
dispatchCopyFile={global.dispatchCopyFile}
dispatchCopyFolder={global.dispatchCopyFolder}
@ -584,14 +592,14 @@ export function Workspace () {
<Dropdown.Menu as={CustomMenu} className='custom-dropdown-items branches-dropdown'>
<div data-id="custom-dropdown-menu">
<div className='d-flex text-dark' style={{ fontSize: 14, fontWeight: 'bold' }}>
<span className='mt-2 ml-2 mr-auto'>Switch branches</span>
<span className='mt-2 ml-2 mr-auto'><FormattedMessage id='filePanel.switchBranches' /></span>
<div className='pt-2 pr-2' onClick={() => { toggleBranches(false) }}><i className='fa fa-close'></i>
</div>
</div>
<div className='border-top py-2'>
<input
className='form-control border checkout-input bg-light'
placeholder='Find or create a branch.'
placeholder={intl.formatMessage({ id: 'filePanel.findOrCreateABranch' })}
style={{ minWidth: 225 }}
onChange={handleBranchFilterChange}
data-id='workspaceGitInput'
@ -614,13 +622,13 @@ export function Workspace () {
}) :
<Dropdown.Item onClick={switchToNewBranch}>
<div className="pl-1 pr-1" data-id="workspaceGitCreateNewBranch">
<i className="fas fa-code-branch pr-2"></i><span>Create branch: { branchFilter } from '{currentBranch}'</span>
<i className="fas fa-code-branch pr-2"></i><span><FormattedMessage id='filePanel.createBranch' />: { branchFilter } from '{currentBranch}'</span>
</div>
</Dropdown.Item>
}
</div>
{
(selectedWorkspace.branches || []).length > 4 && <div className='text-center border-top pt-2'><label style={{ fontSize: 12, cursor: 'pointer' }} onClick={showAllBranches}>View all branches</label></div>
(selectedWorkspace.branches || []).length > 4 && <div className='text-center border-top pt-2'><label style={{ fontSize: 12, cursor: 'pointer' }} onClick={showAllBranches}><FormattedMessage id='filePanel.viewAllBranches' /></label></div>
}
</div>
</Dropdown.Menu>

@ -96,6 +96,7 @@ export interface FileExplorerProps {
toast: (toasterMsg: string) => void,
dispatchDeletePath: (path: string[]) => Promise<void>,
dispatchRenamePath: (oldPath: string, newPath: string) => Promise<void>,
dispatchDownloadPath: (path: string) => Promise<void>,
dispatchUploadFile: (target?: React.SyntheticEvent, targetFolder?: string) => Promise<void>,
dispatchCopyFile: (src: string, dest: string) => Promise<void>,
dispatchCopyFolder: (src: string, dest: string) => Promise<void>,
@ -127,6 +128,7 @@ export interface FileExplorerContextMenuProps {
createNewFolder: (parentFolder?: string) => void,
deletePath: (path: string | string[]) => void,
renamePath: (path: string, type: string) => void,
downloadPath: (path: string) => void,
hideContextMenu: () => void,
publishToGist?: (path?: string, type?: string) => void,
pushChangesToGist?: (path?: string, type?: string) => void,

@ -24,6 +24,12 @@ export const contextMenuActions: MenuItems = [{
type: ['file', 'folder', 'gist'],
multiselect: false,
label: ''
},{
id: 'download',
name: 'Download',
type: ['file'],
multiselect: false,
label: ''
}, {
id: 'run',
name: 'Run',

Loading…
Cancel
Save