Title section, File section, Learn section for Hoe tab

builtincomp
lianahus 2 years ago committed by Aniket
parent 22c5351b96
commit c4f12804bb
  1. 6
      apps/remix-ide/src/app/ui/landing-page/landing-page.js
  2. 161
      libs/remix-ui/home-tab/src/lib/components/homeTabFile.tsx
  3. 56
      libs/remix-ui/home-tab/src/lib/components/homeTabLearn.tsx
  4. 101
      libs/remix-ui/home-tab/src/lib/components/homeTabTitle.tsx
  5. 2
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.css
  6. 204
      libs/remix-ui/home-tab/src/lib/remix-ui-home-tab.tsx

@ -30,9 +30,9 @@ export class LandingPage extends ViewPlugin {
} }
render () { render () {
return <div id='landingPageHomeContainer' className='remixui_homeContainer justify-content-between bg-light d-flex' data-id='landingPageHomeContainer'><RemixUiHomeTab return <div id='landingPageHomeContainer' className='remixui_homeContainer justify-content-between bg-light d-flex' data-id='landingPageHomeContainer'>
plugin={this} <RemixUiHomeTab plugin={this} />
/></div> </div>
} }
} }

@ -0,0 +1,161 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useRef, useReducer } from 'react'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
interface HomeTabFileProps {
plugin: any
}
const loadingInitialState = {
tooltip: '',
showModalDialog: false,
importSource: ''
}
const loadingReducer = (state = loadingInitialState, action) => {
return { ...state, tooltip: action.tooltip, showModalDialog: false, importSource: '' }
}
function HomeTabFile ({plugin}: HomeTabFileProps) {
const [state, setState] = useState<{
searchInput: string,
showModalDialog: boolean,
modalInfo: { title: string, loadItem: string, examples: Array<string> },
importSource: string,
toasterMsg: string
}>({
searchInput: '',
showModalDialog: false,
modalInfo: { title: '', loadItem: '', examples: [] },
importSource: '',
toasterMsg: ''
})
const [, dispatch] = useReducer(loadingReducer, loadingInitialState)
const inputValue = useRef(null)
const processLoading = () => {
const contentImport = plugin.contentImport
const workspace = plugin.fileManager.getProvider('workspace')
contentImport.import(
state.importSource,
(loadingMsg) => dispatch({ tooltip: loadingMsg }),
async (error, content, cleanUrl, type, url) => {
if (error) {
toast(error.message || error)
} else {
try {
if (await workspace.exists(type + '/' + cleanUrl)) toast('File already exists in workspace')
else {
workspace.addExternal(cleanUrl, content, url)
plugin.call('menuicons', 'select', 'filePanel')
}
} catch (e) {
toast(e.message)
}
}
}
)
setState(prevState => {
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const toast = (message: string) => {
setState(prevState => {
return { ...prevState, toasterMsg: message }
})
}
const createNewFile = async () => {
plugin.verticalIcons.select('filePanel')
await plugin.call('filePanel', 'createNewFile')
}
const uploadFile = async (target) => {
await plugin.call('filePanel', 'uploadFile', target)
}
const connectToLocalhost = () => {
plugin.appManager.activatePlugin('remixd')
}
const importFromGist = () => {
plugin.call('gistHandler', 'load', '')
plugin.verticalIcons.select('filePanel')
}
const showFullMessage = (title: string, loadItem: string, examples: Array<string>) => {
setState(prevState => {
return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples } }
})
}
const hideFullMessage = () => { //eslint-disable-line
setState(prevState => {
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const examples = state.modalInfo.examples.map((urlEl, key) => (<div key={key} className="p-1 user-select-auto"><a>{urlEl}</a></div>))
return (
<>
<ModalDialog
id='homeTab'
title={ 'Import from ' + state.modalInfo.title }
okLabel='Import'
hide={ !state.showModalDialog }
handleHide={ () => hideFullMessage() }
okFn={ () => processLoading() }
>
<div className="p-2 user-select-auto">
{ state.modalInfo.loadItem !== '' && <span>Enter the { state.modalInfo.loadItem } you would like to load.</span> }
{ state.modalInfo.examples.length !== 0 &&
<>
<div>e.g</div>
<div>
{ examples }
</div>
</> }
<input
ref={inputValue}
type='text'
name='prompt_text'
id='inputPrompt_text'
className="w-100 mt-1 form-control"
data-id="homeTabModalDialogCustomPromptText"
value={state.importSource}
onInput={(e) => {
setState(prevState => {
return { ...prevState, importSource: inputValue.current.value }
})
}}
/>
</div>
</ModalDialog>
<Toaster message={state.toasterMsg} />
<div className="justify-content-start p-2 border-bottom d-flex flex-column" id="hTFileSection">
<label>Files</label>
<button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => createNewFile()}>New File</button>
<label className="btn p-2 border my-1" style={{width: 'fit-content'}} htmlFor="openFileInput">Open File</label>
<input title="open file" type="file" id="openFileInput" onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}} multiple />
<button className="btn p-2 border my-1" style={{width: 'fit-content'}} onClick={() => connectToLocalhost()}>Connect for Localhost</button>
<label className="pt-2">Load From</label>
<div className="d-flex">
<button className="btn p-2 border mr-2" data-id="landingPageImportFromGitHubButton" onClick={() => showFullMessage('GitHub', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'])}>GitHub</button>
<button className="btn p-2 border mr-2" data-id="landingPalandingPageImportFromGistButtongeImportFromGistButton" onClick={() => importFromGist()}>Gist</button>
<button className="btn p-2 border mr-2" onClick={() => showFullMessage('Ipfs', 'ipfs URL', ['ipfs://<ipfs-hash>'])}>IPFS</button>
<button className="btn p-2 border" onClick={() => showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])}>HTTPS</button>
</div>
</div>
</>
)
}
export default HomeTabFile

@ -0,0 +1,56 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useEffect, useState, useContext } from 'react'
import { ThemeContext } from '../themeContext'
enum VisibleTutorial {
Basics,
Intermediate,
Advanced
}
function HomeTabLearn () {
const [state, setState] = useState<{
visibleTutorial: VisibleTutorial
}>({
visibleTutorial: VisibleTutorial.Basics
})
const themeFilter = useContext(ThemeContext)
const openLink = () => {
window.open("https://remix-ide.readthedocs.io/en/latest/search.html?q=learneth&check_keywords=yes&area=default", '_blank')
}
return (
<div className="d-flex px-2 pb-2 pt-2 d-flex flex-column" id="hTLearnSection">
<div className="d-flex justify-content-between">
<label className="pt-2 align-self-center m-0">Learn</label>
<button
onClick={ ()=> openLink()}
className="h-100 px-2 pt-0 btn"
>
<img className="align-self-center" src="assets/img/learnEthLogo.webp" alt="" style={ { filter: themeFilter.filter, width: "1rem", height: "1ren" } } />
</button>
</div>
<div className="d-flex flex-column">
<button className="btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Basics }})}>
{(state.visibleTutorial === VisibleTutorial.Basics) && <div className="text-left">
Introduction to Remix's interface and concepts used in Ethereum, as well as the basics of Solidity.
</div>}
<label className="pb-1 float-left" style={{fontSize: "1rem"}}>Remix Basics</label>
</button>
<button className="btn border " onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Intermediate }})}>
{(state.visibleTutorial === VisibleTutorial.Intermediate) && <div className="text-left">Using the web3.js to interact with a contract. Using Recorder tool.</div>}
<label className="pb-1 float-left" style={{fontSize: "1rem"}}>Remix Intermediate</label>
</button>
<button className="btn border" onClick={() => setState((prevState) => {return { ...prevState, visibleTutorial: VisibleTutorial.Advanced }})}>
{(state.visibleTutorial === VisibleTutorial.Advanced) && <div className="text-left">Learn the Proxy Pattern and working with Libraries in Remix. Learn to use the Debugger</div>}
<label className="pb-1 float-left" style={{fontSize: "1rem"}}>Remix Advanced</label>
</button>
</div>
<br/>
</div>
)
}
export default HomeTabLearn

