Skip to content

Commit

Permalink
Update metadata, the proper way (#292)
Browse files Browse the repository at this point in the history
* prototype view switching

* refactor, more UI

* formik form setup & data flow

* debug output, fixes, refactor

* description preview refactor

* publish/update date changes

* output created & updated date at top of asset
* use ddo.created & ddo.updated everywhere
* stop pushing metadata.main.datePublished

* owner check for edit link

* all the feedback states and switching between them: loading, error, success

* refactor feedback, one component for publish & edit

* action & date output fixes

* move all content, iterate form fields from it

* UI updates

* styling tweaks

* ddo dataflow refactor, more useAsset usage

* more useAsset usage

* form actions styling

* prepare edit history component

* metadata output tweaks

* copy

* safeguard against profile urls without protocol defined

* refetch ddo after edit

Signed-off-by: mihaisc <mihai.scarlat@smartcontrol.ro>

* switch author for dataTokenOptions in metadata preview

* refactor

* copy

* showPricing fix

* validation: minimum characters for title & description

* disable submit button when validation fails

* form validation fixes

* manually trigger onChange validation in publish & edit forms

Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro>
  • Loading branch information
kremalicious and mihaisc authored Dec 10, 2020
1 parent c57731c commit 960c5b3
Show file tree
Hide file tree
Showing 46 changed files with 950 additions and 438 deletions.
25 changes: 25 additions & 0 deletions content/pages/edit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"description": "Update selected metadata of this data set. Updating metadata will create an on-chain transaction you have to approve in your wallet.",
"form": {
"success": "🎉 Successfully updated. 🎉",
"successAction": "Close",
"error": "Updating DDO failed.",
"data": [
{
"name": "name",
"label": "New Title",
"placeholder": "e.g. Shapes of Desert Plants",
"help": "Enter a concise title.",
"required": true
},
{
"name": "description",
"label": "New Description",
"help": "Add a thorough description with as much detail as possible. You can use [Markdown](https://daringfireball.net/projects/markdown/basics).",
"type": "textarea",
"rows": 10,
"required": true
}
]
}
}
18 changes: 18 additions & 0 deletions src/components/atoms/DebugOutput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { ReactElement } from 'react'

export default function DebugOutput({
title,
output
}: {
title: string
output: any
}): ReactElement {
return (
<div>
<h5>{title}</h5>
<pre>
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
</div>
)
}
12 changes: 2 additions & 10 deletions src/components/atoms/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,7 @@ export interface InputProps {
}

