diff --git a/apps/doc-viewer/src/app/App.tsx b/apps/doc-viewer/src/app/App.tsx index 5f5092eb24..6052af9393 100644 --- a/apps/doc-viewer/src/app/App.tsx +++ b/apps/doc-viewer/src/app/App.tsx @@ -12,9 +12,14 @@ export default function App() { setContents(fileContents) }) }, []) + const edit = () => { + if (!client.mdFile) return + client.call('fileManager', 'open' as any, client.mdFile) + } return ( <>
+
diff --git a/apps/doc-viewer/src/app/docviewer.ts b/apps/doc-viewer/src/app/docviewer.ts index d6fed6e490..30776489b0 100644 --- a/apps/doc-viewer/src/app/docviewer.ts +++ b/apps/doc-viewer/src/app/docviewer.ts @@ -5,6 +5,7 @@ import EventEmitter from 'events' export class DocViewer extends PluginClient { mdFile: string eventEmitter: EventEmitter + refreshId: any constructor() { super() this.eventEmitter = new EventEmitter() @@ -14,9 +15,17 @@ export class DocViewer extends PluginClient { this.onload() } - async viewDocs(docs: string[]) { - this.mdFile = docs[0] + private async refresh() { + if (!this.mdFile) return clearInterval(this.refreshId) const contents = await this.call('fileManager', 'readFile', this.mdFile) this.eventEmitter.emit('contentsReady', contents) } + + async viewDocs(docs: string[]) { + this.mdFile = docs[0] + this.refresh() + this.refreshId = setInterval(async () => { + this.refresh() + }, 500) + } } \ No newline at end of file diff --git a/apps/etherscan/src/app/AppContext.tsx b/apps/etherscan/src/app/AppContext.tsx index f121406a67..69d967534d 100644 --- a/apps/etherscan/src/app/AppContext.tsx +++ b/apps/etherscan/src/app/AppContext.tsx @@ -20,5 +20,6 @@ export const AppContext = React.createContext({ themeType: 'dark' as ThemeType, setThemeType: (themeType: ThemeType) => { console.log('Calling Set Theme Type') - } + }, + networkName: '' }) diff --git a/apps/etherscan/src/app/RemixPlugin.tsx b/apps/etherscan/src/app/EtherscanPluginClient.ts similarity index 80% rename from apps/etherscan/src/app/RemixPlugin.tsx rename to apps/etherscan/src/app/EtherscanPluginClient.ts index 59967c5e41..ec545f806e 100644 --- a/apps/etherscan/src/app/RemixPlugin.tsx +++ b/apps/etherscan/src/app/EtherscanPluginClient.ts @@ -1,10 +1,21 @@ import {PluginClient} from '@remixproject/plugin' +import { createClient } from '@remixproject/plugin-webview' import {verify, EtherScanReturn} from './utils/verify' import {getReceiptStatus, getEtherScanApi, getNetworkName, getProxyContractReceiptStatus} from './utils' +import EventManager from 'events' -export class RemixClient extends PluginClient { - loaded() { - return this.onload() +export class EtherscanPluginClient extends PluginClient { + public internalEvents: EventManager + + constructor() { + super() + createClient(this) + this.internalEvents = new EventManager() + this.onload() + } + + onActivation(): void { + this.internalEvents.emit('etherscan_activated') } async verify( diff --git a/apps/etherscan/src/app/app.tsx b/apps/etherscan/src/app/app.tsx index 972413f7f2..39e89e00d7 100644 --- a/apps/etherscan/src/app/app.tsx +++ b/apps/etherscan/src/app/app.tsx @@ -2,8 +2,7 @@ import React, {useState, useEffect, useRef} from 'react' import {CompilationFileSources, CompilationResult} from '@remixproject/plugin-api' -import {RemixClient} from './RemixPlugin' -import {createClient} from '@remixproject/plugin-webview' +import { EtherscanPluginClient } from './EtherscanPluginClient' import {AppContext} from './AppContext' import {DisplayRoutes} from './routes' @@ -21,32 +20,29 @@ export const getNewContractNames = (compilationResult: CompilationResult) => { for (const file of Object.keys(compiledContracts)) { const newContractNames = Object.keys(compiledContracts[file]) + result = [...result, ...newContractNames] } return result } +const plugin = new EtherscanPluginClient() + const App = () => { const [apiKey, setAPIKey] = useLocalStorage('apiKey', '') - const [clientInstance, setClientInstance] = useState(undefined as any) - const [receipts, setReceipts] = useLocalStorage('receipts', []) - const [contracts, setContracts] = useState([] as string[]) - const [themeType, setThemeType] = useState('dark' as ThemeType) + const [receipts, setReceipts] = useLocalStorage('receipts', []) + const [contracts, setContracts] = useState([]) + const [themeType, setThemeType] = useState('dark') + const [networkName, setNetworkName] = useState('Loading...') const timer = useRef(null) - - const clientInstanceRef = useRef(clientInstance) - clientInstanceRef.current = clientInstance const contractsRef = useRef(contracts) + contractsRef.current = contracts useEffect(() => { - const client = new RemixClient() - createClient(client) - const loadClient = async () => { - await client.onload() - setClientInstance(client) - client.on('solidity', 'compilationFinished', (fileName: string, source: CompilationFileSources, languageVersion: string, data: CompilationResult) => { + plugin.internalEvents.on('etherscan_activated', () => { + plugin.on('solidity', 'compilationFinished', (fileName: string, source: CompilationFileSources, languageVersion: string, data: CompilationResult) => { const newContractsNames = getNewContractNames(data) const newContractsToSave: string[] = [...contractsRef.current, ...newContractsNames] @@ -55,21 +51,16 @@ const App = () => { setContracts(uniqueContracts) }) - - //const currentTheme = await client.call("theme", "currentTheme") - //setThemeType(currentTheme.quality) - //client.on("theme", "themeChanged", (theme) => { - // setThemeType(theme.quality) - //}) - } - - loadClient() + plugin.on('blockchain' as any, 'networkStatus', (result) => { + setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`) + }) + // @ts-ignore + plugin.call('blockchain', 'getCurrentNetworkStatus').then((result: any) => setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`)) + }) }, []) useEffect(() => { - let receiptsNotVerified: Receipt[] = receipts.filter((item: Receipt) => { - return item.status === 'Pending in queue' || item.status === 'Max rate limit reached' - }) + let receiptsNotVerified: Receipt[] = receipts.filter((item: Receipt) => item.status === 'Pending in queue' || item.status === 'Max rate limit reached') if (receiptsNotVerified.length > 0) { if (timer.current) { @@ -77,15 +68,12 @@ const App = () => { timer.current = null } timer.current = setInterval(async () => { - const {network, networkId} = await getNetworkName(clientInstanceRef.current) - if (!clientInstanceRef.current) { - return - } + const {network, networkId} = await getNetworkName(plugin) - if (network === 'vm') { - return - } + if (!plugin) return + if (network === 'vm') return let newReceipts = receipts + for (const item of receiptsNotVerified) { await new Promise((r) => setTimeout(r, 500)) // avoid api rate limit exceed. let status @@ -110,9 +98,7 @@ const App = () => { }) } } - receiptsNotVerified = newReceipts.filter((item: Receipt) => { - return item.status === 'Pending in queue' || item.status === 'Max rate limit reached' - }) + receiptsNotVerified = newReceipts.filter((item: Receipt) => item.status === 'Pending in queue' || item.status === 'Max rate limit reached') if (timer.current && receiptsNotVerified.length === 0) { clearInterval(timer.current) timer.current = null @@ -127,16 +113,17 @@ const App = () => { value={{ apiKey, setAPIKey, - clientInstance, + clientInstance: plugin, receipts, setReceipts, contracts, setContracts, themeType, - setThemeType + setThemeType, + networkName }} > - + { plugin && } ) } diff --git a/apps/etherscan/src/app/views/HomeView.tsx b/apps/etherscan/src/app/views/HomeView.tsx index e9db144bd1..c08a021f99 100644 --- a/apps/etherscan/src/app/views/HomeView.tsx +++ b/apps/etherscan/src/app/views/HomeView.tsx @@ -10,7 +10,7 @@ import {VerifyView} from './VerifyView' export const HomeView: React.FC = () => { return ( - {({apiKey, clientInstance, setReceipts, receipts, contracts}) => { + {({apiKey, clientInstance, setReceipts, receipts, contracts, networkName}) => { return !apiKey ? ( { const newReceipts = [...receipts, receipt] setReceipts(newReceipts) }} + networkName={networkName} /> ) }} diff --git a/apps/etherscan/src/app/views/VerifyView.tsx b/apps/etherscan/src/app/views/VerifyView.tsx index adfd0e6cfb..ab6e7c641c 100644 --- a/apps/etherscan/src/app/views/VerifyView.tsx +++ b/apps/etherscan/src/app/views/VerifyView.tsx @@ -14,7 +14,8 @@ interface Props { client: PluginClient apiKey: string onVerifiedContract: (receipt: Receipt) => void - contracts: string[] + contracts: string[], + networkName: string } interface FormValues { @@ -23,27 +24,14 @@ interface FormValues { expectedImplAddress?: string } -export const VerifyView: React.FC = ({apiKey, client, contracts, onVerifiedContract}) => { +export const VerifyView: React.FC = ({apiKey, client, contracts, onVerifiedContract, networkName}) => { const [results, setResults] = useState('') - const [networkName, setNetworkName] = useState('Loading...') const [selectedContract, setSelectedContract] = useState('') const [showConstructorArgs, setShowConstructorArgs] = useState(false) const [isProxyContract, setIsProxyContract] = useState(false) const [constructorInputs, setConstructorInputs] = useState([]) const verificationResult = useRef({}) - useEffect(() => { - if (client && client.on) { - client.on('blockchain' as any, 'networkStatus', (result) => { - setNetworkName(`${result.network.name} ${result.network.id !== '-' ? `(Chain id: ${result.network.id})` : '(Not supported)'}`) - }) - } - return () => { - // To fix memory leak - if (client && client.off) client.off('blockchain' as any, 'networkStatus') - } - }, [client]) - useEffect(() => { if (contracts.includes(selectedContract)) updateConsFields(selectedContract) }, [contracts]) diff --git a/apps/remix-ide-e2e/src/tests/walkthrough.test.ts b/apps/remix-ide-e2e/src/tests/walkthrough.test.ts new file mode 100644 index 0000000000..3ef2982a10 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/walkthrough.test.ts @@ -0,0 +1,20 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + + 'Should run walkthrough for recorder': function (browser: NightwatchBrowser) { + browser + .waitForElementPresent('*[data-id="remixIdeSidePanel"]') + .clickLaunchIcon('udapp') + .waitForElementPresent('*[data-id="recorderStartWalkthrough"]') + .click('*[data-id="recorderStartWalkthrough"]') + .waitForElementPresent('*[id="remixRecorderWalkthrowTitle"]') + .waitForElementPresent('*[data-id="remixRecorderExpanded"]') + } +} \ No newline at end of file diff --git a/apps/remix-ide-e2e/src/tests/workspace_git.test.ts b/apps/remix-ide-e2e/src/tests/workspace_git.test.ts index 0a1e1ec63b..138b040a3e 100644 --- a/apps/remix-ide-e2e/src/tests/workspace_git.test.ts +++ b/apps/remix-ide-e2e/src/tests/workspace_git.test.ts @@ -339,6 +339,36 @@ module.exports = { .waitForElementVisible('[data-id="treeViewDivtreeViewItemrecursive/plugins/build"]') }, + // GIT SUBMODULES E2E ENDS + + // GIT WORKSPACE E2E STARTS + + 'Should create a git workspace (uniswapV4Periphery) #group4': function (browser: NightwatchBrowser) { + browser + .click('*[data-id="workspacesMenuDropdown"]') + .click('*[data-id="workspacecreate"]') + .waitForElementVisible('*[data-id="modalDialogCustomPromptTextCreate"]') + .waitForElementVisible('[data-id="fileSystemModalDialogModalFooter-react"] > button') + .click('select[id="wstemplate"]') + .click('select[id="wstemplate"] option[value=uniswapV4Periphery]') + .waitForElementPresent('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') + .execute(function () { (document.querySelector('[data-id="fileSystemModalDialogModalFooter-react"] .modal-ok') as HTMLElement).click() }) + .pause(100) + .waitForElementVisible('*[data-id="treeViewLitreeViewItemcontracts"]') + .openFile('contracts') + .openFile('contracts/hooks') + .openFile('contracts/hooks/examples') + .openFile('contracts/hooks/examples/FullRange.sol') + .pause(1000) + .getEditorValue((content) => { + browser.assert.ok(content.indexOf(`contract FullRange is BaseHook`) !== -1, + 'Incorrect content') + }) + }, + + // GIT WORKSPACE E2E ENDS + + tearDown: sauce, } diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index 7c4b13e928..838a2645d9 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -166,7 +166,7 @@ class AppComponent { this.showMatamo = matomoDomains[window.location.hostname] && !Registry.getInstance().get('config').api.exists('settings/matomo-analytics') this.platform = isElectron() ? 'desktop' : 'web' - this.showEnter = matomoDomains[window.location.hostname] && !localStorage.getItem('hadUsageTypeAsked') + this.showEnter = this.showMatamo && !localStorage.getItem('hadUsageTypeAsked') this.walkthroughService = new WalkthroughService(appManager, !this.showMatamo || !this.showEnter) diff --git a/apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts b/apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts index 9ed52afe98..f58b76888d 100644 --- a/apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts +++ b/apps/remix-ide/src/app/plugins/copilot/suggestion-service/copilot-suggestion.ts @@ -50,8 +50,8 @@ export class CopilotSuggestion extends Plugin { const options: SuggestOptions = { do_sample: false, top_k: 0, - temperature, - max_new_tokens + temperature: temperature || 0, + max_new_tokens: max_new_tokens || 0 } return this.service.suggest(this.context ? this.context + '\n\n' + content : content, options) } @@ -76,4 +76,4 @@ export class CopilotSuggestion extends Plugin { async uninstall() { this.service.terminate() } -} \ No newline at end of file +} diff --git a/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts b/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts index ecd8278ca3..1fad0fd67e 100644 --- a/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts +++ b/apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts @@ -142,6 +142,14 @@ export default class CodeParserCompiler { this.compiler.set('runs', state.runs) this.compiler.set('useFileConfiguration', true) this.compiler.set('compilerRetriggerMode', CompilerRetriggerMode.retrigger) + + if (await this.plugin.call('fileManager', 'exists','remappings.txt')) { + const remappings = await this.plugin.call('fileManager', 'readFile','remappings.txt') + this.compiler.set('remappings', remappings.split('\n').filter(Boolean)) + } else { + this.compiler.set('remappings', []) + } + const configFileContent = { "language": "Solidity", "settings": { diff --git a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json index a645dfc06c..d2a2973e5f 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/filePanel.json +++ b/apps/remix-ide/src/app/tabs/locales/en/filePanel.json @@ -106,6 +106,8 @@ "filePanel.semaphore": "Semaphore", "filePanel.hashchecker": "Hash Checker", "filePanel.rln": "Rate-Limiting Nullifier", + "filePanel.breakthroughLabsUniswapv4Hooks": "Breakthrough-Labs Uniswapv4Hooks", + "filePanel.uniswapV4Periphery": "Uniswap v4 Periphery", "filePanel.transparent": "Transparent", "filePanel.initGitRepoTitle": "Check option to initialize workspace as a new git repository", "filePanel.switchToBranchTitle1": "Checkout new branch from remote branch", diff --git a/apps/remix-ide/src/app/tabs/locales/en/udapp.json b/apps/remix-ide/src/app/tabs/locales/en/udapp.json index d2e2af9193..5d045b6acf 100644 --- a/apps/remix-ide/src/app/tabs/locales/en/udapp.json +++ b/apps/remix-ide/src/app/tabs/locales/en/udapp.json @@ -76,6 +76,7 @@ "udapp.transactionSaveTooltip1": "No transactions to save", "udapp.transactionSaveTooltip2": "Save {count} transaction as scenario file", "udapp.transactionSaveTooltip3": "Save {count} transactions as scenario file", + "udapp.transactionsWalkthroughTooltip": "Start walkthrough tour for recorder.", "udapp.infoRecorderTooltip": "Save transactions (deployed contracts and function executions) and replay them in another environment e.g Transactions created in Remix VM can be replayed in the Injected Provider.", "udapp.livemodeRecorderTooltip": "If contracts are updated after recording transactions, checking this box will run recorded transactions with the latest copy of the compiled contracts", "udapp.livemodeRecorderLabel": "Run transactions using the latest compilation result", diff --git a/apps/remix-ide/src/app/tabs/locales/fr/home.json b/apps/remix-ide/src/app/tabs/locales/fr/home.json index c6a4fc865c..802d22bef4 100644 --- a/apps/remix-ide/src/app/tabs/locales/fr/home.json +++ b/apps/remix-ide/src/app/tabs/locales/fr/home.json @@ -1,66 +1,61 @@ { - "home.scamAlert": "Alerte arnaque", - "home.scamAlertText": "Le seul URL utilisé par Remix est remix.ethereum.org", - "home.scamAlertText2": "Méfiez-vous des vidéos en ligne qui font la promotion de \"front-runner bots\"", - "home.scamAlertText3": "Conseils de sécurité supplémentaires", - "home.learnMore": "En savoir plus", - "home.here": "ici", - "home.featured": "Recommandé", - "home.jumpIntoWeb3": "JUMP INTO WEB3", - "home.jumpIntoWeb3More": "More", - "home.jumpIntoWeb3Text": "Remix IDE is part of the Remix Project, a rich toolset that can be used for the entire journey of contract development by users of any knowledge level. Learn more on the Remix Project website.", - "home.remixYouTube": "REGARDER POUR APPRENDRE", - "home.remixYouTubeText1": "Conseils vidéo de l'équipe de Remix", - "home.remixYouTubeMore": "Regarder", - "home.remixYouTubeText2": "Remix a une bibliothèque grandissante de vidéos contenant beaucoup de conseils pour utiliser l'outil. Vérifiez-les et abonnez-vous pour obtenir nos dernieres vidéos.", - "home.betaTesting": "Tests BETA", - "home.betaTestingText1": "Notre communauté nous soutient.", - "home.betaTestingText2": "Aidez-nous à tester les versions bêta dès maintenant et à gérer les nouvelles fonctionnalités !", - "home.betaTestingMore": "S'inscrire", - "home.featuredPlugins": "Plugins recommandés", - "home.solidityPluginDesc": "Compiler, tester et analyser les smart contrats.", - "home.cookbookDesc": "Trouvez des smarts contrats, des bibliothèques solidity et découvrez des protocoles.", - "home.codeAnalyizerPluginDesc": "Analysez votre code en utilisant Remix, Solhint et Slither.", - "home.starkNetPluginDesc": "Compiler et déployer des contrats avec Cairo, le language natif pour StarkNet.", - "home.solhintPluginDesc": "Solhint est un projet open source de linter pour code solidity.", - "home.sourcifyPluginDesc": "Service de vérification de contract solidity et des métadonnées.", - "home.unitTestPluginDesc": "Écrire et exécuter des tests unitaires pour vos contrats en Solidity.", - "home.dgitPluginDesc": "Ajoutez le contrôle source à vos projets.", - "home.oneClickDappDesc": "Générer rapidement des interfaces smart contract", - "home.getStarted": "Démarrer", - "home.projectTemplates": "Template de projet", - "home.blankTemplateDesc": "Créer un espace de travail vide.", - "home.remixDefaultTemplateDesc": "Créer un espace de travail avec des exemples de fichiers.", - "home.ozerc20TemplateDesc": "Créer un token ERC20 en important la bibliothèque OpenZeppelin.", - "home.ozerc721TemplateDesc": "Créez un token NFT en important la bibliothèque OpenZeppelin.", - "home.ozerc1155TemplateDesc": "Créez un token ERC1155 en important la bibliothèque OpenZeppelin.", - "home.gnosisSafeMultisigTemplateDesc": "Créer des allet multi-signatures en utilisant ce modèle.", - "home.zeroxErc20TemplateDesc": "Créez un token ERC20 en important le contrat 0xProject.", - "home.learn": "Apprendre", - "home.learnEth1": "Bases de remix", - "home.learnEth1Desc": "Une introduction à l'interface de Remix et aux opérations de base.", - "home.learnEth2": "Introduction à Solidity", - "home.learnEth2Desc": "Apprenez de façon interactive les concepts de base de Solidity.", - "home.remixAdvanced": "Déploiement avec les librairies", - "home.remixAdvancedDesc": "Apprendre à déployer avec les librairies dans Remix", - "home.remixYoutubePlaylist": "Playlist Youtube de Remix", - "home.remixTwitterProfile": "Profile Twitter de Remix", - "home.remixLinkedinProfile": "Profil Linkedin de Remix", - "home.remixMediumPosts": "Articles Medium de Remix", - "home.joinUsOnDiscord": "Rejoignez-nous sur Discord", - "home.nativeIDE": "L’IDE natif pour le développement Web3.", - "home.website": "Site Web", + "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\"", + "home.scamAlertText3": "Additional safety tips", + "home.learnMore": "Learn more", + "home.here": "here", + "home.featured": "Featured", + "home.jumpIntoWeb3": "WE NEED YOUR HELP", + "home.jumpIntoWeb3More": "Go to survey", + "home.jumpIntoWeb3Text": "Remixers... Have a spare moment? Please help us improve your Remix experience with this one-minute survey.", + "home.remixYouTube": "WATCH TO LEARN", + "home.remixYouTubeText1": "Video Tips from the Remix Team", + "home.remixYouTubeMore": "Watch", + "home.remixYouTubeText2": "Remix has a growing library of videos containing lots of tips for using the tool. Check them out and subscribe to get our latest uploads.", + "home.betaTesting": "BETA TESTING", + "home.betaTestingText1": "Our community supports us.", + "home.betaTestingText2": "Help us beta test releases now and get a handle on new features!", + "home.betaTestingMore": "Sign up", + "home.featuredPlugins": "Featured Plugins", + "home.solidityPluginDesc": "Compile, test, and analyze smart contracts.", + "home.starkNetPluginDesc": "Compile and deploy contracts with Cairo, a native language for StarkNet.", + "home.solhintPluginDesc": "Solhint is an open source project for linting Solidity code.", + "home.sourcifyPluginDesc": "Solidity contract and metadata verification service.", + "home.unitTestPluginDesc": "Write and run unit tests for your contracts in Solidity.", + "home.dgitPluginDesc": "Add source control to your projects.", + "home.oneClickDappDesc": "Quickly generate smart contract interfaces", + "home.getStarted": "Get Started", + "home.projectTemplates": "Project Templates", + "home.blankTemplateDesc": "Create an empty workspace.", + "home.remixDefaultTemplateDesc": "Create a workspace with sample files.", + "home.ozerc20TemplateDesc": "Create an ERC20 token by importing OpenZeppelin library.", + "home.ozerc721TemplateDesc": "Create an NFT token by importing OpenZeppelin library.", + "home.ozerc1155TemplateDesc": "Create an ERC1155 token by importing OpenZeppelin library.", + "home.gnosisSafeMultisigTemplateDesc": "Create Multi-Signature wallets using this template.", + "home.zeroxErc20TemplateDesc": "Create an ERC20 token by importing 0xProject contract.", + "home.learn": "Learn", + "home.learnEth1": "Remix Basics", + "home.learnEth1Desc": "An introduction to Remix's interface and basic operations.", + "home.learnEth2": "Intro to Solidity", + "home.learnEth2Desc": "Interactively learn Solidity beginner concepts.", + "home.remixAdvanced": "Deploying with Libraries", + "home.remixAdvancedDesc": "Learn to deploy with libraries in Remix", + "home.remixYoutubePlaylist": "Remix Youtube Playlist", + "home.remixTwitterProfile": "Remix Twitter Profile", + "home.remixLinkedinProfile": "Remix Linkedin Profile", + "home.remixMediumPosts": "Remix Medium Posts", + "home.remixGitterChannel": "Join us on Discord", + "home.nativeIDE": "The Native IDE for Web3 Development.", + "home.website": "Website", "home.documentation": "Documentation", - "home.remixPlugin": "Plugin Remix", - "home.remixDesktop": "Version Desktop de Remix", - "home.searchDocumentation": "Rechercher dans la documentation", - "home.files": "Fichiers", - "home.newFile": "Nouveau Fichier", - "home.openFile": "Ouvrir Fichier", - "home.accessFileSystem": "Accéder au système de fichiers", - "home.loadFrom": "Charger de", - "home.resources": "Ressources", - "home.connectToLocalhost": "Se connecter à Localhost", - "home.seeAllTutorials": "Voir tous les tutoriels", - "home.maintainedByRemix": "Maintenu par Remix" + "home.remixPlugin": "Remix Plugin", + "home.remixDesktop": "Remix Desktop", + "home.searchDocumentation": "Search Documentation", + "home.files": "Files", + "home.newFile": "New File", + "home.openFile": "Open File", + "home.connectToLocalhost": "Access File System", + "home.loadFrom": "Load from", + "home.resources": "Resources" } diff --git a/apps/remix-ide/src/app/tabs/settings-tab.tsx b/apps/remix-ide/src/app/tabs/settings-tab.tsx index b3b94e7a89..e9a77080b7 100644 --- a/apps/remix-ide/src/app/tabs/settings-tab.tsx +++ b/apps/remix-ide/src/app/tabs/settings-tab.tsx @@ -5,6 +5,12 @@ import * as packageJson from '../../../../../package.json' import {RemixUiSettings} from '@remix-ui/settings' //eslint-disable-line import {Registry} from '@remix-project/remix-lib' import {PluginViewWrapper} from '@remix-ui/helper' +declare global { + interface Window { + _paq: any + } +} +const _paq = (window._paq = window._paq || []) const profile = { name: 'settings', @@ -85,6 +91,15 @@ module.exports = class SettingsTab extends ViewPlugin { updateMatomoAnalyticsChoice(isChecked) { this.config.set('settings/matomo-analytics', isChecked) this.useMatomoAnalytics = isChecked + if (!isChecked) { + _paq.push(['optUserOut']) + // revoke tracking consent + _paq.push(['forgetConsentGiven']); + } else { + _paq.push(['forgetUserOptOut']) + // user has given consent to process their data + _paq.push(['setConsentGiven']); + } this.dispatch({ ...this }) diff --git a/apps/remix-ide/src/assets/js/loader.js b/apps/remix-ide/src/assets/js/loader.js index 8db5546da5..5938aeaf9c 100644 --- a/apps/remix-ide/src/assets/js/loader.js +++ b/apps/remix-ide/src/assets/js/loader.js @@ -17,14 +17,20 @@ if (domains[window.location.hostname]) { /* tracker methods like "setCustomDimension" should be called before "trackPageView" */ _paq.push(["setExcludedQueryParams", ["code","gist"]]); _paq.push(["setExcludedReferrers", ["etherscan.io"]]); - _paq.push(['disableCookies']); _paq.push(['enableJSErrorTracking']); + // require user tracking consent before processing data + _paq.push(['requireConsent']); _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); _paq.push(['enableHeartBeatTimer']); if (!window.localStorage.getItem('config-v0.8:.remix.config') || (window.localStorage.getItem('config-v0.8:.remix.config') && !window.localStorage.getItem('config-v0.8:.remix.config').includes('settings/matomo-analytics'))) { _paq.push(['optUserOut']) + + } else { + _paq.push(['forgetUserOptOut']) + // user has given consent to process their data + _paq.push(['setConsentGiven']) } (function () { var u = "https://ethereumfoundation.matomo.cloud/"; @@ -38,17 +44,17 @@ if (domains[window.location.hostname]) { function isElectron() { // Renderer process if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') { - return true + return true } // Main process if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) { - return true + return true } // Detect the user agent when the `nodeIntegration` option is set to false if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) { - return true + return true } return false diff --git a/apps/remix-ide/src/blockchain/blockchain.tsx b/apps/remix-ide/src/blockchain/blockchain.tsx index 779ea27f0a..d3f667de1d 100644 --- a/apps/remix-ide/src/blockchain/blockchain.tsx +++ b/apps/remix-ide/src/blockchain/blockchain.tsx @@ -23,7 +23,7 @@ const profile = { name: 'blockchain', displayName: 'Blockchain', description: 'Blockchain - Logic', - methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'web3', 'getProvider'], + methods: ['getCode', 'getTransactionReceipt', 'addProvider', 'removeProvider', 'getCurrentFork', 'getAccounts', 'web3VM', 'web3', 'getProvider', 'getCurrentNetworkStatus'], version: packageJson.version } diff --git a/apps/remix-ide/src/index.css b/apps/remix-ide/src/index.css index 8396f022e7..c8e34313c1 100644 --- a/apps/remix-ide/src/index.css +++ b/apps/remix-ide/src/index.css @@ -19,4 +19,4 @@ font-size: 0.8rem; font-weight: normal; max-width: 300px; -} \ No newline at end of file +} diff --git a/apps/remix-ide/src/walkthroughService.js b/apps/remix-ide/src/walkthroughService.js index 67bb850a8c..f54d0e28ce 100644 --- a/apps/remix-ide/src/walkthroughService.js +++ b/apps/remix-ide/src/walkthroughService.js @@ -7,14 +7,14 @@ const profile = { displayName: 'Walkthrough', description: 'Remix walkthrough for beginner', version: packageJson.version, - methods: ['start'] + methods: ['start', 'startRecorderW'] } export class WalkthroughService extends Plugin { constructor (appManager, showWalkthrough) { super(profile) - let readyToStart = 0; - /*appManager.event.on('activate', (plugin) => { + /*let readyToStart = 0; + appManager.event.on('activate', (plugin) => { if (plugin.name === 'udapp') readyToStart++ if (readyToStart == 2 && showWalkthrough) { this.start() @@ -28,6 +28,59 @@ export class WalkthroughService extends Plugin { })*/ } + startRecorderW () { + introJs().setOptions({ + steps: [{ + title: 'Transactions Recorder', + intro: 'Save transactions (deployed contracts and function executions) and replay them in another environment e.g Transactions created in Remix VM can be replayed in the Injected Provider.Click to launch the Home tab that contains links, tips, and shortcuts..', + element: document.querySelector('#udappRecorderCard'), + tooltipClass: 'bg-light text-dark', + position: 'right', + highlightClass: 'bg-light border border-warning' + }, + { + element: document.querySelector('#udappRecorderUseLatest'), + title: 'Transactions Recorder', + intro: 'If set the recorder will run transactions using the latest compilation result.', + tooltipClass: 'bg-light text-dark', + position: 'right', + highlightClass: 'bg-light border border-warning' + }, + { + element: document.querySelector('#udappRecorderSave'), + title: 'Transactions Recorder', + intro: 'Once there is a Once one or a few transactions have been executed from Remix, click this button to save these transactions as a scenario file.', + tooltipClass: 'bg-light text-dark', + position: 'right', + highlightClass: 'bg-light border border-warning' + }, + { + element: document.querySelector('#udappRecorderRun'), + title: 'Transactions Recorder', + intro: 'Open a scenario file and click this button to run it against the current selected provider.', + tooltipClass: 'bg-light text-dark', + position: 'right', + highlightClass: 'bg-light border border-warning' + } + ] + }).onafterchange((targetElement) => { + const header = document.getElementsByClassName('introjs-tooltip-header')[0] + if (header) { + header.classList.add('d-flex') + header.classList.add('justify-content-between') + header.classList.add('text-nowrap') + header.classList.add('pr-0') + header.id="remixRecorderWalkthrowTitle" + } + const skipbutton = document.getElementsByClassName('introjs-skipbutton')[0] + if (skipbutton) { + skipbutton.classList.add('ml-3') + skipbutton.classList.add('text-decoration-none') + skipbutton.id = 'remixTourSkipbtn' + } + }).start() + } + start () { if (!localStorage.getItem('hadTour_initial')) { introJs().setOptions({ diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/enter.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/enter.tsx index b13cd5adc2..30903ccc3c 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/enter.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/enter.tsx @@ -46,10 +46,10 @@ const EnterDialog = (props: EnterDialogProps) => {
- - - - + + + +
diff --git a/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx b/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx index c4637cb85d..b1fb029b77 100644 --- a/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/components/modals/matomo.tsx @@ -57,11 +57,15 @@ const MatomoDialog = (props) => { const declineModal = async () => { settings.updateMatomoAnalyticsChoice(false) _paq.push(['optUserOut']) + // revoke tracking consent + _paq.push(['forgetConsentGiven']); setVisible(false) } const handleModalOkClick = async () => { _paq.push(['forgetUserOptOut']) + // user has given consent to process their data + _paq.push(['setConsentGiven']); settings.updateMatomoAnalyticsChoice(true) setVisible(false) } diff --git a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx index 974cc015ee..050056bb17 100644 --- a/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx +++ b/libs/remix-ui/app/src/lib/remix-app/remix-app.tsx @@ -25,7 +25,7 @@ interface IRemixAppUi { } const RemixApp = (props: IRemixAppUi) => { const [appReady, setAppReady] = useState(false) - const [showEnterDialog, setShowEnterDialog] = useState(true) + const [showEnterDialog, setShowEnterDialog] = useState(false) const [hideSidePanel, setHideSidePanel] = useState(false) const [maximiseTrigger, setMaximiseTrigger] = useState(0) const [resetTrigger, setResetTrigger] = useState(0) diff --git a/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx index ecb4cd4ac2..2c99939551 100644 --- a/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx +++ b/libs/remix-ui/home-tab/src/lib/components/homeTabGetStarted.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import React, {useEffect, useRef, useContext} from 'react' import {useIntl, FormattedMessage} from 'react-intl' -import {TEMPLATE_NAMES} from '@remix-ui/workspace' +import {TEMPLATE_NAMES,TEMPLATE_METADATA} from '@remix-ui/workspace' import {ThemeContext} from '../themeContext' import Carousel from 'react-multi-carousel' import WorkspaceTemplate from './workspaceTemplate' @@ -60,15 +60,19 @@ function HomeTabGetStarted({plugin}: HomeTabGetStartedProps) { } const createWorkspace = async (templateName) => { + if(platform === appPlatformTypes.desktop){ await plugin.call('remix-templates', 'loadTemplateInNewWindow', templateName) return } - - await plugin.appManager.activatePlugin('filePanel') - const timeStamp = Date.now() let templateDisplayName = TEMPLATE_NAMES[templateName] + const metadata = TEMPLATE_METADATA[templateName] + if (metadata) { + await plugin.call('dGitProvider', 'clone', {url: metadata.url, branch: metadata.branch}, templateDisplayName) + return + } + await plugin.appManager.activatePlugin('filePanel') templateDisplayName = await plugin.call('filePanel', 'getAvailableWorkspaceName', templateDisplayName) await plugin.call('filePanel', 'createWorkspace', templateDisplayName, templateName) await plugin.call('filePanel', 'setWorkspace', templateDisplayName) diff --git a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx index 14714c4a44..5a59befec9 100644 --- a/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx +++ b/libs/remix-ui/run-tab/src/lib/components/contractDropdownUI.tsx @@ -514,7 +514,7 @@ export function ContractDropdownUI(props: ContractDropdownProps) {
- + }> - - - +
- + } ) } diff --git a/libs/remix-ui/run-tab/src/lib/run-tab.tsx b/libs/remix-ui/run-tab/src/lib/run-tab.tsx index f716bbd9a8..9f68cc9944 100644 --- a/libs/remix-ui/run-tab/src/lib/run-tab.tsx +++ b/libs/remix-ui/run-tab/src/lib/run-tab.tsx @@ -301,6 +301,7 @@ export function RunTabUI(props: RunTabProps) { proxy={runTab.proxy} /> JSX.Element, count: number currentFile: string + plugin: RunTab } export interface InstanceContainerProps { diff --git a/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx index f4f943dd1c..65d9f6b0db 100644 --- a/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx +++ b/libs/remix-ui/solidity-unit-testing/src/lib/solidity-unit-testing.tsx @@ -190,9 +190,13 @@ export const SolidityUnitTesting = (props: Record) => { }, []) // eslint-disable-line const updateDirList = (path: string) => { - testTabLogic.dirList(path).then((options: string[]) => { - setPathOptions(options) - }) + try { + testTabLogic.dirList(path).then((options: string[]) => { + setPathOptions(options) + }) + } catch { + console.log("No test directory has been found in the workspace.") + } } const handleTestDirInput = async (e: any) => { diff --git a/libs/remix-ui/terminal/src/lib/terminalWelcome.tsx b/libs/remix-ui/terminal/src/lib/terminalWelcome.tsx index 1c7821a461..24c37b400e 100644 --- a/libs/remix-ui/terminal/src/lib/terminalWelcome.tsx +++ b/libs/remix-ui/terminal/src/lib/terminalWelcome.tsx @@ -54,7 +54,7 @@ const TerminalWelcomeMessage = ({packageJson, storage}) => { ethers.js {' '} -
  • +
  • gpt <your question here> {' '}
  • diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx b/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx index 49ffcf2387..73f11e72fb 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx +++ b/libs/remix-ui/vertical-icons-panel/src/lib/components/Icon.tsx @@ -84,49 +84,57 @@ const Icon = ({iconRecord, verticalIconPlugin, contextMenuAction, theme}: IconPr } }, []) + + const stylePC = iconRecord.active ? 'flex-start' : 'center' return ( <> - -
    { - ;(verticalIconPlugin as any).toggle(name) - }} - {...{plugin: name}} - onContextMenu={(e: any) => { - e.preventDefault() - e.stopPropagation() - handleContextMenu(e) - }} - data-id={`verticalIconsKind${name}`} - id={`verticalIconsKind${name}`} - ref={iconRef} +
    + {iconRecord.active &&
    } + - {name} { + ;(verticalIconPlugin as any).toggle(name) + }} + {...{plugin: name}} + onContextMenu={(e: any) => { + e.preventDefault() + e.stopPropagation() + handleContextMenu(e) + }} + data-id={`verticalIconsKind${name}`} + id={`verticalIconsKind${name}`} + ref={iconRef} + > + {name} + +
    + + {showContext ? ( + - -
    -
    - {showContext ? ( - - ) : null} + ) : null} + ) } diff --git a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css index 393ee84773..66287f32fa 100644 --- a/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css +++ b/libs/remix-ui/vertical-icons-panel/src/lib/remix-ui-vertical-icons-panel.css @@ -3,7 +3,9 @@ width: 42px; height: 42px; cursor: pointer; - } + justify-content: space-between; + align-self: center; +} .remixui_homeIcon:hover { box-shadow: 0px 0px 14px -7px; } @@ -17,8 +19,6 @@ .remixui_icons { display: flex; flex-flow: column nowrap; - justify-content: space-between; - align-items: center; } .remixui_icon:hover { box-shadow: 0px 0px 14px -7px; diff --git a/libs/remix-ui/workspace/src/lib/actions/index.ts b/libs/remix-ui/workspace/src/lib/actions/index.ts index 4ee4af4c0d..a4d25643a9 100644 --- a/libs/remix-ui/workspace/src/lib/actions/index.ts +++ b/libs/remix-ui/workspace/src/lib/actions/index.ts @@ -368,8 +368,13 @@ export const emitContextMenuEvent = async (cmd: customAction) => { } export const handleClickFile = async (path: string, type: 'file' | 'folder' | 'gist') => { - await plugin.fileManager.open(path) - dispatch(focusElement([{ key: path, type }])) + if (type === 'file' && path.endsWith('.md')) { + // just opening the preview + await plugin.call('doc-viewer' as any, 'viewDocs', [path]) + } else { + await plugin.fileManager.open(path) + dispatch(focusElement([{ key: path, type }])) + } } export const handleExpandPath = (paths: string[]) => { diff --git a/libs/remix-ui/workspace/src/lib/actions/workspace.ts b/libs/remix-ui/workspace/src/lib/actions/workspace.ts index 0b8122bbf4..c5e124a9e4 100644 --- a/libs/remix-ui/workspace/src/lib/actions/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/actions/workspace.ts @@ -1,6 +1,7 @@ import React from 'react' import { bufferToHex } from '@ethereumjs/util' import { hash } from '@remix-project/remix-lib' +import { TEMPLATE_METADATA, TEMPLATE_NAMES } from '../utils/constants' import axios, { AxiosResponse } from 'axios' import { addInputFieldSuccess, @@ -198,10 +199,13 @@ export const createWorkspace = async ( } export const createWorkspaceTemplate = async (workspaceName: string, template: WorkspaceTemplate = 'remixDefault') => { + const metadata = TEMPLATE_METADATA[template] if (!workspaceName) throw new Error('workspace name cannot be empty') if (checkSpecialChars(workspaceName) || checkSlash(workspaceName)) throw new Error('special characters are not allowed') if ((await workspaceExists(workspaceName)) && template === 'remixDefault') throw new Error('workspace already exists') - else { + else if (metadata) { + await plugin.call('dGitProvider', 'clone', {url: metadata.url, branch: metadata.branch}, workspaceName) + } else { const workspaceProvider = plugin.fileProviders.workspace await workspaceProvider.createWorkspace(workspaceName) } diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index d46aa060c5..88497ca40f 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -773,8 +773,15 @@ export function Workspace() { {intl.formatMessage({id: 'filePanel.rln'})} + + + + -