Skip to content

Commit 6d7f927

Browse files
authored
Merge pull request #6094 from ethereum/desktop-metamask-3
Desktop metamask 3
2 parents 287242b + c39d940 commit 6d7f927

File tree

82 files changed

+3572
-923
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+3572
-923
lines changed

.circleci/config.yml

Lines changed: 260 additions & 157 deletions
Large diffs are not rendered by default.

apps/remix-ide-e2e/src/commands/addFile.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ function addFile(browser: NightwatchBrowser, name: string, content: NightwatchCo
2828
browser.clickLaunchIcon('filePanel')
2929
}
3030
})
31+
.execute(function () {
32+
const container = document.querySelector('[data-test-id="virtuoso-scroller"]');
33+
container.scrollTop = container.scrollHeight;
34+
})
3135
.scrollInto(readmeSelector)
3236
.waitForElementVisible(readmeSelector)
3337
.click(readmeSelector).pause(1000) // focus on root directory

apps/remix-ide-e2e/src/githttpbackend/yarn.lock

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,7 @@ [email protected]:
2222
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
2323
integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
2424

25-
[email protected], body-parser@^1.20.2:
26-
version "1.20.3"
27-
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
28-
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
29-
dependencies:
30-
bytes "3.1.2"
31-
content-type "~1.0.5"
32-
debug "2.6.9"
33-
depd "2.0.0"
34-
destroy "1.2.0"
35-
http-errors "2.0.0"
36-
iconv-lite "0.4.24"
37-
on-finished "2.4.1"
38-
qs "6.13.0"
39-
raw-body "2.5.2"
40-
type-is "~1.6.18"
41-
unpipe "1.0.0"
42-
43-
body-parser@^1.20.3:
25+
[email protected], body-parser@^1.20.3:
4426
version "1.20.3"
4527
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
4628
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==

apps/remix-ide/src/app.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ import { circomPlugin } from './app/plugins/electron/circomElectronPlugin'
7171
import { GitHubAuthHandler } from './app/plugins/electron/gitHubAuthHandler'
7272
import { GitPlugin } from './app/plugins/git'
7373
import { Matomo } from './app/plugins/matomo'
74+
import { DesktopClient } from './app/plugins/desktop-client'
75+
import { DesktopHost } from './app/plugins/electron/desktopHostPlugin'
7476
import { WalletConnect } from './app/plugins/walletconnect'
7577

