Merge branch 'master' into master

pull/4487/head
Aniket 1 year ago committed by GitHub
commit 9ac2402491
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 70
      apps/remix-ide/src/app/plugins/remixd-handle.tsx
  2. 33
      apps/remix-ide/src/app/tabs/locales/en/editor.json
  3. 1
      apps/remix-ide/src/app/tabs/locales/en/home.json
  4. 46
      apps/remix-ide/src/app/tabs/locales/en/index.js
  5. 19
      apps/remix-ide/src/app/tabs/locales/en/publishToStorage.json
  6. 19
      apps/remix-ide/src/app/tabs/locales/en/remixApp.json
  7. 16
      apps/remix-ide/src/app/tabs/locales/en/remixd.json
  8. 1
      apps/remix-ide/src/app/tabs/locales/en/solidity.json
  9. 47
      apps/remix-ide/src/app/tabs/locales/es/index.js
  10. 47
      apps/remix-ide/src/app/tabs/locales/fr/index.js
  11. 47
      apps/remix-ide/src/app/tabs/locales/it/index.js
  12. 49
      apps/remix-ide/src/app/tabs/locales/zh/index.js
  13. 13
      libs/remix-ui/app/src/lib/remix-app/components/modals/enter.tsx
  14. 59
      libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx
  15. 12
      libs/remix-ui/app/src/lib/remix-app/context/provider.tsx
  16. 107
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  17. 48
      libs/remix-ui/publish-to-storage/src/lib/publish-to-storage.tsx
  18. 8
      libs/remix-ui/renderer/src/lib/renderer.tsx
  19. 3
      libs/remix-ui/vertical-icons-panel/src/lib/components/Home.tsx

