IDE redesign, initial implementation #362

Merged
Irev-Dev merged 17 commits from kurt/update-ide-panel-toolbar-360 into main 2021-06-15 10:05:03 +02:00
4 changed files with 150 additions and 11 deletions
Showing only changes of commit 5083d8e7f8 - Show all commits

View File

@@ -0,0 +1,131 @@
import { useState } from 'react'
import { useIdeContext } from 'src/helpers/hooks/useIdeContext'
import OutBound from 'src/components/OutBound/OutBound'
import { prepareEncodedUrl, makeExternalUrl } from './helpers'
import { copyTextToClipboard } from 'src/helpers/clipboard'
import { useRender } from 'src/components/IdeWrapper/useRender'
import { toast } from '@redwoodjs/web/toast'
const ideTypeNameMap = {
Irev-Dev commented 2021-06-14 02:26:20 +02:00 (Migrated from github.com)
Review

image
and

image

![image](https://user-images.githubusercontent.com/29681384/121826427-e7eea880-ccfa-11eb-9e5b-c82fd9605d87.png) and ![image](https://user-images.githubusercontent.com/29681384/121826437-f2a93d80-ccfa-11eb-9b3d-b5fa0e0968d3.png)
openScad: 'OpenSCAD',
cadQuery: 'CadQuery',
}
const ExternalScript = () => {
const { state, thunkDispatch } = useIdeContext()
const handleRender = useRender()
const [rawUrl, setRawUrl] = useState('')
const [script, setScript] = useState('')
const [asyncState, setAsyncState] =
useState<'INIT' | 'SUCCESS' | 'ERROR' | 'LOADING'>('INIT')
const cadName = ideTypeNameMap[state.ideType]
const onPaste: React.ClipboardEventHandler<HTMLInputElement> = async ({
clipboardData,
}) => {
const url = clipboardData.getData('Text')
processUserUrl(url)
}
Irev-Dev commented 2021-06-14 02:28:06 +02:00 (Migrated from github.com)
Review

When a user pastes a url it will immediately start processing it without having a "submit" button. I think this is appropriate since the whole point is to link to an external URL and no one will be typing those, you can type and hit enter though.

When a user pastes a url it will immediately start processing it without having a "submit" button. I think this is appropriate since the whole point is to link to an external URL and no one will be typing those, you can type and hit enter though.
const onChange: React.ChangeEventHandler<HTMLInputElement> = async ({
target,
}) => setRawUrl(target.value)
const onKeyDown = async ({ key, target }) =>
key === 'Enter' && processUserUrl(target.value)
async function processUserUrl(url: string) {
setRawUrl(url)
try {
setAsyncState('LOADING')
const response = await fetch(prepareEncodedUrl(url))
if (response.status === 404) throw new Error("couldn't find script")
const script2 = await response.text()
if (script2.startsWith('<!DOCTYPE html>'))
throw new Error('got html document, not a script')
setScript(script2)
setAsyncState('SUCCESS')
} catch (e) {
setAsyncState('ERROR')
toast.error(
"We had trouble with you're URL, are you sure it was correct?"
)
}
}
const onCopyRender: React.MouseEventHandler<HTMLButtonElement> = () => {
copyTextToClipboard(makeExternalUrl(rawUrl))
thunkDispatch({ type: 'updateCode', payload: script })
setTimeout(handleRender)
}
return (
<div className="p-4">
<p className="text-sm pb-4">
Paste an external url containing a {cadName} script to generate a new
CadHub url for this resource.{' '}
<OutBound
className="underline text-gray-500"
to="TODO learn.cadhub link"
>
Learn more
</OutBound>{' '}
about this feature.
</p>
{['INIT', 'ERROR'].includes(asyncState) && (
<>
<p>Paste url</p>
<input
className="p-1 text-xs rounded border border-gray-700 w-full"
value={rawUrl}
onChange={onChange}
onPaste={onPaste}
onKeyDown={onKeyDown}
/>
</>
)}
{asyncState === 'ERROR' && (
<p className="text-sm text-red-800">That didn't work, try again.</p>
)}
{asyncState === 'LOADING' && (
<div className="h-10 relative">
<div className="inset-0 absolute flex items-center justify-center">
<div className="h-6 w-6 bg-pink-600 rounded-full animate-ping"></div>
</div>
</div>
)}
{asyncState === 'SUCCESS' && (
<>
<input
value={makeExternalUrl(rawUrl).replace(/^.+:\/\//g, '')}
readOnly
className="p-1 mt-4 text-xs rounded-t border border-gray-700 w-full"
/>
<button
className="w-full bg-gray-700 py-1 rounded-b text-gray-300"
onClick={() => copyTextToClipboard(makeExternalUrl(rawUrl))}
>
Copy URL
</button>
<div className="flex flex-col gap-2 pt-2">
<button
className="bg-gray-500 p-1 px-2 rounded text-gray-300"
onClick={onCopyRender}
>
Copy &amp; Render
</button>
<button
className="bg-gray-500 p-1 px-2 rounded text-gray-300"
onClick={() => {
setAsyncState('INIT')
setRawUrl('')
setScript('')
}}
>
Create another URL
</button>
</div>
</>
)}
</div>
)
}
export default ExternalScript

View File

@@ -6,7 +6,7 @@ const FullScriptEncoding = () => {
const { state } = useIdeContext()
const encodedLink = makeEncodedLink(state.code)
return (
<>
<div className="p-4">
<p className="text-sm pb-4 border-b border-gray-700">
Encodes your CodeCad script into a URL so that you can share your work
</p>
@@ -21,7 +21,7 @@ const FullScriptEncoding = () => {
>
Copy URL
</button>
</>
</div>
)
}

View File

@@ -10,11 +10,6 @@ const scriptKey = 'encoded_script'
const scriptKeyV2 = 'encoded_script_v2'
const fetchText = 'fetch_text_v1'
export function makeEncodedLink(code: string): string {
const encodedScript = encode(code)
return `${location.origin}${location.pathname}#${scriptKeyV2}=${encodedScript}`
}
export const githubSafe = (url: string): string =>
url.includes('github.com')
? url
@@ -22,7 +17,20 @@ export const githubSafe = (url: string): string =>
.replace('/blob/', '/')
: url
const prepareEncodedUrl = flow(decodeURIComponent, githubSafe)
export const prepareEncodedUrl = flow(decodeURIComponent, githubSafe)
const prepareDecodedUrl = flow(githubSafe, encodeURIComponent)
export function makeEncodedLink(code: string): string {
const encodedScript = encode(code)
return `${location.origin}${location.pathname}#${scriptKeyV2}=${encodedScript}`
}
export function makeExternalUrl(resourceUrl: string): string {
return `${location.origin}${
location.pathname
}#${fetchText}=${prepareDecodedUrl(resourceUrl)}`
}
export function useIdeInit(cadPackage: string) {
Irev-Dev commented 2021-06-14 02:32:54 +02:00 (Migrated from github.com)
Review

All of the logic in this hook I had in the IdeToolbarNew because that was the main wrapping component that had the context provider, so it made sense to have init useEffects there. However since most of the logic relates to checking if there is something encoded in the URL, I thought it made sense to bundle the logic here with the rest of the encoded url code, and bring it into IdeToolbarNew as a custom hook.

I've also now rename IdeToolbarNew to IdeWrapper

All of the logic in this hook I had in the `IdeToolbarNew` because that was the main wrapping component that had the context provider, so it made sense to have init useEffects there. However since most of the logic relates to checking if there is something encoded in the URL, I thought it made sense to bundle the logic here with the rest of the encoded url code, and bring it into `IdeToolbarNew` as a custom hook. I've also now rename `IdeToolbarNew` to `IdeWrapper`
const { thunkDispatch } = useIdeContext()

View File

@@ -1,6 +1,7 @@
import { Popover } from '@headlessui/react'
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'
import FullScriptEncoding from 'src/components/EncodedUrl/FullScriptEncoding'
import ExternalScript from 'src/components/EncodedUrl/ExternalScript'
const TopButton = ({
onClick,
@@ -50,12 +51,11 @@ const IdeHeader = ({ handleRender }: { handleRender: () => void }) => {
className="bg-gray-300 rounded-md shadow-md overflow-hidden text-gray-700"
selectedTabClassName="bg-gray-200"
>
<TabPanel className="p-4">
<TabPanel>
<FullScriptEncoding />
</TabPanel>
<TabPanel>
<p>blah</p>
<input onPaste={(e) => console.log(e)} />
<ExternalScript />
</TabPanel>
<TabList className="flex whitespace-nowrap text-gray-700 border-t border-gray-700">