Skip to content

Commit

Permalink
Merge pull request #80 from FuelLabs/sophie/sharable-link
Browse files Browse the repository at this point in the history
feat: Sharable permanent links using gists
  • Loading branch information
sdankel authored May 10, 2024
2 parents b9a6e0c + c174564 commit f377262
Show file tree
Hide file tree
Showing 21 changed files with 1,512 additions and 170 deletions.
1,052 changes: 975 additions & 77 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ tokio = { version = "1", features = ["full"] }
serde_json = "1.0.91"
regex = "1.7.0"
rocket = { version = "0.5.0-rc.2", features = ["tls", "json"] }
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0", features = ["derive"] }
octocrab = "0.38.0"
thiserror = "1.0.60"
61 changes: 61 additions & 0 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"react": "^18.2.0",
"react-ace": "^10.1.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.0",
"react-scripts": "^5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
Expand Down
65 changes: 62 additions & 3 deletions app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,25 @@ import {
} from './utils/localStorage';
import InteractionDrawer from './features/interact/components/InteractionDrawer';
import { useLog } from './features/editor/hooks/useLog';
import { Toolchain } from './features/editor/components/ToolchainDropdown';
import {
Toolchain,
isToolchain,
} from './features/editor/components/ToolchainDropdown';
import { useTranspile } from './features/editor/hooks/useTranspile';
import EditorView from './features/editor/components/EditorView';
import { Analytics, track } from '@vercel/analytics/react';
import { useGist } from './features/editor/hooks/useGist';
import { useSearchParams } from 'react-router-dom';
import Copyable from './components/Copyable';

const DRAWER_WIDTH = '40vw';

function App() {
// The current sway code in the editor.
const [swayCode, setSwayCode] = useState(loadSwayCode());
const [swayCode, setSwayCode] = useState<string>(loadSwayCode());

// The current solidity code in the editor.
const [solidityCode, setSolidityCode] = useState(loadSolidityCode());
const [solidityCode, setSolidityCode] = useState<string>(loadSolidityCode());

// An error message to display to the user.
const [showSolidity, setShowSolidity] = useState(false);
Expand Down Expand Up @@ -56,12 +62,28 @@ function App() {
// An error message to display to the user.
const [drawerOpen, setDrawerOpen] = useState(false);

// The query parameters for the current URL.
const [searchParams] = useSearchParams();

// If showSolidity is toggled on, reset the compiled state.
useEffect(() => {
if (showSolidity) {
setIsCompiled(false);
}
}, [showSolidity]);

// Load the query parameters from the URL and set the state accordingly. Gists are loaded in useGist.
useEffect(() => {
if (searchParams.get('transpile') === 'true') {
setShowSolidity(true);
}
let toolchainParam = searchParams.get('toolchain');

if (isToolchain(toolchainParam)) {
setToolchain(toolchainParam);
}
}, [searchParams, setShowSolidity, setToolchain]);

const onSwayCodeChange = useCallback(
(code: string) => {
saveSwayCode(code);
Expand All @@ -80,13 +102,49 @@ function App() {
[setSolidityCode]
);

// Loading shared code by query parameter and get a function for creating sharable permalinks.
const { newGist } = useGist(onSwayCodeChange, onSolidityCodeChange);

const setError = useCallback(
(error: string | undefined) => {
updateLog(error);
},
[updateLog]
);

const onShareClick = useCallback(async () => {
track('Share Click', { toolchain });
const response = await newGist(swayCode, {
contract: solidityCode,
language: 'solidity',
});
if (!!response) {
const permalink = `${window.location.origin}/?toolchain=${toolchain}&transpile=${showSolidity}&gist=${response.id}`;
console.log('permalink: ', permalink);
updateLog([
<Copyable
href
value={permalink}
tooltip='permalink to your code'
label='Permalink to Playground'
/>,
<Copyable
href
value={response?.url}
tooltip='link to gist'
label='Link to Gist'
/>,
]);
}
}, [
newGist,
swayCode,
solidityCode,
updateLog,
toolchain,
showSolidity,
]);

const onCompileClick = useCallback(() => {
track('Compile Click', { toolchain });
if (showSolidity) {
Expand Down Expand Up @@ -124,6 +182,7 @@ function App() {
<ActionToolbar
deployState={deployState}
setContractId={setContractId}
onShareClick={onShareClick}
onCompile={onCompileClick}
isCompiled={isCompiled}
setDeployState={setDeployState}
Expand Down
5 changes: 3 additions & 2 deletions app/src/components/Copyable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@ export interface CopyableProps {
value: string;
label: string;
tooltip: string;
href?: boolean;
}

async function handleCopy(value: string) {
await navigator.clipboard.writeText(value);
}

function Copyable({ value, label, tooltip }: CopyableProps) {
function Copyable({ value, label, tooltip, href }: CopyableProps) {
return (
<div
style={{ cursor: 'pointer', color: darkColors.gray6 }}
onClick={() => handleCopy(value)}>
<Tooltip title={`Click to copy ${tooltip}`}>
<span>
<span style={{ padding: '8px 0 8px' }}>{label}</span>
{href ? (<a href={value} target='_blank' rel='noreferrer'>{label}</a>) : (<span style={{ padding: '8px 0 8px' }}>{label}</span>)}
<IconButton disableRipple aria-label='copy'>
<ContentCopyIcon style={{ fontSize: '14px' }} />
</IconButton>
Expand Down
1 change: 1 addition & 0 deletions app/src/components/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { queryClient } from '../utils/queryClient';
import { FuelProvider } from '@fuels/react';
import { defaultConnectors } from '@fuels/connectors';


type ProvidersProps = {
children: ReactNode;
};
Expand Down
2 changes: 1 addition & 1 deletion app/src/components/SecondaryButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function SecondaryButton({
if (!!header) {
style = {
...style,
minWidth: '115px',
minWidth: '105px',
height: '40px',
marginRight: '15px',
marginBottom: '10px',
Expand Down
5 changes: 5 additions & 0 deletions app/src/features/editor/components/ToolchainDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const ToolchainNames = [
] as const;
export type Toolchain = (typeof ToolchainNames)[number];

export function isToolchain(value: string | null): value is Toolchain {
const found = ToolchainNames.find(name => name === value);
return !!value && found !== undefined;
}

export interface ToolchainDropdownProps {
toolchain: Toolchain;
setToolchain: (toolchain: Toolchain) => void;
Expand Down
2 changes: 1 addition & 1 deletion app/src/features/editor/examples/examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe(`test examples`, () => {
method: 'POST',
body: JSON.stringify({
contract: code,
lanaguage: 'solidity',
language: 'solidity',
}),
});

Expand Down
Loading

0 comments on commit f377262

Please sign in to comment.