@ -1,5 +1,6 @@
/* eslint-disable no-unused-vars */
import React, {useRef, useState, useEffect} from 'react' // eslint-disable-line
import {FormattedMessage} from 'react-intl'
import {WebsocketPlugin} from '@remixproject/engine-web'
import * as packageJson from '../../../../../package.json'
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
@ -84,7 +85,7 @@ export class RemixdHandle extends WebsocketPlugin {
console.log(error)
const alert: AlertModal = {
id: 'connectionAlert',
message: 'Cannot connect to the remixd daemon. Please make sure you have the remixd running in the background.'
message: window._intl.formatMessage({id: 'remixd.connectionAlert1'}),
}
this.call('notification', 'alert', alert)
this.canceled()
@ -95,7 +96,7 @@ export class RemixdHandle extends WebsocketPlugin {
clearInterval(intervalId)
const alert: AlertModal = {
id: 'connectionAlert',
message: 'Connection to remixd terminated. Please make sure remixd is still running in the background.'
message: window._intl.formatMessage({id: 'remixd.connectionAlert2'}),
}
this.call('notification', 'alert', alert)
this.canceled()
@ -115,10 +116,10 @@ export class RemixdHandle extends WebsocketPlugin {
// warn the user only if he/she is in the browser context
const mod: AppModal = {
id: 'remixdConnect',
title: 'Access file system using remixd',
title: window._intl.formatMessage({id: 'remixd.remixdConnect'}),
message: remixdDialog(),
okLabel: 'Connect',
cancelLabel: 'Cancel'
okLabel: window._intl.formatMessage({id: 'remixd.connect'}),
cancelLabel: window._intl.formatMessage({id: 'remixd.cancel'}),
}
const result = await this.call('notification', 'modal', mod)
if (result) {
@ -159,45 +160,62 @@ function remixdDialog() {
<>
<div className="">
<div className="mb-2 text-break">
Access your local file system from Remix IDE using{' '}
<a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">
Remixd NPM package
</a>
.
<FormattedMessage
id="remixd.text1"
values={{
a: (chunks) => (
<a target="_blank" href="https://www.npmjs.com/package/@remix-project/remixd">
{chunks}
</a>
),
}}
/>
</div>
<div className="mb-2 text-break">
Remixd{' '}
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">
documentation
</a>
.
<FormattedMessage
id="remixd.text2"
values={{
a: (chunks) => (
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html">
{chunks}
</a>
),
}}
/>
</div>
<div className="mb-2 text-break">
The remixd command is:
<FormattedMessage id="remixd.text3" />
<br />
<b>{commandText}</b>
</div>
<div className="mb-2 text-break">
The remixd command without options uses the terminal's current directory as the shared directory and the shared Remix domain can only be https://remix.ethereum.org,
https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org
<FormattedMessage id="remixd.text4" />
</div>
<div className="mb-2 text-break">
Example command with flags: <br />
<FormattedMessage id="remixd.text5" /> <br />
<b>{fullCommandText}</b>
</div>
<div className="mb-2 text-break">
For info about ports, see{' '}
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">
Remixd ports usage
</a>
<FormattedMessage
id="remixd.text6"
values={{
a: (chunks) => (
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#ports-usage">
{chunks}
</a>
),
}}
/>
</div>
<div className="mb-2 text-break">
<FormattedMessage id="remixd.text7" />
</div>
<div className="mb-2 text-break">This feature is still in Alpha. We recommend to keep a backup of the shared folder.</div>
<div className="mb-2 text-break">
<h6 className="text-danger">
Before using, make sure remixd version is latest i.e. <b>v{remixdVersion}</b>
<FormattedMessage id="remixd.text8" /> <b>v{remixdVersion}</b>
<br></br>
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/remixd.html#update-to-the-latest-remixd">
Read here how to update it
<FormattedMessage id="remixd.text9" />
</a>
</h6>
</div>

@ -0,0 +1,33 @@
{
"editor.keyboardShortcuts": "Keyboard Shortcuts",
"editor.keyboardShortcuts.text1": "Compile the current contract",
"editor.keyboardShortcuts.text2": "Open the File Explorer",
"editor.keyboardShortcuts.text3": "Open the Plugin Manager",
"editor.keyboardShortcuts.text4": "Compile the current contract & Run an associated script",
"editor.editorKeyboardShortcuts": "Editor Keyboard Shortcuts",
"editor.editorKeyboardShortcuts.text1": "Format the code in the current file",
"editor.importantLinks": "Important Links",
"editor.importantLinks.text1": "Official website about the Remix Project",
"editor.importantLinks.text2": "Official documentation",
"editor.title1": "Pasted Code Alert",
"editor.title1.message1": "You have just pasted a code snippet or contract in the editor.",
"editor.title1.message2": "Make sure you fully understand this code before deploying or interacting with it. Don't get scammed!",
"editor.title1.message3": "Running untrusted code can put your wallet <span> at risk </span>. In a worst-case scenario, you could <span>lose all your money</span>.",
"editor.title1.message4": "If you don't fully understand it, please don't run this code.",
"editor.title1.message5": "If you are not a smart contract developer, ask someone you trust who has the skills to determine if this code is safe to use.",
"editor.title1.message6": "See <a> these recommendations </a> for more information.",
"editor.zoomIn": "Zoom In",
"editor.zoomOut": "Zoom Out",
"editor.formatCode": "Format Code",
"editor.generateDocumentation": "Generate documentation for this function",
"editor.generateDocumentation2": "Generate documentation for the function \"{name}\"",
"editor.generateDocumentationByAI": "solidity code: {content}\n Generate the documentation for the function {currentFunction} using the Doxygen style syntax",
"editor.explainFunction": "Explain this function",
"editor.explainFunction2": "Explain the function \"{name}\"",
"editor.explainFunctionByAI": "solidity code: {content}\n Explain the function {currentFunction}",
"editor.executeFreeFunction": "Run a free function",
"editor.executeFreeFunction2": "Run the free function \"{name}\"",
"editor.toastText1": "This can only execute free function",
"editor.toastText2": "Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.",
"editor.text": "The file is opened in <b>read-only</b> mode."
}

@ -1,4 +1,5 @@
{
"home.home": "Home",
"home.scamAlert": "Scam Alert",
"home.scamAlertText": "The only URL Remix uses is remix.ethereum.org",
"home.scamAlertText2": "Beware of online videos promoting \"liquidity front runner bots\"",

@ -1,37 +1,13 @@
import debuggerJson from './debugger.json';
import filePanelJson from './filePanel.json';
import homeJson from './home.json';
import panelJson from './panel.json';
import pluginManagerJson from './pluginManager.json';
import searchJson from './search.json';
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 electronJson from './electron.json';
import solUmlGenJson from './solUmlGen.json'
import remixAppJson from './remixApp.json'
import remixUiTabsJson from './remixUiTabs.json'
import circuitJson from './circuit.json';
function readAndCombineJsonFiles() {
const dataContext = require.context('./', true, /\.json$/)
export default {
...debuggerJson,
...filePanelJson,
...homeJson,
...panelJson,
...pluginManagerJson,
...searchJson,
...settingsJson,
...solidityJson,
...terminalJson,
...udappJson,
...solidityUnitTestingJson,
...permissionHandlerJson,
...electronJson,
...solUmlGenJson,
...remixAppJson,
...remixUiTabsJson,
...circuitJson
let combinedData = {}
dataContext.keys().forEach((key) => {
const jsonData = dataContext(key)
combinedData = {...combinedData, ...jsonData}
})
return combinedData
}
export default readAndCombineJsonFiles()

@ -0,0 +1,19 @@
{
"publishToStorage.title1": "Publish To Storage",
"publishToStorage.title1.message": "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.",
"publishToStorage.title2": "Published {name}'s Metadata and Sources",
"publishToStorage.title2.message": "Metadata and sources of \"{name}\" were published successfully.",
"publishToStorage.title3": "Swarm Publish Failed",
"publishToStorage.title4": "IPFS Settings",
"publishToStorage.title4.message1": "You have not set your own custom IPFS settings.",
"publishToStorage.title4.message2": "We won’t be providing a public endpoint anymore for publishing your contracts to IPFS.",
"publishToStorage.title4.message3": "Instead of that, 4 options are now available:",
"publishToStorage.title4.message4": "DEFAULT OPTION: Use the public INFURA node. This will not guarantee your data will persist.",
"publishToStorage.title4.message5": "Use your own INFURA IPFS node. This requires a subscription. <a>Learn more</a>",
"publishToStorage.title4.message6": "Use any external IPFS which doesn’t require any authentification.",
"publishToStorage.title4.message7": "Use your own local ipfs node (which usually runs under http://localhost:5001)",
"publishToStorage.title4.message8": "You can update your IPFS settings in the SETTINGS tab.",
"publishToStorage.title4.message9": "Now the default option will be used.",
"publishToStorage.title5": "IPFS Publish Failed",
"publishToStorage.title5.message": "Failed to publish metadata file and sources to {storage}, please check the {storage} gateways is available."
}

@ -1,3 +1,20 @@
{
"remixApp.scrollToSeeAllTabs": "Scroll to see all tabs"
"remixApp.scrollToSeeAllTabs": "Scroll to see all tabs",
"remixApp.alert": "Alert",
"remixApp.ok": "OK",
"remixApp.enterText1": "Welcome to Remix IDE",
"remixApp.enterText2": "In order to understand your needs better, we would like to know how you typically use Remix",
"remixApp.enterText3": "Learning - discovering web3 development",
"remixApp.enterText4": "Prototyping - trying out concepts and techniques",
"remixApp.enterText5": "Developing projects - Remix as your main dev tool",
"remixApp.enterText6": "Production - only deployments",
"remixApp.matomoText1": "An Opt-in version of <a>Matomo</a>, an open source data analytics platform is being used to improve Remix IDE.",
"remixApp.matomoText2": "We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.",
"remixApp.matomoText3": "All data collected through Matomo is stored on our own server - no data is ever given to third parties.",
"remixApp.matomoText4": "We do not collect nor store any personally identifiable information (PII).",
"remixApp.matomoText5": "For more info, see: <a>Matomo Analytics on Remix iDE</a>.",
"remixApp.matomoText6": "You can change your choice in the Settings panel anytime.",
"remixApp.matomoTitle": "Help us to improve Remix IDE",
"remixApp.accept": "Accept",
"remixApp.decline": "Decline"
}

@ -0,0 +1,16 @@
{
"remixd.connectionAlert1": "Cannot connect to the remixd daemon. Please make sure you have the remixd running in the background.",
"remixd.connectionAlert2": "Connection to remixd terminated. Please make sure remixd is still running in the background.",
"remixd.remixdConnect": "Access file system using remixd",
"remixd.connect": "Connect",
"remixd.cancel": "Cancel",
"remixd.text1": "Access your local file system from Remix IDE using <a>Remixd NPM package</a>.",
"remixd.text2": "Remixd <a>documentation</a>.",
"remixd.text3": "The remixd command is:",
"remixd.text4": "The remixd command without options uses the terminal's current directory as the shared directory and the shared Remix domain can only be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org",
"remixd.text5": "Example command with flags:",
"remixd.text6": "For info about ports, see <a>Remixd ports usage</a>",
"remixd.text7": "This feature is still in Alpha. We recommend to keep a backup of the shared folder.",
"remixd.text8": "Before using, make sure remixd version is latest i.e.",
"remixd.text9": "Read here how to update it"
}

@ -1,5 +1,6 @@
{
"solidity.displayName": "Solidity compiler",
"solidity.openaigptMessage": "solidity code: {content}\n error message: {messageText}\n explain why the error occurred and how to fix it.",
"solidity._comment_compiler-container.tsx": "libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx",
"solidity.compiler": "Compiler",

@ -1,36 +1,17 @@
import debuggerJson from './debugger.json';
import filePanelJson from './filePanel.json';
import homeJson from './home.json';
import panelJson from './panel.json';
import pluginManagerJson from './pluginManager.json';
import searchJson from './search.json';
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 solUmlGenJson from './solUmlGen.json'
import remixAppJson from './remixApp.json'
import remixUiTabsJson from './remixUiTabs.json'
import enJson from '../en';
import enJson from '../en'
function readAndCombineJsonFiles() {
const dataContext = require.context('./', true, /\.json$/)
let combinedData = {}
dataContext.keys().forEach((key) => {
const jsonData = dataContext(key)
combinedData = {...combinedData, ...jsonData}
})
return combinedData
}
// There may have some un-translated content. Always fill in the gaps with EN JSON.
// No need for a defaultMessage prop when render a FormattedMessage component.
export default Object.assign({}, enJson, {
...debuggerJson,
...filePanelJson,
...homeJson,
...panelJson,
...pluginManagerJson,
...searchJson,
...settingsJson,
...solidityJson,
...terminalJson,
...udappJson,
...solidityUnitTestingJson,
...permissionHandlerJson,
...solUmlGenJson,
...remixAppJson,
...remixUiTabsJson,
})
export default Object.assign({}, enJson, readAndCombineJsonFiles())

@ -1,36 +1,17 @@
import debuggerJson from './debugger.json';
import filePanelJson from './filePanel.json';
import homeJson from './home.json';
import panelJson from './panel.json';
import pluginManagerJson from './pluginManager.json';
import searchJson from './search.json';
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 solUmlGenJson from './solUmlGen.json'
import remixAppJson from './remixApp.json'
import remixUiTabsJson from './remixUiTabs.json'
import enJson from '../en';
import enJson from '../en'
function readAndCombineJsonFiles() {
const dataContext = require.context('./', true, /\.json$/)
let combinedData = {}
dataContext.keys().forEach((key) => {
const jsonData = dataContext(key)
combinedData = {...combinedData, ...jsonData}
})
return combinedData
}
// There may have some un-translated content. Always fill in the gaps with EN JSON.
// No need for a defaultMessage prop when render a FormattedMessage component.
export default Object.assign({}, enJson, {
...debuggerJson,
...filePanelJson,
...homeJson,
...panelJson,
...pluginManagerJson,
...searchJson,
...settingsJson,
...solidityJson,
...terminalJson,
...udappJson,
...solidityUnitTestingJson,
...permissionHandlerJson,
...solUmlGenJson,
...remixAppJson,
...remixUiTabsJson,
})
export default Object.assign({}, enJson, readAndCombineJsonFiles())

@ -1,36 +1,17 @@
import debuggerJson from './debugger.json';
import filePanelJson from './filePanel.json';
import homeJson from './home.json';
import panelJson from './panel.json';
import pluginManagerJson from './pluginManager.json';
import searchJson from './search.json';
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 solUmlGenJson from './solUmlGen.json'
import remixAppJson from './remixApp.json'
import remixUiTabsJson from './remixUiTabs.json'
import enJson from '../en';
import enJson from '../en'
function readAndCombineJsonFiles() {
const dataContext = require.context('./', true, /\.json$/)
let combinedData = {}
dataContext.keys().forEach((key) => {
const jsonData = dataContext(key)
combinedData = {...combinedData, ...jsonData}
})
return combinedData
}
// There may have some un-translated content. Always fill in the gaps with EN JSON.
// No need for a defaultMessage prop when render a FormattedMessage component.
export default Object.assign({}, enJson, {
...debuggerJson,
...filePanelJson,
...homeJson,
...panelJson,
...pluginManagerJson,
...searchJson,
...settingsJson,
...solidityJson,
...terminalJson,
...udappJson,
...solidityUnitTestingJson,
...permissionHandlerJson,
...solUmlGenJson,
...remixAppJson,
...remixUiTabsJson,
})
export default Object.assign({}, enJson, readAndCombineJsonFiles())

@ -1,38 +1,17 @@
import debuggerJson from './debugger.json';
import filePanelJson from './filePanel.json';
import homeJson from './home.json';
import panelJson from './panel.json';
import pluginManagerJson from './pluginManager.json';
import searchJson from './search.json';
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 solUmlGenJson from './solUmlGen.json'
import remixAppJson from './remixApp.json'
import remixUiTabsJson from './remixUiTabs.json'
import enJson from '../en';
import circuitJson from './circuit.json';
import enJson from '../en'
function readAndCombineJsonFiles() {
const dataContext = require.context('./', true, /\.json$/)
let combinedData = {}
dataContext.keys().forEach((key) => {
const jsonData = dataContext(key)
combinedData = {...combinedData, ...jsonData}
})
return combinedData
}
// There may have some un-translated content. Always fill in the gaps with EN JSON.
// No need for a defaultMessage prop when render a FormattedMessage component.
export default Object.assign({}, enJson, {
...debuggerJson,
...filePanelJson,
...homeJson,
...panelJson,
...pluginManagerJson,
...searchJson,
...settingsJson,
...solidityJson,
...terminalJson,
...udappJson,
...solidityUnitTestingJson,
...permissionHandlerJson,
...solUmlGenJson,
...remixAppJson,
...remixUiTabsJson,
...circuitJson
})
export default Object.assign({}, enJson, readAndCombineJsonFiles())

@ -1,4 +1,5 @@
import React, {useContext, useEffect, useState} from 'react'
import {FormattedMessage} from 'react-intl'
import {AppContext} from '../../context/context'
import {UsageTypes} from '../../types'
import { type } from 'os'
@ -30,19 +31,19 @@ const EnterDialog = (props: EnterDialogProps) => {
}}
>
<div className="modal-header d-flex flex-column">
<h3 className='text-dark'>Welcome to Remix IDE</h3>
<h3 className='text-dark'><FormattedMessage id="remixApp.enterText1" /></h3>
<div className='d-flex flex-row pt-2'>
<h6 className="modal-title text-dark" data-id={`EnterModalDialogModalTitle-react`}>
In order to understand your needs better, we would like to know how you typically use Remix
<FormattedMessage id="remixApp.enterText2" />
</h6>
<i className="text-dark fal fa-door-open text-center" style={{minWidth: "100px", fontSize: "xxx-large"}}></i>
</div>
</div>
<div className="modal-body text-break remixModalBody d-flex flex-column p-3 justify-content-between" data-id={`EnterModalDialogModalBody-react`}>
<button className="btn btn-secondary text-left" data-id="beginnerbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Beginner)}}>Learning - discovering web3 development</button>
<button className="btn btn-secondary my-1 text-left" data-id="prototyperbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Prototyper)}}>Prototyping - trying out concepts and techniques</button>
<button className="btn btn-secondary text-left" data-id="advanceUserbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Advance)}}>Developing projects - Remix as your main dev tool</button>
<button className="btn btn-secondary mt-1 text-left" data-id="productionbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Production)}}>Production - only deployments</button>
<button className="btn btn-secondary text-left" data-id="beginnerbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Beginner)}}><FormattedMessage id="remixApp.enterText3" /></button>
<button className="btn btn-secondary my-1 text-left" data-id="prototyperbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Prototyper)}}><FormattedMessage id="remixApp.enterText4" /></button>
<button className="btn btn-secondary text-left" data-id="advanceUserbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Advance)}}><FormattedMessage id="remixApp.enterText5" /></button>
<button className="btn btn-secondary mt-1 text-left" data-id="productionbtn" style={{minWidth: "100px"}} onClick={() => {enterAs(UsageTypes.Production)}}><FormattedMessage id="remixApp.enterText6" /></button>
</div>
</div>
</div>

@ -1,4 +1,5 @@
import React, {useContext, useEffect, useState} from 'react'
import {FormattedMessage} from 'react-intl'
import {AppContext} from '../../context/context'
import {useDialogDispatchers} from '../../context/provider'
declare global {
@ -9,7 +10,7 @@ declare global {
const _paq = (window._paq = window._paq || [])
interface MatomoDialogProps {
okFn: () => void,
okFn: () => void
hide: boolean
}
@ -22,25 +23,41 @@ const MatomoDialog = (props: MatomoDialogProps) => {
return (
<>
<p>
An Opt-in version of{' '}
<a href="https://matomo.org" target="_blank" rel="noreferrer">
Matomo
</a>
, an open source data analytics platform is being used to improve Remix IDE.
<FormattedMessage
id="remixApp.matomoText1"
values={{
a: (chunks) => (
<a href="https://matomo.org" target="_blank" rel="noreferrer">
{chunks}
</a>
),
}}
/>
</p>
<p>We realize that our users have sensitive information in their code and that their privacy - your privacy - must be protected.</p>
<p>
All data collected through Matomo is stored on our own server - no data is ever given to third parties.
<FormattedMessage id="remixApp.matomoText2" />
</p>
<p>We do not collect nor store any personally identifiable information (PII).</p>
<p>
For more info, see:{' '}
<a href="https://medium.com/p/66ef69e14931/" target="_blank" rel="noreferrer">
Matomo Analytics on Remix iDE
</a>
.
<FormattedMessage id="remixApp.matomoText3" />
</p>
<p>
<FormattedMessage id="remixApp.matomoText4" />
</p>
<p>
<FormattedMessage
id="remixApp.matomoText5"
values={{
a: (chunks) => (
<a href="https://medium.com/p/66ef69e14931/" target="_blank" rel="noreferrer">
{chunks}
</a>
),
}}
/>
</p>
<p>
<FormattedMessage id="remixApp.matomoText6" />
</p>
<p>You can change your choice in the Settings panel anytime.</p>
</>
)
}
@ -49,12 +66,12 @@ const MatomoDialog = (props: MatomoDialogProps) => {
if (visible && showMatamo) {
modal({
id: 'matomoModal',
title: 'Help us to improve Remix IDE',
title: <FormattedMessage id="remixApp.matomoTitle" />,
message: message(),
okLabel: 'Accept',
okLabel: <FormattedMessage id="remixApp.accept" />,
okFn: handleModalOkClick,
cancelLabel: 'Decline',
cancelFn: declineModal
cancelLabel: <FormattedMessage id="remixApp.decline" />,
cancelFn: declineModal,
})
}
}, [visible])
@ -62,13 +79,13 @@ const MatomoDialog = (props: MatomoDialogProps) => {
const declineModal = async () => {
settings.updateMatomoAnalyticsChoice(false)
// revoke tracking consent
_paq.push(['forgetConsentGiven']);
_paq.push(['forgetConsentGiven'])
setVisible(false)
}
const handleModalOkClick = async () => {
// user has given consent to process their data
_paq.push(['setConsentGiven']);
_paq.push(['setConsentGiven'])
settings.updateMatomoAnalyticsChoice(true)
setVisible(false)
props.okFn()

@ -1,4 +1,5 @@
import React, {useReducer} from 'react'
import {useIntl, IntlShape} from 'react-intl'
import {modalActionTypes} from '../actions/modals'
import {AlertModal, AppModal} from '../interface'
import {modalReducer} from '../reducer/modals'
@ -6,6 +7,12 @@ import {ModalInitialState} from '../state/modals'
import {ModalTypes} from '../types'
import {AppContext, dispatchModalContext, modalContext, platformContext, onLineContext} from './context'
declare global {
interface Window {
_intl: IntlShape
}
}
export const ModalProvider = ({children = [], reducer = modalReducer, initialState = ModalInitialState} = {}) => {
const [{modals, toasters, focusModal, focusToaster}, dispatch] = useReducer(reducer, initialState)
@ -43,9 +50,9 @@ export const ModalProvider = ({children = [], reducer = modalReducer, initialSta
const alert = (modalData: AlertModal) => {
return modal({
id: modalData.id,
title: modalData.title || 'Alert',
title: modalData.title || window._intl.formatMessage({id: 'remixApp.alert'}),
message: modalData.message || modalData.title,
okLabel: 'OK',
okLabel: window._intl.formatMessage({id: 'remixApp.ok'}),
okFn: (value?: any) => {},
cancelLabel: '',
cancelFn: () => {}
@ -81,6 +88,7 @@ export const ModalProvider = ({children = [], reducer = modalReducer, initialSta
}
export const AppProvider = ({children = [], value = {}} = null) => {
window._intl = useIntl()
return (
<AppContext.Provider value={value}>
<ModalProvider>

@ -1,7 +1,9 @@
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl'
import { isArray } from 'lodash'
import Editor, { loader, Monaco } from '@monaco-editor/react'
import { AlertModal } from '@remix-ui/app'
import { QueryParams } from '@remix-project/remix-lib'
import { reducerActions, reducerListener, initialState } from './actions/editor'
import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity'
import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo'
@ -90,6 +92,18 @@ type errorMarker = {
loader.config({ paths: { vs: 'assets/js/monaco-editor/min/vs' } })
const queryParams = new QueryParams()
// @ts-ignore
const queryLocale = queryParams.get().lang
const locales = {
zh: 'zh-cn',
en: '',
fr: 'fr',
it: 'it',
es: 'es'
}
loader.config({ "vs/nls": { availableLanguages: { "*": locales[queryLocale] || '' } } })
export type DecorationsReturn = {
currentDecorations: Array<string>
registeredDecorations?: Array<any>
@ -130,6 +144,7 @@ export interface EditorUIProps {
editorAPI: EditorAPIType
}
export const EditorUI = (props: EditorUIProps) => {
const intl = useIntl()
const [, setCurrentBreakpoints] = useState({})
const defaultEditorValue = `
\t\t\t\t\t\t\t ____ _____ __ __ ___ __ __ ___ ____ _____
@ -137,16 +152,16 @@ export const EditorUI = (props: EditorUIProps) => {
\t\t\t\t\t\t\t| |_) | | _| | |\\/| | | | \\ / | | | | | | | _|
\t\t\t\t\t\t\t| _ < | |___ | | | | | | / \\ | | | |_| | | |___
\t\t\t\t\t\t\t|_| \\_\\ |_____| |_| |_| |___| /_/\\_\\ |___| |____/ |_____|\n\n
\t\t\t\t\t\t\tKeyboard Shortcuts:\n
\t\t\t\t\t\t\t\tCTRL + S: Compile the current contract\n
\t\t\t\t\t\t\t\tCTRL + Shift + F : Open the File Explorer\n
\t\t\t\t\t\t\t\tCTRL + Shift + A : Open the Plugin Manager\n
\t\t\t\t\t\t\t\tCTRL + SHIFT + S: Compile the current contract & Run an associated script\n
\t\t\t\t\t\t\tEditor Keyboard Shortcuts:\n
\t\t\t\t\t\t\t\tCTRL + Alt + F : Format the code in the current file\n
\t\t\t\t\t\t\tImportant Links:\n
\t\t\t\t\t\t\t\tOfficial website about the Remix Project: https://remix-project.org/\n
\t\t\t\t\t\t\t\tOfficial documentation: https://remix-ide.readthedocs.io/en/latest/\n
\t\t\t\t\t\t\t${intl.formatMessage({id: 'editor.keyboardShortcuts'})}:\n
\t\t\t\t\t\t\t\tCTRL + S: ${intl.formatMessage({id: 'editor.keyboardShortcuts.text1'})}\n
\t\t\t\t\t\t\t\tCTRL + Shift + F : ${intl.formatMessage({id: 'editor.keyboardShortcuts.text2'})}\n
\t\t\t\t\t\t\t\tCTRL + Shift + A : ${intl.formatMessage({id: 'editor.keyboardShortcuts.text3'})}\n
\t\t\t\t\t\t\t\tCTRL + SHIFT + S: ${intl.formatMessage({id: 'editor.keyboardShortcuts.text4'})}\n
\t\t\t\t\t\t\t${intl.formatMessage({id: 'editor.editorKeyboardShortcuts'})}:\n
\t\t\t\t\t\t\t\tCTRL + Alt + F : ${intl.formatMessage({id: 'editor.editorKeyboardShortcuts.text1'})}\n
\t\t\t\t\t\t\t${intl.formatMessage({id: 'editor.importantLinks'})}:\n
\t\t\t\t\t\t\t\t${intl.formatMessage({id: 'editor.importantLinks.text1'})}: https://remix-project.org/\n
\t\t\t\t\t\t\t\t${intl.formatMessage({id: 'editor.importantLinks.text2'})}: https://remix-ide.readthedocs.io/en/latest/\n
\t\t\t\t\t\t\t\tGithub: https://github.com/ethereum/remix-project\n
\t\t\t\t\t\t\t\tGitter: https://gitter.im/ethereum/remix\n
\t\t\t\t\t\t\t\tMedium: https://medium.com/remix-ide\n
@ -614,27 +629,34 @@ export const EditorUI = (props: EditorUIProps) => {
if (!pasteCodeRef.current && e && e.range && e.range.startLineNumber >= 0 && e.range.endLineNumber >= 0 && e.range.endLineNumber - e.range.startLineNumber > 10) {
const modalContent: AlertModal = {
id: 'newCodePasted',
title: 'Pasted Code Alert',
title: intl.formatMessage({id: 'editor.title1'}),
message: (
<div>
{' '}
<i className="fas fa-exclamation-triangle text-danger mr-1"></i>
You have just pasted a code snippet or contract in the editor.
<FormattedMessage id="editor.title1.message1" />
<div>
Make sure you fully understand this code before deploying or interacting with it. Don't get scammed!
<FormattedMessage id="editor.title1.message2" />
<div className="mt-2">
<FormattedMessage id="editor.title1.message3" values={{span: (chunks) => <span className="text-warning">{chunks}</span>}} />
</div>
<div className="text-warning mt-2">
<FormattedMessage id="editor.title1.message4" />
</div>
<div className="mt-2">
Running untrusted code can put your wallet <span className="text-warning"> at risk </span>. In a worst-case scenario, you could{' '}
<span className="text-warning">lose all your money</span>.
<FormattedMessage id="editor.title1.message5" />
</div>
<div className="text-warning mt-2">If you don't fully understand it, please don't run this code.</div>
<div className="mt-2">If you are not a smart contract developer, ask someone you trust who has the skills to determine if this code is safe to use.</div>
<div className="mt-2">
See{' '}
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/security.html">
{' '}
these recommendations{' '}
</a>{' '}
for more information.
<FormattedMessage
id="editor.title1.message6"
values={{
a: (chunks) => (
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/security.html">
{chunks}
</a>
),
}}
/>
</div>
</div>
</div>
@ -648,7 +670,7 @@ export const EditorUI = (props: EditorUIProps) => {
// add context menu items
const zoominAction = {
id: 'zoomIn',
label: 'Zoom In',
label: intl.formatMessage({id: 'editor.zoomIn'}),
contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'zooming', // create a new grouping
keybindings: [
@ -661,7 +683,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
const zoomOutAction = {
id: 'zoomOut',
label: 'Zoom Out',
label: intl.formatMessage({id: 'editor.zoomOut'}),
contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'zooming', // create a new grouping
keybindings: [
@ -674,7 +696,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
const formatAction = {
id: 'autoFormat',
label: 'Format Code',
label: intl.formatMessage({id: 'editor.formatCode'}),
contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'formatting', // create a new grouping
keybindings: [
@ -690,17 +712,14 @@ export const EditorUI = (props: EditorUIProps) => {
let gptGenerateDocumentationAction
const executeGptGenerateDocumentationAction = {
id: 'generateDocumentation',
label: 'Generate documentation for this function',
label: intl.formatMessage({id: 'editor.generateDocumentation'}),
contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [],
run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file)
const message = `
solidity code: ${content}
Generate the documentation for the function ${currentFunction.current} using the Doxygen style syntax
`
const message = intl.formatMessage({id: 'editor.generateDocumentationByAI'}, {content, currentFunction: currentFunction.current})
await props.plugin.call('openaigpt', 'message', message)
_paq.push(['trackEvent', 'ai', 'openai', 'generateDocumentation'])
},
@ -709,17 +728,14 @@ export const EditorUI = (props: EditorUIProps) => {
let gptExplainFunctionAction
const executegptExplainFunctionAction = {
id: 'explainFunction',
label: 'Explain this function',
label: intl.formatMessage({id: 'editor.explainFunction'}),
contextMenuOrder: 1, // choose the order
contextMenuGroupId: 'gtp', // create a new grouping
keybindings: [],
run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
const content = await props.plugin.call('fileManager', 'readFile', file)
const message = `
solidity code: ${content}
Explain the function ${currentFunction.current}
`
const message = intl.formatMessage({id: 'editor.explainFunctionByAI'}, {content, currentFunction: currentFunction.current})
await props.plugin.call('openaigpt', 'message', message)
_paq.push(['trackEvent', 'ai', 'openai', 'explainFunction'])
},
@ -729,7 +745,7 @@ export const EditorUI = (props: EditorUIProps) => {
let freeFunctionAction
const executeFreeFunctionAction = {
id: 'executeFreeFunction',
label: 'Run a free function',
label: intl.formatMessage({id: 'editor.executeFreeFunction'}),
contextMenuOrder: 0, // choose the order
contextMenuGroupId: 'execute', // create a new grouping
precondition: 'freeFunctionCondition',
@ -746,10 +762,10 @@ export const EditorUI = (props: EditorUIProps) => {
const file = await props.plugin.call('fileManager', 'getCurrentFile')
props.plugin.call('solidity-script', 'execute', file, freeFunctionNode.name)
} else {
props.plugin.call('notification', 'toast', 'This can only execute free function')
props.plugin.call('notification', 'toast', intl.formatMessage({id: 'editor.toastText1'}))
}
} else {
props.plugin.call('notification', 'toast', 'Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.')
props.plugin.call('notification', 'toast', intl.formatMessage({id: 'editor.toastText2'}))
}
},
}
@ -787,15 +803,15 @@ export const EditorUI = (props: EditorUIProps) => {
const { nodesAtPosition } = await retrieveNodesAtPosition(props.editorAPI, props.plugin)
const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction')
if (freeFunctionNode) {
executeFreeFunctionAction.label = `Run the free function "${freeFunctionNode.name}"`
executeFreeFunctionAction.label = intl.formatMessage({id: 'editor.executeFreeFunction2'}, {name: freeFunctionNode.name})
freeFunctionAction = editor.addAction(executeFreeFunctionAction)
}
const functionImpl = nodesAtPosition.find((node) => node.kind === 'function')
if (functionImpl) {
currentFunction.current = functionImpl.name
executeGptGenerateDocumentationAction.label = `Generate documentation for the function "${functionImpl.name}"`
executeGptGenerateDocumentationAction.label = intl.formatMessage({id: 'editor.generateDocumentation2'}, {name: functionImpl.name})
gptGenerateDocumentationAction = editor.addAction(executeGptGenerateDocumentationAction)
executegptExplainFunctionAction.label = `Explain the function "${functionImpl.name}"`
executegptExplainFunctionAction.label = intl.formatMessage({id: 'editor.explainFunction2'}, {name: functionImpl.name})
gptExplainFunctionAction = editor.addAction(executegptExplainFunctionAction)
}
freeFunctionCondition.set(!!freeFunctionNode)
@ -889,7 +905,12 @@ export const EditorUI = (props: EditorUIProps) => {
{editorModelsState[props.currentFile]?.readOnly && (
<span className="pl-4 h6 mb-0 w-100 alert-info position-absolute bottom-0 end-0">
<i className="fas fa-lock-alt p-2"></i>
The file is opened in <b>read-only</b> mode.
<FormattedMessage
id="editor.text"
values={{
b: (chunks) => <b>{chunks}</b>,
}}
/>
</span>
)}
</div>

@ -1,10 +1,12 @@
import React, {useEffect, useState} from 'react' // eslint-disable-line
import {FormattedMessage, useIntl} from 'react-intl'
import {ModalDialog} from '@remix-ui/modal-dialog' // eslint-disable-line
import {RemixUiPublishToStorageProps} from './types' // eslint-disable-line
import {publishToIPFS} from './publishToIPFS'
import {publishToSwarm} from './publishOnSwarm'
export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const intl = useIntl()
const {api, storage, contract, resetStorage} = props
const [modalShown, setModalShown] = useState(false)
const [state, setState] = useState({
@ -23,41 +25,47 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const storageService = async () => {
if (contract.metadata === undefined || contract.metadata.length === 0) {
modal(
'Publish To Storage',
"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."
intl.formatMessage({id: 'publishToStorage.title1'}),
intl.formatMessage({id: 'publishToStorage.message1'})
)
} else {
if (storage === 'swarm') {
try {
const result = await publishToSwarm(contract, api)
modal(`Published ${contract.name}'s Metadata and Sources`, publishMessage(result.uploaded))
modal(intl.formatMessage({id: 'publishToStorage.title2'}, {name: contract.name}), publishMessage(result.uploaded))
} catch (err) {
modal('Swarm Publish Failed', publishMessageFailed(storage, err.message))
modal(intl.formatMessage({id: 'publishToStorage.title3'}), publishMessageFailed(storage, err.message))
}
} else {
if (!api.config.get('settings/ipfs-url') && !modalShown) {
modal(
'IPFS Settings',
intl.formatMessage({id: 'publishToStorage.title4'}),
<div>
You have not set your own custom IPFS settings.<br></br>
<FormattedMessage id="publishToStorage.title4.message1" /><br></br>
<br></br>
We wont be providing a public endpoint anymore for publishing your contracts to IPFS.<br></br>Instead of that, 4 options are now available:<br></br>
<FormattedMessage id="publishToStorage.title4.message2" /><br></br><FormattedMessage id="publishToStorage.title4.message3" /><br></br>
<br></br>
<ul className="pl-3">
<li key="ipfs-default">DEFAULT OPTION: Use the public INFURA node. This will not guarantee your data will persist.</li>
<li key="ipfs-default"><FormattedMessage id="publishToStorage.title4.message4" /></li>
<li key="infura-options">
Use your own INFURA IPFS node. This requires a subscription.{' '}
<a href="https://infura.io/product/ipfs" target={'_blank'}>
Learn more
</a>
<FormattedMessage
id="publishToStorage.title4.message5"
values={{
a: (chunks) => (
<a href="https://infura.io/product/ipfs" target={'_blank'}>
{chunks}
</a>
)
}}
/>
</li>
<li key="ipfs-external">Use any external IPFS which doesnt require any authentification.</li>
<li key="ipfs-local">Use your own local ipfs node (which usually runs under http://localhost:5001)</li>
<li key="ipfs-external"><FormattedMessage id="publishToStorage.title4.message6" /></li>
<li key="ipfs-local"><FormattedMessage id="publishToStorage.title4.message7" /></li>
</ul>
You can update your IPFS settings in the SETTINGS tab.
<FormattedMessage id="publishToStorage.title4.message8" />
<br></br>
Now the default option will be used.
<FormattedMessage id="publishToStorage.title4.message9" />
</div>,
async () => await ipfs(contract, api)
)
@ -76,9 +84,9 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const ipfs = async (contract, api) => {
try {
const result = await publishToIPFS(contract, api)
modal(`Published ${contract.name}'s Metadata and Sources`, publishMessage(result.uploaded))
modal(intl.formatMessage({id: 'publishToStorage.title2'}, {name: contract.name}), publishMessage(result.uploaded))
} catch (err) {
modal('IPFS Publish Failed', publishMessageFailed(storage, err.message))
modal(intl.formatMessage({id: 'publishToStorage.title5'}), publishMessageFailed(storage, err.message))
}
setModalShown(true)
}
@ -86,7 +94,7 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const publishMessage = (uploaded) => (
<span>
{' '}
Metadata and sources of "{contract.name.toLowerCase()}" were published successfully. <br />
<FormattedMessage id="publishToStorage.title2.message" values={{name: contract.name.toLowerCase()}} /> <br />
<pre>
<div>
{uploaded.map((value, index) => (
@ -101,7 +109,7 @@ export const PublishToStorage = (props: RemixUiPublishToStorageProps) => {
const publishMessageFailed = (storage, err) => (
<span>
Failed to publish metadata file and sources to {storage}, please check the {storage} gateways is available. <br />
<FormattedMessage id="publishToStorage.title5.message" values={{storage}} /> <br />
{err}
</span>
)

@ -1,4 +1,5 @@
import React, {useEffect, useState} from 'react' //eslint-disable-line
import {useIntl} from 'react-intl'
import {CopyToClipboard} from '@remix-ui/clipboard'
import {helper} from '@remix-project/remix-solidity'
import './renderer.css'
@ -11,6 +12,7 @@ interface RendererProps {
}
export const Renderer = ({message, opt = {}, plugin}: RendererProps) => {
const intl = useIntl()
const [messageText, setMessageText] = useState(null)
const [editorOptions, setEditorOptions] = useState({
useSpan: false,
@ -72,11 +74,7 @@ export const Renderer = ({message, opt = {}, plugin}: RendererProps) => {
const askGtp = async () => {
try {
const content = await plugin.call('fileManager', 'readFile', editorOptions.errFile)
const message = `
solidity code: ${content}
error message: ${messageText}
explain why the error occurred and how to fix it.
`
const message = intl.formatMessage({id: 'solidity.openaigptMessage'}, {content, messageText})
await plugin.call('openaigpt', 'message', message)
_paq.push(['trackEvent', 'ai', 'openai', 'explainSolidityError'])
} catch (err) {

@ -1,5 +1,6 @@
import {CustomTooltip} from '@remix-ui/helper'
import React from 'react'
import { FormattedMessage } from 'react-intl'
import BasicLogo from './BasicLogo'
interface HomeProps {
verticalIconPlugin: any
@ -7,7 +8,7 @@ interface HomeProps {
function Home({verticalIconPlugin}: HomeProps) {
return (
<CustomTooltip placement="right" tooltipText={'Home'}>
<CustomTooltip placement="right" tooltipText={<FormattedMessage id='home.home' />}>
<div
className="mt-2 my-1 remixui_homeIcon"
onClick={async () => await verticalIconPlugin.activateHome()}

Loading…
Cancel
Save