@ -0,0 +1,101 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useEffect, useState, useRef } from 'react'
function HomeTabTitle () {
const [state, setState] = useState<{
searchInput: string
}>({
searchInput: ''
})
useEffect(() => {
document.addEventListener("keyup", (e) => handleSearchKeyDown(e))
return () => {
document.removeEventListener("keyup", handleSearchKeyDown)
}
}, [])
const searchInputRef = useRef(null)
const remiAudioEl = useRef(null)
const playRemi = async () => {
remiAudioEl.current.play()
}
const handleSearchKeyDown = (e: KeyboardEvent) => {
if (e.target !== searchInputRef.current) return
if (e.key === "Enter") {
openLink()
setState(prevState => {
return { ...prevState, searchInput: '' }
})
searchInputRef.current.value = ""
}
}
const openLink = (url = "") => {
if (url === "") {
window.open("https://remix-ide.readthedocs.io/en/latest/search.html?q=" + state.searchInput + "&check_keywords=yes&area=default", '_blank')
} else {
window.open(url, '_blank')
}
}
return (
<div className="px-2 pb-2 pt-2 d-flex flex-column border-bottom" id="hTTitleSection">
<div className="mr-4 d-flex">
<img className="align-self-end remixui_home_logoImg" src="assets/img/guitarRemiCroped.webp" onClick={ () => playRemi() } alt=""></img>
<audio
id="remiAudio"
muted={false}
src="assets/audio/remiGuitar-single-power-chord-A-minor.wav"
ref={remiAudioEl}
></audio>
</div>
<div className="d-flex justify-content-between">
<span className="h-80" style={ { fontSize: 'xx-large', fontFamily: "Noah" } }>Remix</span>
<span>
<button
onClick={ ()=> openLink("https://www.youtube.com/channel/UCjTUPyFEr2xDGN6Cg8nKDaA")}
className="h-100 btn fab fa-youtube">
</button>
<button
onClick={ ()=> openLink("https://twitter.com/EthereumRemix")}
className="h-100 pl-2 btn fab fa-twitter">
</button>
</span>
</div>
<b className="pb-1 text-dark" style={{fontStyle: 'italic'}}>The Native IDE for Solidity Development.</b>
<div className="pb-1" id="hTGeneralLinks">
<a className="remixui_home_text" target="__blank" href="https://remix-ide.readthedocs.io/en/latest">Learn more</a>
<a className="pl-2 remixui_home_text" target="__blank" href="https://remix-ide.readthedocs.io/en/latest">Documentation</a>
<a className="pl-2 remixui_home_text" target="__blank" href="https://remix-ide.readthedocs.io/en/latest">more</a>
<a className="pl-2 remixui_home_text" target="__blank" href="https://remix-ide.readthedocs.io/en/latest">more</a>
</div>
<div className="d-flex pb-1 align-items-center">
<input
ref={searchInputRef}
onChange={(event) => {
setState(prevState => {
return { ...prevState, searchInput: event.target.value.trim() }
})
}}
type="text"
className="border form-control border-right-0"
id="searchInput"
placeholder="Search Documentation"
data-id="terminalInputSearch"
/>
<button
className="form-control border d-flex align-items-center p-2 justify-content-center fas fa-search bg-light"
onClick={ (e) => openLink() }
disabled={state.searchInput === ""}
style={{width: "3rem"}}
>
</button>
</div>
</div>
)
}
export default HomeTabTitle

