Merge branch 'master' into hometabMinor

pull/5370/head
Liana Husikyan 5 months ago committed by GitHub
commit ce7dced805
  1. 2
      .circleci/config.yml
  2. 11
      apps/learneth/src/components/BackButton/index.tsx
  3. 6
      apps/learneth/src/components/RepoImporter/index.tsx
  4. 2
      apps/learneth/src/pages/Home/index.tsx
  5. 6
      apps/learneth/src/pages/StepDetail/index.tsx
  6. 2
      apps/learneth/src/pages/StepList/index.tsx
  7. 38
      apps/learneth/src/redux/models/remixide.ts
  8. 4
      apps/learneth/src/redux/models/workshop.ts
  9. 10
      apps/remix-ide/src/app.js
  10. 5
      apps/remix-ide/src/app/components/status-bar.tsx
  11. 3
      apps/remix-ide/src/app/files/fileManager.ts
  12. 26
      apps/remix-ide/src/app/plugins/matomo.ts
  13. 2
      apps/remix-ide/src/assets/css/themes/remix-black_undtds.css
  14. 2
      apps/remix-ide/src/assets/css/themes/remix-dark_tvx1s2.css
  15. 3
      apps/remix-ide/src/remixAppManager.js
  16. 12
      libs/remix-api/src/lib/plugins/filePanel-api.ts
  17. 2
      libs/remix-api/src/lib/plugins/fileSystem-api.ts
  18. 2
      libs/remix-api/src/lib/plugins/settings-api.ts
  19. 3
      libs/remix-api/src/lib/remix-api.ts
  20. 78
      libs/remix-ui/app/src/lib/remix-app/remix-app.tsx
  21. 10
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  22. 31
      libs/remix-ui/solidity-compiler/src/lib/solScanTable.tsx
  23. 34
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  24. 2
      libs/remix-ui/statusbar/src/css/statusbar.css
  25. 4
      libs/remix-ui/statusbar/src/lib/components/gitStatus.tsx
  26. 2
      libs/remix-ui/statusbar/src/lib/remixui-statusbar-panel.tsx
  27. 2
      libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx

@ -499,7 +499,7 @@ workflows:
- build-desktop: - build-desktop:
filters: filters:
branches: branches:
only: ['master', /.*desktop.*/] only: [/.*desktop.*/]
- build-remixdesktop-mac: - build-remixdesktop-mac:
requires: requires:
- build-desktop - build-desktop