export default function Input(props: Partial<InputProps>): ReactElement {
const {
required,
name,
label,
help,
additionalComponent,
size,
field
} = props
const { label, help, additionalComponent, size, field } = props

const hasError =
props.form?.touched[field.name] && props.form?.errors[field.name]
Expand All @@ -67,7 +59,7 @@ export default function Input(props: Partial<InputProps>): ReactElement {
className={styleClasses}
data-is-submitting={props.form?.isSubmitting ? true : null}
>
<Label htmlFor={name} required={required}>
<Label htmlFor={props.name} required={props.required}>
{label}
</Label>
<InputElement size={size} {...field} {...props} />
Expand Down
8 changes: 8 additions & 0 deletions src/components/atoms/Markdown.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@
word-break: break-word;
}

.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5 {
margin-bottom: calc(var(--spacer) / 2);
}

.markdown h1 {
font-size: var(--font-size-h3);
}
Expand Down
4 changes: 3 additions & 1 deletion src/components/atoms/Publisher/PublisherLinks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ export default function PublisherLinks({
? `https://twitter.com/${link.value}`
: link.name === 'GitHub'
? `https://github.com/${link.value}`
: link.value
: link.value.includes('http') // safeguard against urls without protocol defined
? link.value
: `//${link.value}`

return (
<a href={href} key={link.name} target="_blank" rel="noreferrer">
Expand Down
9 changes: 5 additions & 4 deletions src/components/atoms/Time.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { ReactElement, useEffect, useState } from 'react'
import { format, formatDistance } from 'date-fns'
import { setDate } from 'date-fns/esm'

export default function Time({
date,
Expand All @@ -15,24 +14,26 @@ export default function Time({
}): ReactElement {
const [dateIso, setDateIso] = useState<string>()
const [dateNew, setDateNew] = useState<Date>()

useEffect(() => {
if (!date) return

const dateNew = isUnix ? new Date(Number(date) * 1000) : new Date(date)
setDateIso(dateNew.toISOString())
setDateNew(dateNew)
}, [date])
}, [date, isUnix])

return !dateIso || !dateNew ? (
<></>
) : (
<time
title={relative ? format(dateNew, 'MMMM d, yyyy') : undefined}
title={format(dateNew, 'PPppp')}
dateTime={dateIso}
className={className || undefined}
>
{relative
? formatDistance(dateNew, Date.now(), { addSuffix: true })
: format(dateNew, 'MMMM d, yyyy')}
: format(dateNew, 'PP')}
</time>
)
}
28 changes: 19 additions & 9 deletions src/components/molecules/FormFields/FilesInput/Info.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import React, { ReactElement } from 'react'
import React, { ReactElement, useEffect } from 'react'
import { File as FileMetadata } from '@oceanprotocol/lib/dist/node/ddo/interfaces/File'
import { prettySize } from '../../../../utils'
import cleanupContentType from '../../../../utils/cleanupContentType'
import styles from './Info.module.css'
import { useField, useFormikContext } from 'formik'

export default function FileInfo({
file,
removeItem
name,
file
}: {
name: string
file: FileMetadata
removeItem?(): void
}): ReactElement {
const { validateField } = useFormikContext()
const [field, meta, helpers] = useField(name)

// On mount, validate the field manually
useEffect(() => {
validateField(name)
}, [name, validateField])

return (
<div className={styles.info}>
<h3 className={styles.url}>{file.url}</h3>
Expand All @@ -19,11 +28,12 @@ export default function FileInfo({
{file.contentLength && <li>{prettySize(+file.contentLength)}</li>}
{file.contentType && <li>{cleanupContentType(file.contentType)}</li>}
</ul>
{removeItem && (
<button className={styles.removeButton} onClick={() => removeItem()}>
&times;
</button>
)}
<button
className={styles.removeButton}
onClick={() => helpers.setValue(undefined)}
>
&times;
</button>
</div>
)
}
10 changes: 5 additions & 5 deletions src/components/molecules/FormFields/FilesInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export default function FilesInput(props: InputProps): ReactElement {
const [isLoading, setIsLoading] = useState(false)

async function handleButtonClick(e: React.SyntheticEvent, url: string) {
// hack so the onBlur-triggered validation does not show,
// like when this field is required
helpers.setTouched(false)

// File example 'https://oceanprotocol.com/tech-whitepaper.pdf'
e.preventDefault()

Expand All @@ -26,14 +30,10 @@ export default function FilesInput(props: InputProps): ReactElement {
}
}

function removeItem() {
helpers.setValue(undefined)
}

return (
<>
{field?.value && field.value[0] && typeof field.value === 'object' ? (
<FileInfo file={field.value[0]} removeItem={removeItem} />
<FileInfo name={props.name} file={field.value[0]} />
) : (
<FileInput
{...props}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
}

.box {
composes: box from '../../atoms/Box.module.css';
composes: box from '../atoms/Box.module.css';
width: 100%;
}

Expand Down
78 changes: 78 additions & 0 deletions src/components/molecules/MetadataFeedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import Alert from '../atoms/Alert'
import Button from '../atoms/Button'
import Loader from '../atoms/Loader'
import React, { ReactElement } from 'react'
import styles from './MetadataFeedback.module.css'
import SuccessConfetti from '../atoms/SuccessConfetti'

interface Action {
name: string
onClick?: () => void
to?: string
}

function ActionSuccess({ action }: { action: Action }) {
const { name, onClick, to } = action

return (
<Button
style="primary"
size="small"
onClick={onClick || null}
to={to || null}
className={styles.action}
>
{name}
</Button>
)
}

function ActionError({ setError }: { setError: (error: string) => void }) {
return (
<Button
style="primary"
size="small"
className={styles.action}
onClick={() => setError(undefined)}
>
Try Again
</Button>
)
}

export default function MetadataFeedback({
title,
error,
success,
loading,
successAction,
setError
}: {
title: string
error: string
success: string
loading?: string
successAction: Action
setError: (error: string) => void
}): ReactElement {
return (
<div className={styles.feedback}>
<div className={styles.box}>
<h3>{title}</h3>
{error ? (
<>
<Alert text={error} state="error" />
<ActionError setError={setError} />
</>
) : success ? (
<SuccessConfetti
success={success}
action={<ActionSuccess action={successAction} />}
/>
) : (
<Loader message={loading} />
)}
</div>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@
margin: 0;
}

.author {
.datatoken {
margin-top: calc(var(--spacer) / 8);
margin-bottom: 0;
color: var(--color-secondary);
font-weight: var(--font-weight-bold);
}

.preview [class*='MetaItem-module--metaItem'] h3 {
Expand Down
Loading

1 comment on commit 960c5b3

@vercel
Copy link

@vercel vercel bot commented on 960c5b3 Dec 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.