Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions components/CodeEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ import { MarkerSeverity } from './Editor/types'
// @ts-ignore
import sdkTypeDefs from '!raw-loader!generated/cryptostats-sdk.d.ts'

// @ts-ignore
import graphTypeDefs from '!raw-loader!generated/graph-ts.d.ts'

interface EditorProps {
onValidated: (code: string, markers: any[]) => void
onChange?: (code: string) => void
defaultValue: string
fileId: string
onMount?: (editor: any, monaco: any) => void
isSubgraph: boolean
}

const Editor: React.FC<EditorProps> = ({
Expand All @@ -19,6 +23,7 @@ const Editor: React.FC<EditorProps> = ({
defaultValue,
fileId,
onMount,
isSubgraph,
}) => {
const code = useRef(defaultValue)
const monaco = useMonaco()
Expand Down Expand Up @@ -48,10 +53,18 @@ const Editor: React.FC<EditorProps> = ({
})

var sdkUri = 'ts:filename/sdk.d.ts'
monaco.languages.typescript.javascriptDefaults.addExtraLib(sdkTypeDefs, sdkUri)
monaco.languages.typescript.javascriptDefaults.addExtraLib(
isSubgraph ? graphTypeDefs : sdkTypeDefs,
sdkUri
)

// When resolving definitions and references, the editor will try to use created models.
// Creating a model for the library allows "peek definition/references" commands to work with the library.
monaco.editor.createModel(sdkTypeDefs, 'typescript', monaco.Uri.parse(sdkUri))
monaco.editor.createModel(
isSubgraph ? graphTypeDefs : sdkTypeDefs,
'typescript',
monaco.Uri.parse(sdkUri)
)

return () => monaco.editor.getModels().forEach((model: any) => model.dispose())
}
Expand Down
69 changes: 56 additions & 13 deletions components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { useENSName } from 'use-ens-name'
import Button from 'components/Button'
import CodeEditor from 'components/CodeEditor'
import ConnectionButton from 'components/ConnectionButton'
import FileList from 'components/FileList'
import FileList, { FileType } from 'components/FileList'
import { useAdapter, newModule } from 'hooks/local-adapters'
import { useCompiler } from 'hooks/compiler'
import { useConsole } from 'hooks/console'
Expand All @@ -39,6 +39,8 @@ import Console from './Console'
import BottomTitleBar, { BottomView } from './BottomTitleBar'
import SaveMessage from './SaveMessage'
import ImageLibrary from './ImageLibrary/ImageLibrary'
import { useASCompiler } from 'hooks/useASCompiler'
import { newSubgraph, useLocalSubgraph } from 'hooks/useLocalSubgraph'

const Header = styled(Top)`
background-image: url('/editor_logo.png');
Expand Down Expand Up @@ -226,7 +228,10 @@ const formatLog = (val: any) => {
const Editor: React.FC = () => {
const router = useRouter()
const plausible = usePlausible()
const [fileName, setFileName] = useEditorState<string | null>('open-file', null)
const [openFile, setOpenFile] = useEditorState<{ fileName: string; fileType: FileType } | null>(
'open-file',
null
)
const [started, setStarted] = useState(false)
const [leftCollapsed, setLeftCollapsed] = useEditorState('left-collapsed', false)
const [newAdapterModalOpen, setNewAdapterModalOpen] = useState(false)
Expand All @@ -235,8 +240,12 @@ const Editor: React.FC = () => {
const [imageLibraryOpen, setImageLibraryOpen] = useState(false)
const [bottomView, setBottomView] = useState(BottomView.NONE)
const editorRef = useRef<any>(null)
const { save, adapter } = useAdapter(fileName)
const { save, adapter } = useAdapter(openFile?.fileName)
const { subgraph, saveSchema } = useLocalSubgraph(
openFile?.fileType === FileType.Subgraph ? openFile.fileName : null
)
const { evaluate, module } = useCompiler()
const { evaluate: evaluateAS } = useASCompiler()
const { addLine } = useConsole()
const { account } = useWeb3React()
const name = useENSName(account)
Expand All @@ -251,7 +260,7 @@ const Editor: React.FC = () => {
useEffect(() => {
if (router.query.adapter) {
const { adapter, ...query } = router.query
setFileName(adapter as string)
setOpenFile({ fileName: adapter as string, fileType: FileType.Adapter })
router.replace({ pathname: '/editor', query })
}
}, [router.query])
Expand Down Expand Up @@ -304,7 +313,13 @@ const Editor: React.FC = () => {
</FilterBox>

<Fill scrollable={true}>
<FileList selected={fileName} onSelected={setFileName} filter={filter} />
<FileList
selected={openFile?.fileName}
onSelected={(fileName: string, fileType: FileType) =>
setOpenFile({ fileName, fileType })
}
filter={filter}
/>
</Fill>

<LeftSidebarFooter size={70}>
Expand All @@ -324,25 +339,39 @@ const Editor: React.FC = () => {
<Fill>
<TabContainer size={50}>
<Fill>
<Tabs current={adapter?.name} onClose={() => setFileName(null)} />
<Tabs current={adapter?.name} onClose={() => setOpenFile(null)} />
</Fill>
<Right size={100}>
<EditorControls editorRef={editorRef} />
</Right>
</TabContainer>

<Fill>
{fileName && adapter ? (
{openFile && (openFile.fileType === FileType.Adapter ? adapter : subgraph) ? (
<CodeEditor
fileId={fileName}
defaultValue={adapter.code}
fileId={openFile.fileName}
defaultValue={
openFile.fileType === FileType.Adapter ? adapter!.code : subgraph!.schema
}
isSubgraph={openFile?.fileType === FileType.Subgraph}
onMount={(editor: any) => {
editorRef.current = editor
}}
onChange={(code: string) => save(code, adapter.name, adapter.version)}
onChange={(code: string) => {
if (openFile.fileType === FileType.Adapter) {
save(code, adapter!.name, adapter!.version)
} else {
saveSchema(code)
}
}}
onValidated={(code: string, markers: any[]) => {
setMarkers(markers)

if (openFile.fileType === FileType.Subgraph) {
evaluateAS({ code })
return
}

if (
markers.filter((marker: any) => marker.severity === MarkerSeverity.Error)
.length === 0
Expand Down Expand Up @@ -390,11 +419,12 @@ const Editor: React.FC = () => {

<PrimaryFooterContainer size={55}>
<PrimaryFooter
fileName={fileName}
fileName={openFile?.fileName}
markers={markers}
onMarkerClick={() => setBottomView(BottomView.ERRORS)}
onConsoleClick={() => setBottomView(BottomView.CONSOLE)}
editorRef={editorRef}
isSubgraph={openFile?.fileType === FileType.Subgraph}
/>
</PrimaryFooterContainer>
</Fill>
Expand Down Expand Up @@ -424,15 +454,28 @@ const Editor: React.FC = () => {
},
})

setFileName(newModule(emptyAdapter))
setOpenFile({ fileName: newModule(emptyAdapter), fileType: FileType.Adapter })
setNewAdapterModalOpen(false)
},
},
{
label: 'Create Blank Subgraph',
onClick: () => {
plausible('new-subgraph', {
props: {
template: 'blank',
},
})

setOpenFile({ fileName: newSubgraph(), fileType: FileType.Subgraph })
setNewAdapterModalOpen(false)
},
},
]}
>
<NewAdapterForm
onAdapterSelection={(fileName: string) => {
setFileName(fileName)
setOpenFile({ fileName, fileType: FileType.Adapter })
setNewAdapterModalOpen(false)
}}
/>
Expand Down
32 changes: 21 additions & 11 deletions components/Editor/PrimaryFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useAdapter } from 'hooks/local-adapters'
import PublishModal from './PublishModal'
import { MarkerSeverity } from './types'
import Button from 'components/Button'
import PublishSubgraphModal from './PublishSubgraphModal'

const Container = styled.div`
flex: 1;
Expand Down Expand Up @@ -71,18 +72,20 @@ const ErrorChip = styled.button<{ color?: string }>`
`

interface PrimaryFooterProps {
fileName: string | null
fileName?: string | null
markers: any[]
onMarkerClick: () => void
onConsoleClick: () => void
editorRef: any
isSubgraph: boolean
}

const PrimaryFooter: React.FC<PrimaryFooterProps> = ({
fileName,
markers,
onMarkerClick,
onConsoleClick,
isSubgraph,
editorRef,
}) => {
const [showModal, setShowModal] = useState(false)
Expand Down Expand Up @@ -138,23 +141,30 @@ const PrimaryFooter: React.FC<PrimaryFooterProps> = ({

<PublishButton
onClick={() => setShowModal(true)}
disabled={errors.length > 0}
disabled={isSubgraph && errors.length > 0}
className={'primary'}
>
Publish to IPFS
Publish {isSubgraph ? 'subgraph' : 'to IPFS'}
</PublishButton>
</Fragment>
)}
</Side>

{fileName && (
<PublishModal
fileName={fileName}
show={showModal}
onClose={() => setShowModal(false)}
editorRef={editorRef}
/>
)}
{fileName &&
(isSubgraph ? (
<PublishSubgraphModal
fileName={fileName}
show={showModal}
onClose={() => setShowModal(false)}
/>
) : (
<PublishModal
fileName={fileName}
show={showModal}
onClose={() => setShowModal(false)}
editorRef={editorRef}
/>
))}
</Container>
)
}
Expand Down
110 changes: 110 additions & 0 deletions components/Editor/PublishSubgraphModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React, { useEffect, useState } from 'react'
import styled from 'styled-components'
import EditorModal, { Button as ModalButton } from './EditorModal'
import Text from 'components/Text'
import { compileAs } from 'utils/as-compiler'
import { publishSubgraph } from 'utils/publish-subgraph'
import { DEFAULT_MAPPING, useLocalSubgraph } from 'hooks/useLocalSubgraph'

const ShareUrl = styled.div`
padding: 16px;
max-width: 100%;
overflow: hidden;
background-color: #222222;
border: 1px solid #444444;
border-radius: 4px;
`

interface PublishModalProps {
fileName: string
show: boolean
onClose: () => void
}

enum STATE {
COMPILING,
INIT,
SIGN,
SIGNED_PUBLISH_PENDING,
PUBLISHING,
PUBLISHED,
}

const PublishSubgraphModal: React.FC<PublishModalProps> = ({ fileName, onClose, show }) => {
const [state, setState] = useState(STATE.INIT)
const [cid, setCID] = useState<null | string>(null)
const { subgraph, generateManifest } = useLocalSubgraph(fileName)

const close = () => {
onClose()
setState(STATE.INIT)
setCID(null)
}

useEffect(() => {
if (show && subgraph?.schema) {
setState(STATE.COMPILING)
generateManifest().then(manifest => console.log(manifest))

if (subgraph.mappings[DEFAULT_MAPPING]) {
compileAs(subgraph.mappings[DEFAULT_MAPPING]).then(output => {
console.log({ output })
setState(STATE.SIGNED_PUBLISH_PENDING)
})
}
}
}, [subgraph?.schema, show])

const returnButton = { label: 'Return to Editor', onClick: close }

let title = 'Publish Your Adapter on IPFS'
let buttons: ModalButton[] = []
let content = null

if (show) {
switch (state) {
case STATE.COMPILING:
title = 'Compiling'
buttons = [returnButton]
content = <div>Compiling</div>
break

case STATE.SIGNED_PUBLISH_PENDING:
const publishButton = {
label: 'Publish',
onClick: async () => {
setState(STATE.PUBLISHING)
await publishSubgraph()
setState(STATE.PUBLISHED)
},
}
title = 'Publish pending'
buttons = [returnButton, publishButton]
break

case STATE.PUBLISHED:
title = '🎉 Adapter Successfully Published!'
buttons = [returnButton]
content = (
<div>
<Text tag="p" color="white" type="description">
Your adapter has been published to IPFS! You may now share the following link:
</Text>
<Text tag="p" type="label" mt="24" mb="16">
Adapter url
</Text>
<ShareUrl>https://cryptostats.community/discover/adapter/{cid}</ShareUrl>
</div>
)
break
}
}

return (
<EditorModal isOpen={show} onClose={close} title={title} buttons={buttons}>
{content}
</EditorModal>
)
}

export default PublishSubgraphModal
Loading