feat: add identity and feed management (#272)
* feat(wip): add basic feed operations * ci: bump checks * ci: bump checks * feat: rework stamps and add feed functionalities * refactor: polish and fixes * feat(wip): add formulas * feat: show bzz.link for websites * feat: add stamp empty states and formatBzz * feat: add feed download * chore: update manifest-js version * feat: dev mode support with bee-js 3.1.0 (#273) * feat: dev mode support with bee-js 3.1.0 * fix: added missing package-lock.json file * build: remove PR preview * style: work on design * feat: add TroubleshootConnectionCard * build: remove depcheck Co-authored-by: Attila Gazso <agazso@gmail.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core'
|
||||
import { Close } from '@material-ui/icons'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
interface Props {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
wrapper: {
|
||||
padding: theme.spacing(1),
|
||||
cursor: 'pointer',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export function CloseButton({ onClose }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper} onClick={onClose}>
|
||||
<Close />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
interface Props {
|
||||
children: string
|
||||
prettify?: boolean
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
wrapper: {
|
||||
overflow: 'scroll',
|
||||
background: '#ffffff',
|
||||
},
|
||||
pre: {
|
||||
maxHeight: '6em',
|
||||
padding: theme.spacing(2),
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
function prettifyString(string: string): string {
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(string), null, 4)
|
||||
} catch {
|
||||
return string
|
||||
}
|
||||
}
|
||||
|
||||
export function Code({ children, prettify }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<pre className={classes.pre}>{prettify ? prettifyString(children) : children}</pre>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { createStyles, makeStyles, Typography } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
interface Props {
|
||||
children: (string | ReactElement)[] | (string | ReactElement)
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
text: {
|
||||
color: '#606060',
|
||||
fontSize: '0.9rem',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export function DocumentationText({ children }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
return <Typography className={classes.text}>{children}</Typography>
|
||||
}
|
||||
@@ -4,8 +4,12 @@ import { ReactElement, ReactNode } from 'react'
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
wrapper: {
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
},
|
||||
action: {
|
||||
marginTop: theme.spacing(0.75),
|
||||
marginBottom: theme.spacing(1),
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
@@ -21,16 +25,16 @@ export default function ExpandableListItemActions({ children }: Props): ReactEle
|
||||
|
||||
if (Array.isArray(children)) {
|
||||
return (
|
||||
<Grid container direction="row">
|
||||
<div className={classes.wrapper}>
|
||||
{children
|
||||
// Exclude falsy values to allow conditional rendering
|
||||
.filter(x => x)
|
||||
.map((a, i) => (
|
||||
<Grid key={i} className={classes.action}>
|
||||
<div key={i} className={classes.action}>
|
||||
{a}
|
||||
</Grid>
|
||||
</div>
|
||||
))}
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Button, Grid, IconButton, InputBase, ListItem, Typography } from '@material-ui/core'
|
||||
import { Grid, IconButton, InputBase, ListItem, Typography } from '@material-ui/core'
|
||||
import Collapse from '@material-ui/core/Collapse'
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
|
||||
import { ChangeEvent, ReactElement, useState } from 'react'
|
||||
import { Check, Edit, Minus, RotateCcw } from 'react-feather'
|
||||
import { Edit, Minus, Search, X } from 'react-feather'
|
||||
import ExpandableListItemActions from './ExpandableListItemActions'
|
||||
import ExpandableListItemNote from './ExpandableListItemNote'
|
||||
import { SwarmButton } from './SwarmButton'
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -52,6 +53,7 @@ interface Props {
|
||||
expandedOnly?: boolean
|
||||
confirmLabel?: string
|
||||
confirmLabelDisabled?: boolean
|
||||
loading?: boolean
|
||||
onChange?: (value: string) => void
|
||||
onConfirm: (value: string) => void
|
||||
mapperFn?: (value: string) => string
|
||||
@@ -68,6 +70,7 @@ export default function ExpandableListItemKey({
|
||||
expandedOnly,
|
||||
helperText,
|
||||
placeholder,
|
||||
loading,
|
||||
mapperFn,
|
||||
locked,
|
||||
}: Props): ReactElement | null {
|
||||
@@ -126,26 +129,27 @@ export default function ExpandableListItemKey({
|
||||
<Collapse in={open} timeout="auto" unmountOnExit>
|
||||
{helperText && <ExpandableListItemNote>{helperText}</ExpandableListItemNote>}
|
||||
<ExpandableListItemActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
<SwarmButton
|
||||
disabled={
|
||||
loading ||
|
||||
inputValue === value ||
|
||||
Boolean(confirmLabelDisabled) || // Disable if external validation is provided
|
||||
(inputValue === '' && value === undefined) // Disable if no initial value was not provided and the field is empty. The undefined check is improtant so that it is possible to submit with empty input in other cases
|
||||
}
|
||||
startIcon={<Check size="1rem" />}
|
||||
loading={loading}
|
||||
iconType={Search}
|
||||
onClick={() => onConfirm(inputValue)}
|
||||
>
|
||||
{confirmLabel || 'Save'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={inputValue === value || inputValue === ''}
|
||||
startIcon={<RotateCcw size="1rem" />}
|
||||
</SwarmButton>
|
||||
<SwarmButton
|
||||
disabled={loading || inputValue === value || inputValue === ''}
|
||||
iconType={X}
|
||||
onClick={() => setInputValue(value || '')}
|
||||
cancel
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</SwarmButton>
|
||||
</ExpandableListItemActions>
|
||||
</Collapse>
|
||||
</>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
interface Props {
|
||||
steps: string[]
|
||||
index: number
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
wrapper: {
|
||||
height: '52px',
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
todo: {
|
||||
background: '#f7f7f7',
|
||||
color: '#c9c9c9',
|
||||
},
|
||||
inProgress: {
|
||||
background: '#ffffff',
|
||||
color: '#242424',
|
||||
height: '52px',
|
||||
},
|
||||
done: {
|
||||
background: '#f7f7f7',
|
||||
color: '#606060',
|
||||
height: '52px',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export function ProgressIndicator({ steps, index }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
function pickClass(i: number): string {
|
||||
if (i === index) {
|
||||
return classes.inProgress
|
||||
}
|
||||
|
||||
return i < index ? classes.done : classes.todo
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container justifyContent="space-between">
|
||||
{steps.map((x, i) => (
|
||||
<div key={i} className={`${classes.wrapper} ${pickClass(i)}`}>
|
||||
<Typography>{x}</Typography>
|
||||
</div>
|
||||
))}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { Divider, Drawer, Grid, Link as MUILink, List } from '@material-ui/core'
|
||||
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
|
||||
import { OpenInNewSharp } from '@material-ui/icons'
|
||||
import type { ReactElement } from 'react'
|
||||
import { BookOpen, DollarSign, FileText, Home, Layers, Settings } from 'react-feather'
|
||||
import { Bookmark, BookOpen, DollarSign, FileText, Home, Layers, Settings } from 'react-feather'
|
||||
import { Link } from 'react-router-dom'
|
||||
import Logo from '../assets/logo.svg'
|
||||
import { config } from '../config'
|
||||
@@ -21,6 +21,11 @@ const navBarItems = [
|
||||
path: ROUTES.UPLOAD,
|
||||
icon: FileText,
|
||||
},
|
||||
{
|
||||
label: 'Feeds',
|
||||
path: ROUTES.FEEDS,
|
||||
icon: Bookmark,
|
||||
},
|
||||
{
|
||||
label: 'Stamps',
|
||||
path: ROUTES.STAMPS,
|
||||
|
||||
@@ -9,13 +9,16 @@ interface Props {
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
loading?: boolean
|
||||
cancel?: boolean
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
button: {
|
||||
height: '52px',
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
color: '#242424',
|
||||
'&:hover, &:focus': {
|
||||
'& svg': {
|
||||
stroke: '#fff',
|
||||
@@ -23,6 +26,10 @@ const useStyles = makeStyles(() =>
|
||||
},
|
||||
},
|
||||
},
|
||||
cancelButton: {
|
||||
background: '#f7f7f7',
|
||||
color: '#606060',
|
||||
},
|
||||
spinnerWrapper: {
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
@@ -34,19 +41,37 @@ const useStyles = makeStyles(() =>
|
||||
}),
|
||||
)
|
||||
|
||||
export function SwarmButton({ children, onClick, iconType, className, disabled, loading }: Props): ReactElement {
|
||||
export function SwarmButton({
|
||||
children,
|
||||
onClick,
|
||||
iconType,
|
||||
className,
|
||||
disabled,
|
||||
loading,
|
||||
cancel,
|
||||
}: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
function getIconColor() {
|
||||
if (loading || disabled) {
|
||||
return 'rgba(0, 0, 0, 0.26)'
|
||||
}
|
||||
|
||||
return cancel ? '#606060' : '#dd7700'
|
||||
}
|
||||
|
||||
function getButtonClassName() {
|
||||
return [className, classes.button, cancel && classes.cancelButton].filter(x => x).join(' ')
|
||||
}
|
||||
|
||||
const icon = React.createElement(iconType, {
|
||||
size: '1.25rem',
|
||||
color: disabled ? 'rgba(0, 0, 0, 0.26)' : '#dd7700',
|
||||
color: getIconColor(),
|
||||
})
|
||||
|
||||
const classNames = className ? [className, classes.button].join(' ') : classes.button
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={classNames}
|
||||
className={getButtonClassName()}
|
||||
onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
onClick()
|
||||
event.currentTarget.blur()
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Box, Dialog, Grid } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
interface Props {
|
||||
children: ReactElement | ReactElement[]
|
||||
}
|
||||
|
||||
export function SwarmDialog({ children }: Props): ReactElement {
|
||||
return (
|
||||
<Dialog
|
||||
open={true}
|
||||
PaperProps={{
|
||||
style: { borderRadius: 0, background: '#efefef' },
|
||||
}}
|
||||
>
|
||||
<Box p={4} sx={{ maxWidth: '100%', width: '650px' }}>
|
||||
<Grid container direction="column">
|
||||
{children}
|
||||
</Grid>
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { createStyles, FormHelperText, makeStyles, MenuItem, Select as SimpleSelect, Theme } from '@material-ui/core'
|
||||
import { Field } from 'formik'
|
||||
import { Select } from 'formik-material-ui'
|
||||
import { ReactElement } from 'react'
|
||||
|
||||
export type SelectEvent = React.ChangeEvent<{
|
||||
name?: string | undefined
|
||||
value: unknown
|
||||
}>
|
||||
|
||||
interface Props {
|
||||
label?: string
|
||||
name?: string
|
||||
options: { value: string; label: string }[]
|
||||
onChange?: (event: SelectEvent) => void
|
||||
formik?: boolean
|
||||
defaultValue?: string
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
select: {
|
||||
borderRadius: 0,
|
||||
background: theme.palette.background.paper,
|
||||
'& fieldset': {
|
||||
border: 0,
|
||||
},
|
||||
},
|
||||
option: {
|
||||
height: '52px',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export function SwarmSelect({ defaultValue, formik, name, options, onChange, label }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
if (formik) {
|
||||
return (
|
||||
<>
|
||||
{label && <FormHelperText>{label}</FormHelperText>}
|
||||
<Field
|
||||
required
|
||||
component={Select}
|
||||
name={name}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
defaultValue={defaultValue || ''}
|
||||
className={classes.select}
|
||||
placeholder={label}
|
||||
>
|
||||
{options.map((x, i) => (
|
||||
<MenuItem key={i} value={x.value} className={classes.option}>
|
||||
{x.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Field>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{label && <FormHelperText>{label}</FormHelperText>}
|
||||
<SimpleSelect
|
||||
required
|
||||
name={name}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
className={classes.select}
|
||||
defaultValue={defaultValue || ''}
|
||||
onChange={onChange}
|
||||
placeholder={label}
|
||||
>
|
||||
{options.map((x, i) => (
|
||||
<MenuItem key={i} value={x.value} className={classes.option}>
|
||||
{x.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</SimpleSelect>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import { createStyles, makeStyles, TextField as SimpleTextField, Theme } from '@material-ui/core'
|
||||
import { Field } from 'formik'
|
||||
import { TextField } from 'formik-material-ui'
|
||||
import { ChangeEvent, ReactElement } from 'react'
|
||||
|
||||
interface Props {
|
||||
name: string
|
||||
label: string
|
||||
password?: boolean
|
||||
formik?: boolean
|
||||
optional?: boolean
|
||||
onChange?: (event: ChangeEvent<HTMLTextAreaElement>) => void
|
||||
}
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
field: {
|
||||
background: theme.palette.background.paper,
|
||||
height: '52px',
|
||||
'& fieldset': {
|
||||
border: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export function SwarmTextInput({ name, label, password, optional, formik, onChange }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
if (formik) {
|
||||
return (
|
||||
<Field
|
||||
component={TextField}
|
||||
type={password ? 'password' : undefined}
|
||||
required={!optional}
|
||||
name={name}
|
||||
label={label}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
className={classes.field}
|
||||
defaultValue=""
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<SimpleTextField
|
||||
type={password ? 'password' : undefined}
|
||||
required
|
||||
label={label}
|
||||
fullWidth
|
||||
variant="outlined"
|
||||
className={classes.field}
|
||||
defaultValue=""
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||
import { ReactElement } from 'react'
|
||||
import { CloseButton } from './CloseButton'
|
||||
|
||||
interface Props {
|
||||
children: string
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
text: {
|
||||
color: '#606060',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
export function TitleWithClose({ children, onClose }: Props): ReactElement {
|
||||
const classes = useStyles()
|
||||
|
||||
return (
|
||||
<Grid container justifyContent="space-between" alignItems="center">
|
||||
<span> </span>
|
||||
<Typography className={classes.text} align="center">
|
||||
{children}
|
||||
</Typography>
|
||||
<CloseButton onClose={onClose} />
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user