7678
import { TemplatesSelectionPlugin } from './app/plugins/templates-selection/templates-selection-plugin'
@@ -153,6 +155,8 @@ class AppComponent {
153155
popupPanel: PopupPanel
154156
statusBar: StatusBar
155157
settings: SettingsTab
158+
params: any
159+
desktopClientMode: boolean
156160
constructor() {
157161
const PlatFormAPi = new platformApi()
158162
Registry.getInstance().put({
@@ -161,6 +165,8 @@ class AppComponent {
161165
})
162166
this.appManager = new RemixAppManager()
163167
this.queryParams = new QueryParams()
168+
this.params = this.queryParams.get()
169+
this.desktopClientMode = this.params && this.params.activate && this.params.activate.split(',').includes('desktopClient')
164170
this._components = {} as Components
165171
// setup storage
166172
const configStorage = new Storage('config-v0.8:')
@@ -473,8 +479,14 @@ class AppComponent {
473479
this.engine.register([appUpdater])
474480
const remixAIDesktop = new remixAIDesktopPlugin()
475481
this.engine.register([remixAIDesktop])
482+
const desktopHost = new DesktopHost()
483+
this.engine.register([desktopHost])
476484
const githubAuthHandler = new GitHubAuthHandler()
477485
this.engine.register([githubAuthHandler])
486+
} else {
487+
//---- desktop client
488+
const desktopClient = new DesktopClient(blockchain)
489+
this.engine.register([desktopClient])
478490
}
479491

480492
const compilerloader = isElectron() ? new compilerLoaderPluginDesktop() : new compilerLoaderPlugin()
@@ -563,8 +575,6 @@ class AppComponent {
563575
}
564576

565577
async activate() {
566-
const queryParams = new QueryParams()
567-
const params: any = queryParams.get()
568578

569579
try {
570580
this.engine.register(await this.appManager.registeredPlugins())
@@ -642,13 +652,13 @@ class AppComponent {
642652
.activatePlugin(this.workspace)
643653
.then(async () => {
644654
try {
645-
if (params.deactivate) {
646-
await this.appManager.deactivatePlugin(params.deactivate.split(','))
655+
if (this.params.deactivate) {
656+
await this.appManager.deactivatePlugin(this.params.deactivate.split(','))
647657
}
648658
} catch (e) {
649659
console.log(e)
650660
}
651-
if (params.code && (!params.activate || params.activate.split(',').includes('solidity'))) {
661+
if (this.params.code && (!this.params.activate || this.params.activate.split(',').includes('solidity'))) {
652662
// if code is given in url we focus on solidity plugin
653663
this.menuicons.select('solidity')
654664
} else {
@@ -660,19 +670,19 @@ class AppComponent {
660670
}
661671
}
662672

663-
if (params.call) {
664-
const callDetails: any = params.call.split('//')
673+
if (this.params.call) {
674+
const callDetails = this.params.call.split('//')
665675
if (callDetails.length > 1) {
666676
this.appManager.call('notification', 'toast', `initiating ${callDetails[0]} and calling "${callDetails[1]}" ...`)
667677
// @todo(remove the timeout when activatePlugin is on 0.3.0)
668-
_paq.push(['trackEvent', 'App', 'queryParams-calls', params.call])
678+
_paq.push(['trackEvent', 'App', 'queryParams-calls', this.params.call])
669679
//@ts-ignore
670680
await this.appManager.call(...callDetails).catch(console.error)
671681
}
672682
}
673683

674-
if (params.calls) {
675-
const calls = params.calls.split('///')
684+
if (this.params.calls) {
685+
const calls = this.params.calls.split('///')
676686

677687
// call all functions in the list, one after the other
678688
for (const call of calls) {
@@ -717,6 +727,10 @@ class AppComponent {
717727

718728
// activate solidity plugin
719729
this.appManager.activatePlugin(['solidity', 'udapp', 'deploy-libraries', 'link-libraries', 'openzeppelin-proxy', 'scriptRunnerBridge'])
730+
731+
if (isElectron()){
732+
this.appManager.activatePlugin(['desktopHost'])
733+
}
720734
}
721735
}
722736

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import React, { useContext, useEffect } from 'react'
2+
import { AppContext, appActionTypes } from '@remix-ui/app'
3+
import { Provider } from '@remix-ui/environment-explorer'
4+
import { providerLogos } from '../udapp/run-tab'
5+
import { desktopConnection } from '@remix-api'
6+
7+
interface DesktopClientState {
8+
connected: desktopConnection
9+
providers: Provider[]
10+
disableconnect: boolean
11+
currentContext: string
12+
}
13+
14+
const DesktopClientUI = (props: DesktopClientState & { openDesktopApp: () => void } & { onConnect: (providerName: Provider) => void }) => {
15+
const appContext = useContext(AppContext)
16+
const { connected, providers, onConnect, disableconnect, currentContext } = props
17+
const [title, setTitle] = React.useState('Connecting...')
18+
const [disabled, setDisabled] = React.useState(false)
19+
const [hasInjected, setHasInjected] = React.useState(false)
20+
const [hasBrave, setHasBrave] = React.useState(false)
21+
const [filteredList, setFilteredList] = React.useState<Provider[]>([])
22+
23+
useEffect(() => {
24+
console.log('connected', props.connected)
25+
appContext.appStateDispatch({
26+
type: appActionTypes.setConnectedToDesktop,
27+
payload: props.connected,
28+
})
29+
appContext.appStateDispatch({
30+
type: appActionTypes.setShowPopupPanel,
31+
payload: false,
32+
})
33+
}, [props.connected])
34+
35+
useEffect(() => {
36+
console.log('providers', props.providers)
37+
const injectedProviders = providers.find((provider) => provider.config.isInjected)
38+
const braveProvider = providers.find((provider) => provider.name.toLowerCase().includes('brave'))
39+
setHasInjected(!!injectedProviders)
40+
setHasBrave(!!braveProvider)
41+
42+
setFilteredList(providers.filter((provider) => provider.config.isInjected == true && !provider.name.toLocaleLowerCase().includes('brave')))
43+
44+
}, [providers])
45+
46+
useEffect(() => {
47+
if (hasInjected) {
48+
setTitle('Connect to Browser Wallet')
49+
setDisabled(false)
50+
} else if (hasBrave && !hasInjected) {
51+
setTitle('Brave Wallet is not supported')
52+
setDisabled(true)
53+
} else {
54+
setTitle('Connecting...')
55+
}
56+
57+
}, [hasInjected, hasBrave])
58+
59+
if (disabled) {
60+
return (
61+
<div>
62+
<div className="d-flex p-4 bg-light flex-column">
63+
<h3>{title}</h3>
64+
<p>
65+
The Brave Wallet is not supported at this time.
66+
</p>
67+
</div>
68+
</div>
69+
)
70+
}
71+
72+
return (
73+
<div>
74+
<div className="d-flex p-4 bg-light flex-column">
75+
<h3>{title}</h3>
76+
<p>
77+
1. Connect to your favorite Ethereum wallet provider
78+
<br></br>2. Go back to the Remix Desktop application
79+
<br></br>3. Deploy using 'Browser Wallet'
80+
{hasBrave && <div className='text-warning'>
81+
Note: Brave Wallet is not supported.
82+
</div>}
83+
</p>
84+
</div>
85+
86+
<div>
87+
<div className="row">
88+
{filteredList && filteredList.length > 0 ? (
89+
filteredList
90+
.map((provider, index) => (
91+
<div key={index} className="col-md-4 mb-4">
92+
<div className="provider-item card h-100">
93+
<div className="card-body d-flex flex-column align-items-center">
94+
<div className="d-flex mb-2">{providerLogos[provider.name] && providerLogos[provider.name].map((logo, index) => <img key={index} src={logo} style={{ width: '2rem', height: '2rem', marginRight: '0.5rem' }} />)}</div>
95+
<h5 className="card-title">{provider.displayName}</h5>
96+
<p className="card-text">{provider.description}</p>
97+
<button data-id={`connection-btn-${provider.name}`} disabled={disableconnect || currentContext === provider.name} className="btn btn-primary mt-auto" onClick={() => onConnect(provider)}>
98+
{disableconnect ? 'please wait ...' : currentContext === provider.name ? 'Connected' : 'Connect'}
99+
</button>
100+
</div>
101+
</div>
102+
</div>
103+
))
104+
) : (
105+
<div className="col-12">
106+
<div className="alert alert-warning" role="alert">
107+
No injected providers found. Please install MetaMask or another browser wallet.
108+
</div>
109+
</div>
110+
)}
111+
</div>
112+
</div>
113+
</div>
114+
)
115+
}
116+
117+
export default DesktopClientUI

apps/remix-ide/src/app/components/status-bar.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { RemixUIStatusBar } from '@remix-ui/statusbar'
88
import { FilePanelType } from '@remix-ui/workspace'
99
import { VerticalIcons } from './vertical-icons'
1010
import { CustomRemixApi } from '@remix-api'
11+
import { AppAction } from '@remix-ui/app'
1112

1213
const statusBarProfile: PluginProfile = {
1314
name: 'statusBar',
@@ -22,7 +23,8 @@ export class StatusBar extends Plugin<any, CustomRemixApi> implements StatusBarI
2223
events: EventEmitter
2324
filePanelPlugin: FilePanelType
2425
verticalIcons: VerticalIcons
25-
dispatch: React.Dispatch<any> = () => {}
26+
dispatch: React.Dispatch<any> = () => { }
27+
appStateDispatch: React.Dispatch<AppAction> = () => { }
2628
currentWorkspaceName: string = ''
2729
isGitRepo: boolean = false
2830
isAiActive: boolean = false
@@ -86,6 +88,10 @@ export class StatusBar extends Plugin<any, CustomRemixApi> implements StatusBarI
8688
this.dispatch = dispatch
8789
}
8890

91+
setAppStateDispatch(appStateDispatch: React.Dispatch<AppAction>) {
92+
this.appStateDispatch = appStateDispatch
93+
}
94+
8995
renderComponent() {
9096
this.dispatch({
9197
plugins: this,
@@ -99,7 +105,7 @@ export class StatusBar extends Plugin<any, CustomRemixApi> implements StatusBarI
99105
render() {
100106
return (
101107
<div data-id="status-bar-container">
102-
<PluginViewWrapper plugin={this} />
108+
<PluginViewWrapper useAppContext={true} plugin={this} />
103109
</div>
104110
)
105111
}

apps/remix-ide/src/app/files/electronProvider.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,44 @@ export class ElectronProvider extends FileProvider {
7373
}
7474
}
7575

76+
/**
77+
* copy the folder recursively (internal use)
78+
* @param {string} path is the folder to be copied over
79+
* @param {Function} visitFile is a function called for each visited files
80+
* @param {Function} visitFolder is a function called for each visited folders
81+
*/
82+
async _copyFolderToJsonInternal (path, visitFile, visitFolder) {
83+
visitFile = visitFile || function () { /* do nothing. */ }
84+
visitFolder = visitFolder || function () { /* do nothing. */ }
85+
86+
const json = {}
87+
path = this.removePrefix(path)
88+
if (await window.remixFileSystem.exists(path)) {
89+
try {
90+
const items = await window.remixFileSystem.readdir(path)
91+
visitFolder({ path })
92+
if (items.length !== 0) {
93+
for (const item of items) {
94+
const file: any = {}
95+
const curPath = `${path}${path.endsWith('/') ? '' : '/'}${item.file}`
96+
if (item.isDirectory) {
97+
file.children = await this._copyFolderToJsonInternal(curPath, visitFile, visitFolder)
98+
} else {
99+
file.content = await window.remixFileSystem.readFile(curPath, 'utf8')
100+
visitFile({ path: curPath, content: file.content })
101+
}
102+
json[curPath] = file
103+
}
104+
}
105+
} catch (e) {
106+
console.log(e)
107+
throw new Error(e)
108+
}
109+
}
110+
console.log('json', json)
111+
return json
112+
}
113+
76114
/**
77115
* Removes the folder recursively
78116
* @param {*} path is the folder to be removed

apps/remix-ide/src/app/panels/layout.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { QueryParams } from '@remix-project/remix-lib'
66
const profile: Profile = {
77
name: 'layout',
88
description: 'layout',
9-
methods: ['minimize', 'maximiseSidePanel', 'resetSidePanel', 'maximizeTerminal', 'maximisePinnedPanel', 'resetPinnedPanel']
9+
methods: ['minimize', 'minimizeSidePanel', 'maximiseSidePanel', 'resetSidePanel', 'maximizeTerminal', 'maximisePinnedPanel', 'resetPinnedPanel']
1010
}
1111

1212
interface panelState {
@@ -144,6 +144,10 @@ export class Layout extends Plugin {
144144
this.emit('change', this.panels)
145145
}
146146

147+
async minimizeSidePanel () {
148+
this.event.emit('minimizesidepanel')
149+
}
150+
147151
async maximiseSidePanel () {
148152
const current = await this.call('sidePanel', 'currentFocus')
149153
this.maximized[current] = true

0 commit comments

Comments
 (0)