forked from storacha/w3up
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: delegate access to spaces (storacha#293)
Allow user to delegate access to a space. Adds createDelegation to keyring-core and chases it through {react,solid}-keyring https://user-images.githubusercontent.com/58871/214885759-2bcf7167-7001-4ffd-b5f4-a0d4b51a9355.mov To keep this PR reviewable, importing delegations will be added in a subsequent PR. License: MIT Signed-off-by: Oli Evans <oli@protocol.ai>
- Loading branch information
Showing
8 changed files
with
204 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { ChangeEvent, useState } from 'react' | ||
import { useKeyring } from '@w3ui/react-keyring' | ||
import * as DID from '@ipld/dag-ucan/did' | ||
import { CarWriter } from '@ipld/car/writer' | ||
import type { PropsWithChildren } from 'react' | ||
import type { Delegation } from '@ucanto/interface' | ||
|
||
function Header (props: PropsWithChildren): JSX.Element { | ||
return <h3 className='font-semibold text-xs font-mono uppercase tracking-wider mb-4 text-gray-400'>{props.children}</h3> | ||
} | ||
|
||
export async function toCarBlob (delegation: Delegation): Promise<Blob> { | ||
const { writer, out } = CarWriter.create() | ||
for (const block of delegation.export()) { | ||
// @ts-expect-error | ||
void writer.put(block) | ||
} | ||
void writer.close() | ||
|
||
const carParts = [] | ||
for await (const chunk of out) { | ||
carParts.push(chunk) | ||
} | ||
const car = new Blob(carParts, { | ||
type: 'application/vnd.ipld.car' | ||
}) | ||
return car | ||
} | ||
|
||
export function SpaceShare (): JSX.Element { | ||
const [, { createDelegation }] = useKeyring() | ||
const [value, setValue] = useState('') | ||
const [downloadUrl, setDownloadUrl] = useState('') | ||
|
||
async function makeDownloadLink (input: string): Promise<void> { | ||
let audience | ||
try { | ||
audience = DID.parse(input.trim()) | ||
} catch (err) { | ||
setDownloadUrl('') | ||
return | ||
} | ||
|
||
try { | ||
const delegation = await createDelegation(audience, ['*'], { expiration: Infinity }) | ||
const blob = await toCarBlob(delegation) | ||
const url = URL.createObjectURL(blob) | ||
setDownloadUrl(url) | ||
} catch (err) { | ||
throw new Error('failed to register', { cause: err }) | ||
} | ||
} | ||
|
||
function onSubmit (e: React.FormEvent<HTMLFormElement>): void { | ||
e.preventDefault() | ||
void makeDownloadLink(value) | ||
} | ||
|
||
function onChange (e: ChangeEvent<HTMLInputElement>): void { | ||
const input = e.target.value | ||
void makeDownloadLink(input) | ||
setValue(input) | ||
} | ||
|
||
function downloadName (ready: boolean, inputDid: string): string { | ||
if (!ready || inputDid === '') return '' | ||
const [, method = '', id = ''] = inputDid.split(':') | ||
return `did-${method}-${id?.substring(0, 10)}.ucan` | ||
} | ||
|
||
return ( | ||
<div className='pt-12'> | ||
<div className=''> | ||
<Header>Share your space</Header> | ||
<p className='mb-4'>Ask your friend for their Decentralized Identifier (DID) and paste it in below</p> | ||
<form onSubmit={(e: React.FormEvent<HTMLFormElement>) => { void onSubmit(e) }}> | ||
<input | ||
className='text-black px-2 py-2 rounded block mb-4 w-full max-w-4xl font-mono text-sm' | ||
type='pattern' pattern='did:.+' placeholder='did:' | ||
value={value} | ||
onChange={onChange} | ||
/> | ||
<a className='w3ui-button text-center block w-52 opacity-30' style={{ opacity: downloadUrl !== '' ? '1' : '0.2' }} href={downloadUrl ?? ''} download={downloadName(downloadUrl !== '', value)}>Download UCAN</a> | ||
</form> | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.