@ -31,7 +31,7 @@
text-align: center; text-align: center;
} }
.remixui_home_logoImg { .remixui_home_logoImg {
height: 10em; height: 4rem;
} }
.remixui_home_rightPanel { .remixui_home_rightPanel {
right: 0; right: 0;

@ -1,11 +1,14 @@
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import './remix-ui-home-tab.css' import './remix-ui-home-tab.css'
import { ModalDialog } from '@remix-ui/modal-dialog' // eslint-disable-line
import { Toaster } from '@remix-ui/toaster' // eslint-disable-line
import PluginButton from './components/pluginButton' // eslint-disable-line import PluginButton from './components/pluginButton' // eslint-disable-line
import { ThemeContext, themes } from './themeContext' import { ThemeContext, themes } from './themeContext'
import { RSSFeed } from './components/rssFeed' import { RSSFeed } from './components/rssFeed'
import BasicLogo from 'libs/remix-ui/vertical-icons-panel/src/lib/components/BasicLogo'
import HomeTabTitle from './components/homeTabTitle'
import HomeTabFile from './components/homeTabFile'
import HomeTabLearn from './components/homeTabLearn'
declare global { declare global {
interface Window { interface Window {
_paq: any _paq: any
@ -18,71 +21,17 @@ export interface RemixUiHomeTabProps {
plugin: any plugin: any
} }
const loadingInitialState = {
tooltip: '',
showModalDialog: false,
importSource: ''
}
const loadingReducer = (state = loadingInitialState, action) => {
return { ...state, tooltip: action.tooltip, showModalDialog: false, importSource: '' }
}
export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => { export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
const { plugin } = props const { plugin } = props
const fileManager = plugin.fileManager
const [state, setState] = useState<{ const [state, setState] = useState<{
themeQuality: { filter: string, name: string }, themeQuality: { filter: string, name: string },
showMediaPanel: 'none' | 'twitter' | 'medium', showMediaPanel: 'none' | 'twitter' | 'medium'
showModalDialog: boolean,
modalInfo: { title: string, loadItem: string, examples: Array<string> },
importSource: string,
toasterMsg: string
}>({ }>({
themeQuality: themes.light, themeQuality: themes.light,
showMediaPanel: 'none', showMediaPanel: 'none'
showModalDialog: false,
modalInfo: { title: '', loadItem: '', examples: [] },
importSource: '',
toasterMsg: ''
}) })
const processLoading = () => {
const contentImport = plugin.contentImport
const workspace = fileManager.getProvider('workspace')
contentImport.import(
state.importSource,
(loadingMsg) => dispatch({ tooltip: loadingMsg }),
async (error, content, cleanUrl, type, url) => {
if (error) {
toast(error.message || error)
} else {
try {
if (await workspace.exists(type + '/' + cleanUrl)) toast('File already exists in workspace')
else {
workspace.addExternal(type + '/' + cleanUrl, content, url)
plugin.call('menuicons', 'select', 'filePanel')
}
} catch (e) {
toast(e.message)
}
}
}
)
setState(prevState => {
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const [, dispatch] = useReducer(loadingReducer, loadingInitialState)
const playRemi = async () => {
remiAudioEl.current.play()
}
const remiAudioEl = useRef(null)
const inputValue = useRef(null)
const rightPanel = useRef(null) const rightPanel = useRef(null)
useEffect(() => { useEffect(() => {
@ -101,7 +50,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
window.addEventListener('click', (event) => { window.addEventListener('click', (event) => {
const target = event.target as Element const target = event.target as Element
const id = target.id const id = target.id
if (id !== 'remixIDEHomeTwitterbtn' && id !== 'remixIDEHomeMediumbtn' && !rightPanel.current.contains(event.target)) { if (id !== 'remixIDEHomeTwitterbtn' && id !== 'remixIDEHomeMediumbtn' && (rightPanel && rightPanel.current && !rightPanel.current.contains(event.target))) {
// todo check event.target // todo check event.target
setState(prevState => { return { ...prevState, showMediaPanel: 'none' } }) setState(prevState => { return { ...prevState, showMediaPanel: 'none' } })
} }
@ -111,33 +60,13 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
scriptTwitter.src = 'https://platform.twitter.com/widgets.js' scriptTwitter.src = 'https://platform.twitter.com/widgets.js'
scriptTwitter.async = true scriptTwitter.async = true
document.body.appendChild(scriptTwitter) document.body.appendChild(scriptTwitter)
return () => { return () => {
document.body.removeChild(scriptTwitter) document.body.removeChild(scriptTwitter)
} }
}, []) }, [])
const toast = (message: string) => {
setState(prevState => {
return { ...prevState, toasterMsg: message }
})
}
const createNewFile = async () => {
plugin.verticalIcons.select('filePanel')
await plugin.call('filePanel', 'createNewFile')
}
const uploadFile = async (target) => {
await plugin.call('filePanel', 'uploadFile', target)
}
const connectToLocalhost = () => {
plugin.appManager.activatePlugin('remixd')
}
const importFromGist = () => {
plugin.call('gistHandler', 'load', '')
plugin.verticalIcons.select('filePanel')
}
const startSolidity = async () => { const startSolidity = async () => {
await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting']) await plugin.appManager.activatePlugin(['solidity', 'udapp', 'solidityStaticAnalysis', 'solidityUnitTesting'])
plugin.verticalIcons.select('solidity') plugin.verticalIcons.select('solidity')
@ -167,58 +96,38 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
plugin.verticalIcons.select('pluginManager') plugin.verticalIcons.select('pluginManager')
} }
const showFullMessage = (title: string, loadItem: string, examples: Array<string>) => {
setState(prevState => {
return { ...prevState, showModalDialog: true, modalInfo: { title: title, loadItem: loadItem, examples: examples } }
})
}
const hideFullMessage = () => { //eslint-disable-line
setState(prevState => {
return { ...prevState, showModalDialog: false, importSource: '' }
})
}
const maxHeight = Math.max(window.innerHeight - 150, 250) + 'px' const maxHeight = Math.max(window.innerHeight - 150, 250) + 'px'
const examples = state.modalInfo.examples.map((urlEl, key) => (<div key={key} className="p-1 user-select-auto"><a>{urlEl}</a></div>))
const elHeight = '4000px' const elHeight = '4000px'
return ( return (
<> <div className="d-flex flex-row w-100" id="remixUIHTAll">
<ModalDialog <div className="justify-content-between d-flex border-right flex-column" id="remixUIHTLeft" style={{flex: 2}}>
id='homeTab' <HomeTabTitle />
title={ 'Import from ' + state.modalInfo.title } <HomeTabFile plugin={plugin} />
okLabel='Import' <ThemeContext.Provider value={ state.themeQuality }>
hide={ !state.showModalDialog } <HomeTabLearn />
handleHide={ () => hideFullMessage() } </ThemeContext.Provider>
okFn={ () => processLoading() } </div>
> <div className="justify-content-between d-flex" id="remixUIHTRight" style={{flex: 4}}>
<div className="p-2 user-select-auto"> <div className="" id="hTFeaturedeSection">
{ state.modalInfo.loadItem !== '' && <span>Enter the { state.modalInfo.loadItem } you would like to load.</span> }
{ state.modalInfo.examples.length !== 0 &&
<>
<div>e.g</div>
<div>
{ examples }
</div>
</> }
<input
ref={inputValue}
type='text'
name='prompt_text'
id='inputPrompt_text'
className="w-100 mt-1 form-control"
data-id="homeTabModalDialogCustomPromptText"
value={state.importSource}
onInput={(e) => {
setState(prevState => {
return { ...prevState, importSource: inputValue.current.value }
})
}}
/>
</div> </div>
</ModalDialog> <div className="" id="hTScamAlertSection">
<Toaster message={state.toasterMsg} />
<div className="d-flex flex-column ml-4" id="remixUiRightPanel"> </div>
<div className="" id="hTGetStartedSection">
</div>
</div>
</div>
)
}
export default RemixUiHomeTab
/*
<div className="d-flex flex-column ml-4" id="remixUiRightPanel">
<div className="border-bottom d-flex flex-column mr-4 pb-3 mb-3"> <div className="border-bottom d-flex flex-column mr-4 pb-3 mb-3">
<div className="pt-2 d-flex justify-content-between"> <div className="pt-2 d-flex justify-content-between">
<div> <div>
@ -268,36 +177,7 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
</ThemeContext.Provider> </ThemeContext.Provider>
</div> </div>
</div> </div>
<div className="d-flex"> ------------------------
<div className="file">
<h4>File</h4>
<p className="mb-1">
<i className="mr-2 far fa-file"></i>
<label className="ml-1 mb-1 remixui_home_text" data-id="homeTabNewFile" onClick={() => createNewFile()}>New File</label>
</p>
<p className="mb-1">
<i className="mr-2 far fa-file-alt"></i>
<label className="ml-1 remixui_home_labelIt remixui_home_bigLabelSize remixui_home_text" htmlFor="openFileInput">
Open Files
</label>
<input title="open file" type="file" id="openFileInput" onChange={(event) => {
event.stopPropagation()
plugin.verticalIcons.select('filePanel')
uploadFile(event.target)
}} multiple />
</p>
<p className="mb-1">
<i className="mr-1 far fa-hdd"></i>
<label className="ml-1 remixui_home_text" onClick={() => connectToLocalhost()}>Connect to Localhost</label>
</p>
<p className="mt-3 mb-0"><label>LOAD FROM:</label></p>
<div className="btn-group">
<button className="btn mr-1 btn-secondary" data-id="landingPageImportFromGistButton" onClick={() => importFromGist()}>Gist</button>
<button className="btn mx-1 btn-secondary" data-id="landingPageImportFromGitHubButton" onClick={() => showFullMessage('GitHub', 'github URL', ['https://github.com/0xcert/ethereum-erc721/src/contracts/tokens/nf-token-metadata.sol', 'https://github.com/OpenZeppelin/openzeppelin-solidity/blob/67bca857eedf99bf44a4b6a0fc5b5ed553135316/contracts/access/Roles.sol'])}>GitHub</button>
<button className="btn mx-1 btn-secondary" onClick={() => showFullMessage('Ipfs', 'ipfs URL', ['ipfs://<ipfs-hash>'])}>Ipfs</button>
<button className="btn mx-1 btn-secondary" onClick={() => showFullMessage('Https', 'http/https raw content', ['https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol'])}>https</button>
</div>
</div>
<div className="ml-4 pl-4"> <div className="ml-4 pl-4">
<h4>Resources</h4> <h4>Resources</h4>
<p className="mb-1"> <p className="mb-1">
@ -367,8 +247,4 @@ export const RemixUiHomeTab = (props: RemixUiHomeTabProps) => {
</div> </div>
</div> </div>
</div> </div>
</> */
)
}
export default RemixUiHomeTab
Loading…
Cancel
Save