@ -10,6 +10,8 @@ function BackButton({entity}: any) {
const isDetailPage = location.pathname === '/detail' const isDetailPage = location.pathname === '/detail'
const queryParams = new URLSearchParams(location.search) const queryParams = new URLSearchParams(location.search)
const stepId = Number(queryParams.get('stepId')) const stepId = Number(queryParams.get('stepId'))
const nextStep = entity && entity.steps[stepId + 1]
const previousStep = entity && entity.steps[stepId - 1]
return ( return (
<nav className="navbar navbar-light bg-light justify-content-between pt-1 pb-1 pl-1"> <nav className="navbar navbar-light bg-light justify-content-between pt-1 pb-1 pl-1">
@ -29,7 +31,7 @@ function BackButton({entity}: any) {
</li> </li>
{isDetailPage && ( {isDetailPage && (
<li className="nav-item"> <li className="nav-item">
<Link className="btn" to={`/list?id=${entity.id}`} title="Tutorial menu"> <Link className="btn" to={`/list?id=${entity.id}`} title="Tutorial menu" onClick={() => (window as any)._paq.push(['trackEvent', 'learneth', 'back_to_menu_step', entity && entity.name])}>
<i className="fas fa-bars" /> <i className="fas fa-bars" />
</Link> </Link>
</li> </li>
@ -38,13 +40,13 @@ function BackButton({entity}: any) {
{isDetailPage && ( {isDetailPage && (
<form className="form-inline"> <form className="form-inline">
{stepId > 0 && ( {stepId > 0 && (
<Link to={`/detail?id=${entity.id}&stepId=${stepId - 1}`}> <Link to={`/detail?id=${entity.id}&stepId=${stepId - 1}`} onClick={() => (window as any)._paq.push(['trackEvent', 'learneth', 'previous_step', `${entity.name}/${previousStep && previousStep.name}`])}>
<i className="fas fa-chevron-left pr-1" /> <i className="fas fa-chevron-left pr-1" />
</Link> </Link>
)} )}
{stepId + 1}/{entity && <div className="">{entity.steps.length}</div>} {stepId + 1}/{entity && <div className="">{entity.steps.length}</div>}
{stepId < entity.steps.length - 1 && ( {stepId < entity.steps.length - 1 && (
<Link to={`/detail?id=${entity.id}&stepId=${stepId + 1}`}> <Link to={`/detail?id=${entity.id}&stepId=${stepId + 1}`} onClick={() => (window as any)._paq.push(['trackEvent', 'learneth', 'next_step', `${entity.name}/${nextStep && nextStep.name}`])} >
<i className="fas fa-chevron-right pl-1" /> <i className="fas fa-chevron-right pl-1" />
</Link> </Link>
)} )}
@ -73,7 +75,8 @@ function BackButton({entity}: any) {
variant="success" variant="success"
onClick={() => { onClick={() => {
setShow(false) setShow(false)
navigate('/home') navigate('/home');
(window as any)._paq.push(['trackEvent', 'learneth', 'leave_tutorial', entity && entity.name])
}} }}
> >
Yes Yes

@ -19,12 +19,14 @@ function RepoImporter({list, selectedRepo}: any): JSX.Element {
} }
const selectRepo = (repo: {name: string; branch: string}) => { const selectRepo = (repo: {name: string; branch: string}) => {
dispatch({type: 'workshop/loadRepo', payload: repo}) dispatch({type: 'workshop/loadRepo', payload: repo});
(window as any)._paq.push(['trackEvent', 'learneth', 'select_repo', `${name}/${branch}`])
} }
const importRepo = (event: {preventDefault: () => void}) => { const importRepo = (event: {preventDefault: () => void}) => {
event.preventDefault() event.preventDefault()
dispatch({type: 'workshop/loadRepo', payload: {name, branch}}) dispatch({type: 'workshop/loadRepo', payload: {name, branch}});
(window as any)._paq.push(['trackEvent', 'learneth', 'import_repo', `${name}/${branch}`])
} }
const resetAll = () => { const resetAll = () => {

@ -59,7 +59,7 @@ function HomePage(): JSX.Element {
> >
{selectedRepo.entities[item.id].name} {selectedRepo.entities[item.id].name}
</span> </span>
<Link to={`/list?id=${item.id}`} className="text-decoration-none float-right"> <Link onClick={() => (window as any)._paq.push(['trackEvent', 'learneth', 'start_workshop', selectedRepo.entities[item.id].name])} to={`/list?id=${item.id}`} className="text-decoration-none float-right">
<i className="fas fa-play-circle fa-lg" /> <i className="fas fa-play-circle fa-lg" />
</Link> </Link>
</div> </div>

@ -203,7 +203,8 @@ function StepDetailPage() {
<button <button
className="w-100 btn btn-success" className="w-100 btn btn-success"
onClick={() => { onClick={() => {
navigate(`/detail?id=${id}&stepId=${stepId + 1}`) navigate(`/detail?id=${id}&stepId=${stepId + 1}`);
(window as any)._paq.push(['trackEvent', 'learneth', 'navigate_next', `${id}/${stepId + 1}`])
}} }}
> >
Next Next
@ -213,7 +214,8 @@ function StepDetailPage() {
<button <button
className="w-100 btn btn-success" className="w-100 btn btn-success"
onClick={() => { onClick={() => {
navigate(`/list?id=${id}`) navigate(`/list?id=${id}`);
(window as any)._paq.push(['trackEvent', 'learneth', 'navigate_finish', id])
}} }}
> >
Finish tutorial Finish tutorial

@ -28,7 +28,7 @@ function StepListPage(): JSX.Element {
<SlideIn> <SlideIn>
<article className="list-group m-3"> <article className="list-group m-3">
{entity.steps.map((step: any, i: number) => ( {entity.steps.map((step: any, i: number) => (
<Link key={i} to={`/detail?id=${id}&stepId=${i}`} className="rounded-0 btn btn-light border-bottom text-left steplink"> <Link key={i} to={`/detail?id=${id}&stepId=${i}`} className="rounded-0 btn btn-light border-bottom text-left steplink" onClick={() => (window as any)._paq.push(['trackEvent', 'learneth', 'step_slide_in', `${id}/${i}/${step.name}`])}>
{step.name} » {step.name} »
</Link> </Link>
))} ))}

@ -41,7 +41,13 @@ const Model: ModelType = {
payload: { payload: {
screen: false, screen: false,
}, },
}) });
(window as any)._paq = {
push: (args) => {
remixClient.call('matomo' as any, 'track', args)
}
}
yield router.navigate('/home') yield router.navigate('/home')
}, },
@ -65,6 +71,8 @@ const Model: ModelType = {
return return
} }
(<any>window)._paq.push(['trackEvent', 'learneth', 'display_file', `${(step && step.name)}/${path}`])
toast.info(`loading ${path} into IDE`) toast.info(`loading ${path} into IDE`)
yield put({ yield put({
type: 'loading/save', type: 'loading/save',
@ -91,6 +99,7 @@ const Model: ModelType = {
}) })
toast.dismiss() toast.dismiss()
} catch (error) { } catch (error) {
(<any>window)._paq.push(['trackEvent', 'learneth', 'display_file_error', error.message])
toast.dismiss() toast.dismiss()
toast.error('File could not be loaded. Please try again.') toast.error('File could not be loaded. Please try again.')
yield put({ yield put({
@ -136,16 +145,16 @@ const Model: ModelType = {
yield remixClient.call('fileManager', 'setFile', path, step.test.content) yield remixClient.call('fileManager', 'setFile', path, step.test.content)
const result = yield remixClient.call('solidityUnitTesting', 'testFromPath', path) const result = yield remixClient.call('solidityUnitTesting', 'testFromPath', path)
console.log('result ', result) console.log('result ', result);
if (!result) { if (!result) {
yield put({ yield put({
type: 'remixide/save', type: 'remixide/save',
payload: { errors: ['Compiler failed to test this file']}, payload: { errors: ['Compiler failed to test this file']},
}) });
(<any>window)._paq.push(['trackEvent', 'learneth', 'test_step_error', 'Compiler failed to test this file'])
} else { } else {
const success = result.totalFailing === 0 const success = result.totalFailing === 0;
if (success) { if (success) {
yield put({ yield put({
type: 'remixide/save', type: 'remixide/save',
@ -159,13 +168,15 @@ const Model: ModelType = {
}, },
}) })
} }
(<any>window)._paq.push(['trackEvent', 'learneth', 'test_step', success])
} }
} catch (err) { } catch (err) {
console.log('TESTING ERROR', err) console.log('TESTING ERROR', err)
yield put({ yield put({
type: 'remixide/save', type: 'remixide/save',
payload: { errors: [String(err)]}, payload: { errors: [String(err)]},
}) });
(<any>window)._paq.push(['trackEvent', 'learneth', 'test_step_error', err])
} }
yield put({ yield put({
type: 'loading/save', type: 'loading/save',
@ -194,12 +205,15 @@ const Model: ModelType = {
const workshop = detail[selectedId] const workshop = detail[selectedId]
path = `.learneth/${workshop.name}/${step.name}/${path}` path = `.learneth/${workshop.name}/${step.name}/${path}`
yield remixClient.call('fileManager', 'setFile', path, content) yield remixClient.call('fileManager', 'setFile', path, content)
yield remixClient.call('fileManager', 'switchFile', `${path}`) yield remixClient.call('fileManager', 'switchFile', `${path}`);
(<any>window)._paq.push(['trackEvent', 'learneth', 'show_answer', path])
} catch (err) { } catch (err) {
yield put({ yield put({
type: 'remixide/save', type: 'remixide/save',
payload: { errors: [String(err)]}, payload: { errors: [String(err)]},
}) });
(<any>window)._paq.push(['trackEvent', 'learneth', 'show_answer_error', err.message])
} }
toast.dismiss() toast.dismiss()
@ -212,7 +226,8 @@ const Model: ModelType = {
}, },
*testSolidityCompiler(_, { put, select }) { *testSolidityCompiler(_, { put, select }) {
try { try {
yield remixClient.call('solidity', 'getCompilationResult') yield remixClient.call('solidity', 'getCompilationResult');
(<any>window)._paq.push(['trackEvent', 'learneth', 'test_solidity_compiler'])
} catch (err) { } catch (err) {
const errors = yield select((state) => state.remixide.errors) const errors = yield select((state) => state.remixide.errors)
yield put({ yield put({
@ -220,9 +235,10 @@ const Model: ModelType = {
payload: { payload: {
errors: [...errors, "The `Solidity Compiler` is not yet activated.<br>Please activate it using the `SOLIDITY` button in the `Featured Plugins` section of the homepage.<img class='img-thumbnail mt-3' src='assets/activatesolidity.png'>"], errors: [...errors, "The `Solidity Compiler` is not yet activated.<br>Please activate it using the `SOLIDITY` button in the `Featured Plugins` section of the homepage.<img class='img-thumbnail mt-3' src='assets/activatesolidity.png'>"],
}, },
}) });
(<any>window)._paq.push(['trackEvent', 'learneth', 'test_solidity_compiler_error', err.message])
} }
}, }
}, },
} }

@ -140,6 +140,7 @@ const Model: ModelType = {
} }
} }
} }
(<any>window)._paq.push(['trackEvent', 'learneth', 'load_repo', payload.name])
}, },
*resetAll(_, { put }) { *resetAll(_, { put }) {
yield put({ yield put({
@ -155,7 +156,8 @@ const Model: ModelType = {
yield put({ yield put({
type: 'workshop/init', type: 'workshop/init',
}) });
(<any>window)._paq.push(['trackEvent', 'learneth', 'reset_all'])
}, },
}, },
} }

@ -56,6 +56,7 @@ import { xtermPlugin } from './app/plugins/electron/xtermPlugin'
import { ripgrepPlugin } from './app/plugins/electron/ripgrepPlugin' import { ripgrepPlugin } from './app/plugins/electron/ripgrepPlugin'
import { compilerLoaderPlugin, compilerLoaderPluginDesktop } from './app/plugins/electron/compilerLoaderPlugin' import { compilerLoaderPlugin, compilerLoaderPluginDesktop } from './app/plugins/electron/compilerLoaderPlugin'
import { GitPlugin } from './app/plugins/git' import { GitPlugin } from './app/plugins/git'
import { Matomo } from './app/plugins/matomo'
import {SolCoder} from './app/plugins/solcoderAI' import {SolCoder} from './app/plugins/solcoderAI'
@ -223,6 +224,9 @@ class AppComponent {
//---- git //---- git
const git = new GitPlugin() const git = new GitPlugin()
//---- matomo
const matomo = new Matomo()
//---------------- Solidity UML Generator ------------------------- //---------------- Solidity UML Generator -------------------------
const solidityumlgen = new SolidityUmlGen(appManager) const solidityumlgen = new SolidityUmlGen(appManager)
@ -357,7 +361,8 @@ class AppComponent {
templates, templates,
solcoder, solcoder,
git, git,
pluginStateLogger pluginStateLogger,
matomo
]) ])
//---- fs plugin //---- fs plugin
@ -476,7 +481,8 @@ class AppComponent {
'network', 'network',
'web3Provider', 'web3Provider',
'offsetToLineColumnConverter', 'offsetToLineColumnConverter',
'pluginStateLogger' 'pluginStateLogger',
'matomo'
]) ])
await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs']) await this.appManager.activatePlugin(['mainPanel', 'menuicons', 'tabs'])
await this.appManager.activatePlugin(['statusBar']) await this.appManager.activatePlugin(['statusBar'])

@ -7,6 +7,7 @@ import { PluginProfile, StatusBarInterface } from '../../types'
import { RemixUIStatusBar } from '@remix-ui/statusbar' import { RemixUIStatusBar } from '@remix-ui/statusbar'
import { FilePanelType } from '@remix-ui/workspace' import { FilePanelType } from '@remix-ui/workspace'
import { VerticalIcons } from './vertical-icons' import { VerticalIcons } from './vertical-icons'
import { CustomRemixApi } from '@remix-api'
const statusBarProfile: PluginProfile = { const statusBarProfile: PluginProfile = {
name: 'statusBar', name: 'statusBar',
@ -16,7 +17,7 @@ const statusBarProfile: PluginProfile = {
version: packageJson.version, version: packageJson.version,
} }
export class StatusBar extends Plugin implements StatusBarInterface { export class StatusBar extends Plugin<any, CustomRemixApi> implements StatusBarInterface {
htmlElement: HTMLDivElement htmlElement: HTMLDivElement
events: EventEmitter events: EventEmitter
filePanelPlugin: FilePanelType filePanelPlugin: FilePanelType
@ -75,7 +76,7 @@ export class StatusBar extends Plugin implements StatusBarInterface {
const workspaceName = localStorage.getItem('currentWorkspace') const workspaceName = localStorage.getItem('currentWorkspace')
workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = 'error' workspaceName && workspaceName.length > 0 ? this.currentWorkspaceName = workspaceName : this.currentWorkspaceName = 'error'
}) })
this.on('settings', 'copilotChoiceChanged', (isAiActive) => { this.on('settings', 'copilotChoiceChanged', (isAiActive: boolean) => {
this.isAiActive = isAiActive this.isAiActive = isAiActive
}) })
this.renderComponent() this.renderComponent()

@ -25,7 +25,8 @@ const profile = {
methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'writeFileNoRewrite', methods: ['closeAllFiles', 'closeFile', 'file', 'exists', 'open', 'writeFile', 'writeMultipleFiles', 'writeFileNoRewrite',
'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile', 'readFile', 'copyFile', 'copyDir', 'rename', 'mkdir', 'readdir', 'dirList', 'fileList', 'remove', 'getCurrentFile', 'getFile',
'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath', 'getFolder', 'setFile', 'switchFile', 'refresh', 'getProviderOf', 'getProviderByName', 'getPathFromUrl', 'getUrlFromPath',
'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodule', 'copyFolderToJson', 'diff' 'saveCurrentFile', 'setBatchFiles', 'isGitRepo', 'isFile', 'isDirectory', 'hasGitSubmodule', 'copyFolderToJson', 'diff',
'hasGitSubmodules'
], ],
kind: 'file-system' kind: 'file-system'
} }

@ -0,0 +1,26 @@
'use strict'
import { Plugin } from '@remixproject/engine'
const _paq = window._paq = window._paq || []
const profile = {
name: 'matomo',
description: 'send analytics to Matomo',
methods: ['track'],
events: [''],
version: '1.0.0'
}
const allowedPlugins = ['LearnEth', 'etherscan', 'vyper', 'circuit-compiler', 'doc-gen', 'doc-viewer', 'solhint', 'walletconnect', 'scriptRunner']
export class Matomo extends Plugin {
constructor() {
super(profile)
}
async track(data: string[]) {
if (!allowedPlugins.includes(this.currentRequest.from)) return
_paq.push(data)
}
}

@ -241,7 +241,7 @@ textarea {
font-size: inherit; font-size: inherit;
line-height: inherit; line-height: inherit;
background-color: #2b2b2b; background-color: #2b2b2b;
color: #d5d5d5d5d5d5; color: #d5d5d5;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 2px rgba(79, 86, 89, 0.25); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 0 2px rgba(79, 86, 89, 0.25);
} }
button, button,

@ -5663,7 +5663,7 @@ button.bg-success:hover {
background-color: #27926b !important; background-color: #27926b !important;
} }
.bg-info { .bg-info {
background-color: #274458 !important; background-color: #35576e !important;
} }
a.bg-info:focus, a.bg-info:focus,
a.bg-info:hover, a.bg-info:hover,

@ -81,7 +81,8 @@ let requiredModules = [ // services + layout views + system views
'dgit', 'dgit',
'pinnedPanel', 'pinnedPanel',
'pluginStateLogger', 'pluginStateLogger',
'remixGuide' 'remixGuide',
'matomo'
] ]

@ -0,0 +1,12 @@
import { IFilePanel } from '@remixproject/plugin-api'
import { StatusEvents } from '@remixproject/plugin-utils'
export interface IFilePanelApi {
events: IFilePanel['events'] & {
workspaceInitializationCompleted: () => void;
switchToWorkspace: (workspace: string) => Promise<void>;
} & StatusEvents
methods: IFilePanel['methods'] & {
}
}

@ -4,7 +4,7 @@ import { IFileSystem } from "@remixproject/plugin-api"
// Extended interface with 'diff' method // Extended interface with 'diff' method
export interface IExtendedFileSystem extends IFileSystem { export interface IExtendedFileSystem extends IFileSystem {
methods: IFileSystem['methods'] & { methods: IFileSystem['methods'] & {
/** Compare the differences between two files */
diff(change: commitChange): Promise<void> diff(change: commitChange): Promise<void>
isGitRepo(): Promise<boolean>
}; };
} }

@ -3,6 +3,8 @@ import { StatusEvents } from '@remixproject/plugin-utils'
export interface ISettings { export interface ISettings {
events: { events: {
configChanged: () => void, configChanged: () => void,
copilotChoiceUpdated: (isChecked: boolean) => void,
copilotChoiceChanged: (isChecked: boolean) => void,
} & StatusEvents } & StatusEvents
methods: { methods: {
getGithubAccessToken(): string getGithubAccessToken(): string

@ -6,6 +6,8 @@ import { IFileDecoratorApi } from "./plugins/filedecorator-api"
import { IExtendedFileSystem } from "./plugins/fileSystem-api" import { IExtendedFileSystem } from "./plugins/fileSystem-api"
import { INotificationApi } from "./plugins/notification-api" import { INotificationApi } from "./plugins/notification-api"
import { ISettings } from "./plugins/settings-api" import { ISettings } from "./plugins/settings-api"
import { IFilePanelApi } from "./plugins/filePanel-api"
import { Plugin } from "@remixproject/engine"
export interface ICustomRemixApi extends IRemixApi { export interface ICustomRemixApi extends IRemixApi {
dgitApi: IGitApi dgitApi: IGitApi
@ -14,6 +16,7 @@ export interface ICustomRemixApi extends IRemixApi {
settings: ISettings settings: ISettings
fileDecorator: IFileDecoratorApi fileDecorator: IFileDecoratorApi
fileManager: IExtendedFileSystem fileManager: IExtendedFileSystem
filePanel: IFilePanelApi
} }
export declare type CustomRemixApi = Readonly<ICustomRemixApi> export declare type CustomRemixApi = Readonly<ICustomRemixApi>

@ -178,46 +178,48 @@ const RemixApp = (props: IRemixAppUi) => {
<OriginWarning></OriginWarning> <OriginWarning></OriginWarning>
<MatomoDialog hide={!appReady} okFn={() => setShowEnterDialog(true)}></MatomoDialog> <MatomoDialog hide={!appReady} okFn={() => setShowEnterDialog(true)}></MatomoDialog>
{showEnterDialog && <EnterDialog handleUserChoice={(type) => handleUserChosenType(type)}></EnterDialog>} {showEnterDialog && <EnterDialog handleUserChoice={(type) => handleUserChosenType(type)}></EnterDialog>}
<div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE"> <div className='d-flex flex-column'>
<div id="icon-panel" data-id="remixIdeIconPanel" className="custom_icon_panel iconpanel bg-light"> <div className={`remixIDE ${appReady ? '' : 'd-none'}`} data-id="remixIDE">
{props.app.menuicons.render()} <div id="icon-panel" data-id="remixIdeIconPanel" className="custom_icon_panel iconpanel bg-light">
</div> {props.app.menuicons.render()}
<div </div>
ref={sidePanelRef} <div
id="side-panel" ref={sidePanelRef}
data-id="remixIdeSidePanel" id="side-panel"
className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`} data-id="remixIdeSidePanel"
> className={`sidepanel border-right border-left ${hideSidePanel ? 'd-none' : ''}`}
{props.app.sidePanel.render()} >
</div> {props.app.sidePanel.render()}
<DragBar </div>
resetTrigger={resetLeftTrigger}
maximiseTrigger={maximiseLeftTrigger}
minWidth={305}
refObject={sidePanelRef}
hidden={hideSidePanel}
setHideStatus={setHideSidePanel}
layoutPosition='left'
></DragBar>
<div id="main-panel" data-id="remixIdeMainPanel" className="mainpanel d-flex">
<RemixUIMainPanel layout={props.app.layout}></RemixUIMainPanel>
</div>
<div id="pinned-panel" ref={pinnedPanelRef} data-id="remixIdePinnedPanel" className={`flex-row-reverse pinnedpanel border-right border-left ${hidePinnedPanel ? 'd-none' : 'd-flex'}`}>
{props.app.pinnedPanel.render()}
</div>
{
!hidePinnedPanel &&
<DragBar <DragBar
resetTrigger={resetRightTrigger} resetTrigger={resetLeftTrigger}
maximiseTrigger={maximiseRightTrigger} maximiseTrigger={maximiseLeftTrigger}
minWidth={331} minWidth={305}
refObject={pinnedPanelRef} refObject={sidePanelRef}
hidden={hidePinnedPanel} hidden={hideSidePanel}
setHideStatus={setHidePinnedPanel} setHideStatus={setHideSidePanel}
layoutPosition='right' layoutPosition='left'
></DragBar> ></DragBar>
} <div id="main-panel" data-id="remixIdeMainPanel" className="mainpanel d-flex">
<div>{props.app.hiddenPanel.render()}</div> <RemixUIMainPanel layout={props.app.layout}></RemixUIMainPanel>
</div>
<div id="pinned-panel" ref={pinnedPanelRef} data-id="remixIdePinnedPanel" className={`flex-row-reverse pinnedpanel border-right border-left ${hidePinnedPanel ? 'd-none' : 'd-flex'}`}>
{props.app.pinnedPanel.render()}
</div>
{
!hidePinnedPanel &&
<DragBar
resetTrigger={resetRightTrigger}
maximiseTrigger={maximiseRightTrigger}
minWidth={331}
refObject={pinnedPanelRef}
hidden={hidePinnedPanel}
setHideStatus={setHidePinnedPanel}
layoutPosition='right'
></DragBar>
}
<div>{props.app.hiddenPanel.render()}</div>
</div>
<div className="statusBar fixed-bottom"> <div className="statusBar fixed-bottom">
{props.app.statusBar.render()} {props.app.statusBar.render()}
</div> </div>

@ -1,6 +1,6 @@
import React, {useState, useEffect} from 'react' // eslint-disable-line import React, {useState, useEffect} from 'react' // eslint-disable-line
import { FormattedMessage, useIntl } from 'react-intl' import { FormattedMessage, useIntl } from 'react-intl'
import { ContractPropertyName, ContractSelectionProps } from './types' import { ContractPropertyName, ContractSelectionProps, ScanReport } from './types'
import {PublishToStorage} from '@remix-ui/publish-to-storage' // eslint-disable-line import {PublishToStorage} from '@remix-ui/publish-to-storage' // eslint-disable-line
import {TreeView, TreeViewItem} from '@remix-ui/tree-view' // eslint-disable-line import {TreeView, TreeViewItem} from '@remix-ui/tree-view' // eslint-disable-line
import {CopyToClipboard} from '@remix-ui/clipboard' // eslint-disable-line import {CopyToClipboard} from '@remix-ui/clipboard' // eslint-disable-line
@ -312,10 +312,10 @@ export const ContractSelection = (props: ContractSelectionProps) => {
const url = data.payload.scan_details.link const url = data.payload.scan_details.link
const { data: scanData } = await axios.post('https://solidityscan.remixproject.org/downloadResult', { url }) const { data: scanData } = await axios.post('https://solidityscan.remixproject.org/downloadResult', { url })
const scanDetails: Record<string, any>[] = scanData.scan_report.multi_file_scan_details const scanReport: ScanReport = scanData.scan_report
if (scanDetails && scanDetails.length) { if (scanReport?.multi_file_scan_details?.length) {
await plugin.call('terminal', 'logHtml', <SolScanTable scanDetails={scanDetails} fileName={fileName}/>) await plugin.call('terminal', 'logHtml', <SolScanTable scanReport={scanReport} fileName={fileName}/>)
} else { } else {
const modal: AppModal = { const modal: AppModal = {
id: 'SolidityScanError', id: 'SolidityScanError',
@ -338,7 +338,7 @@ export const ContractSelection = (props: ContractSelectionProps) => {
title: <FormattedMessage id="solidity.solScan.modalTitle" />, title: <FormattedMessage id="solidity.solScan.modalTitle" />,
message: <div className='d-flex flex-column'> message: <div className='d-flex flex-column'>
<span><FormattedMessage id="solidity.solScan.modalMessage" /> <span><FormattedMessage id="solidity.solScan.modalMessage" />
<a href={'https://solidityscan.com'} <a href={'https://solidityscan.com/?utm_campaign=remix&utm_source=remix'}
target="_blank" target="_blank"
onClick={() => _paq.push(['trackEvent', 'solidityCompiler', 'solidityScan', 'learnMore'])}> onClick={() => _paq.push(['trackEvent', 'solidityCompiler', 'solidityScan', 'learnMore'])}>
Learn more Learn more

@ -1,27 +1,22 @@
// eslint-disable-next-line no-use-before-define // eslint-disable-next-line no-use-before-define
import React from 'react' import React from 'react'
import parse from 'html-react-parser' import parse from 'html-react-parser'
import { ScanReport } from './types'
const _paq = (window._paq = window._paq || []) const _paq = (window._paq = window._paq || [])
interface SolScanTableProps { interface SolScanTableProps {
scanDetails: Record<string, any>[], scanReport: ScanReport
fileName: string fileName: string
} }
export function SolScanTable(props: SolScanTableProps) { export function SolScanTable(props: SolScanTableProps) {
const { scanDetails, fileName } = props const { scanReport, fileName } = props
const { multi_file_scan_details, multi_file_scan_summary } = scanReport
return ( return (
<> <>
<br/> <br/>
<h6>SolidityScan result for <b>{fileName}</b>:</h6> <h6>SolidityScan result for <b>{fileName}</b>:</h6>
<p className='text-success'><b>{scanDetails.length} warnings </b> found. See the warning details below. For more details,&nbsp;
<a href="https://solidityscan.com/signup"
target='_blank'
onClick={() => _paq.push(['trackEvent', 'solidityCompiler', 'solidityScan', 'goToSolidityScan'])}>
go to SolidityScan.
</a>
</p>
<table className="table table-bordered table-hover"> <table className="table table-bordered table-hover">
<thead> <thead>
<tr> <tr>
@ -35,7 +30,7 @@ export function SolScanTable(props: SolScanTableProps) {
</thead> </thead>
<tbody> <tbody>
{ {
Array.from(scanDetails, (template, index) => { Array.from(multi_file_scan_details, (template, index) => {
return ( return (
<tr key={template.template_details.issue_id}> <tr key={template.template_details.issue_id}>
<td scope="col">{index + 1}.</td> <td scope="col">{index + 1}.</td>
@ -51,6 +46,22 @@ export function SolScanTable(props: SolScanTableProps) {
</tbody> </tbody>
</table> </table>
{ multi_file_scan_summary ? (
<>
<p className='text-success'><b>Scan Summary: </b></p>
<p>&emsp; Lines Analyzed: {multi_file_scan_summary.lines_analyzed_count}</p>
<p>&emsp; Scan Score: {multi_file_scan_summary.score_v2}</p>
<p>&emsp; Issue Distribution: { JSON.stringify(multi_file_scan_summary.issue_severity_distribution, null, 1)} </p>
<p>For more details,&nbsp;
<a href="https://solidityscan.com/?utm_campaign=remix&utm_source=remix"
target='_blank'
onClick={() => _paq.push(['trackEvent', 'solidityCompiler', 'solidityScan', 'goToSolidityScan'])}>
go to SolidityScan.
</a>
</p>
</>
): null}
</> </>
) )
} }

@ -2,6 +2,40 @@ import { ICompilerApi, ConfigurationSettings, iSolJsonBinData } from '@remix-pro
import { CompileTabLogic } from '../logic/compileTabLogic' import { CompileTabLogic } from '../logic/compileTabLogic'
export type onCurrentFileChanged = (fileName: string) => void export type onCurrentFileChanged = (fileName: string) => void
//// SolidityScan Types
export interface ScanTemplate {
issue_id: string
issue_name: string
issue_remediation?: string
issue_severity: string
issue_status: string
static_issue_description: string
issue_description?: string
issue_confidence: string
metric_wise_aggregated_findings?: Record<string, any>[]
}
export interface ScanDetails {
issue_id: string
no_of_findings: string
template_details: ScanTemplate
}
export interface ScanReport {
details_enabled: boolean
file_url_list: string[]
multi_file_scan_details: ScanDetails[]
multi_file_scan_summary: Record<string, any>
multi_file_scan_status: string
scan_id: string
scan_status: string
scan_type: string
// others
}
//// SolidityScan Types
export interface SolidityCompilerProps { export interface SolidityCompilerProps {
api: ICompilerApi api: ICompilerApi
} }

@ -18,7 +18,7 @@
* approximately same height with vscode statusbar * approximately same height with vscode statusbar
**/ **/
.remixui_statusbar_height { .remixui_statusbar_height {
height: 21px; height: 1.6rem;
} }
.remixui_statusbar_activelink { .remixui_statusbar_activelink {

@ -32,7 +32,7 @@ export default function GitStatus({ plugin, gitBranchName, setGitBranchName }: G
setGitBranchName('Not a git repo') setGitBranchName('Not a git repo')
} }
}) })
plugin.on('dGitProvider', 'init', async () => { plugin.on('dgitApi', 'init', async () => {
const isGit = await plugin.call('fileManager', 'isGitRepo') const isGit = await plugin.call('fileManager', 'isGitRepo')
if (isGit) { if (isGit) {
const workspace = localStorage.getItem('currentWorkspace') const workspace = localStorage.getItem('currentWorkspace')
@ -52,7 +52,7 @@ export default function GitStatus({ plugin, gitBranchName, setGitBranchName }: G
} }
const initializeNewGitRepo = async () => { const initializeNewGitRepo = async () => {
await plugin.call('dGitProvider', 'init') await plugin.call('dgitApi', 'init')
const isActive = await plugin.call('manager', 'isActive', 'dgit') const isActive = await plugin.call('manager', 'isActive', 'dgit')
if (isLocalHost === false) { if (isLocalHost === false) {
if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit') if (!isActive) await plugin.call('manager', 'activatePlugin', 'dgit')

@ -71,7 +71,7 @@ export function RemixUIStatusBar({ statusBarPlugin }: RemixUIStatusBarProps) {
<ScamDetails refs={refs} floatStyle={{ ...floatingStyles, minHeight: 'auto', alignContent: 'center', paddingRight: '0.5rem' }} getFloatingProps={getFloatingProps} scamAlerts={scamAlerts} /> <ScamDetails refs={refs} floatStyle={{ ...floatingStyles, minHeight: 'auto', alignContent: 'center', paddingRight: '0.5rem' }} getFloatingProps={getFloatingProps} scamAlerts={scamAlerts} />
</FloatingFocusManager> </FloatingFocusManager>
)} )}
<div className="d-flex remixui_statusbar_height flex-row bg-primary justify-content-between align-items-center"> <div className="d-flex remixui_statusbar_height flex-row bg-info justify-content-between align-items-center">
<div className="remixui_statusbar remixui_statusbar_gitstatus"> <div className="remixui_statusbar remixui_statusbar_gitstatus">
<GitStatus plugin={statusBarPlugin} gitBranchName={gitBranchName} setGitBranchName={setGitBranchName} /> <GitStatus plugin={statusBarPlugin} gitBranchName={gitBranchName} setGitBranchName={setGitBranchName} />
</div> </div>

@ -257,7 +257,7 @@ export function Workspace() {
} }
} }
global.plugin.on('dGitProvider', 'repositoryWithSubmodulesCloned', () => { global.plugin.on('dgitApi', 'repositoryWithSubmodulesCloned', () => {
setHighlightUpdateSubmodules(true) setHighlightUpdateSubmodules(true)
}) })
}, []) }, [])

Loading…
Cancel
Save