Skip to content

Commit

Permalink
feat: update add item modal, add links, update edit form
Browse files Browse the repository at this point in the history
  • Loading branch information
pyphilia committed Mar 23, 2021
1 parent c2a2607 commit 63c81d3
Show file tree
Hide file tree
Showing 23 changed files with 862 additions and 275 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@material-ui/lab": "4.0.0-alpha.57",
"@uppy/aws-s3": "1.7.7",
"@uppy/core": "1.16.1",
"@uppy/dashboard": "1.17.1",
"@uppy/drag-drop": "1.4.25",
"@uppy/react": "1.11.3",
"@uppy/tus": "1.8.5",
Expand Down
65 changes: 57 additions & 8 deletions src/api/item.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { API_HOST, ROOT_ID } from '../config/constants';
import { v4 as uuidv4 } from 'uuid';
import { API_HOST, ITEM_TYPES, ROOT_ID } from '../config/constants';
import {
buildCopyItemRoute,
buildDeleteItemRoute,
Expand Down Expand Up @@ -41,17 +42,44 @@ export const getItem = async (id) => {
export const getItems = () => CacheOperations.getItems();

export const getOwnItems = async () => {
const res = await fetch(`${API_HOST}/${GET_OWN_ITEMS_ROUTE}`, DEFAULT_GET);
// -------- TO REMOVE
const id = 'c4dea86a-5d7c-4144-b038-20b746793e12';
const item = {
id,
name: 'namename',
path: id.replaceAll('-', '_'),
type: ITEM_TYPES.LINK,
description: 'desc',
extra: {
embeddedLinkItem: {
url: 'https://graasp.eu',
html:
'<iframe width="560" height="315" src="https://www.youtube.com/embed/ZAzWT8mRoR0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
icons: [
'https://graasp.eu/cdn/img/epfl/favicons/favicon-32x32.png?v=yyxJ380oWY',
],
thumbnails: ['https://graasp.eu/img/epfl/logo-tile.png'],
},
},
};
await CacheOperations.createItem({ item });
// eslint-disable-next-line no-console
console.log(GET_OWN_ITEMS_ROUTE);
return [item];

if (!res.ok) {
throw new Error((await res.json()).message);
}
// -------- TO REMOVE

const ownItems = await res.json();
// const res = await fetch(`${API_HOST}/${GET_OWN_ITEMS_ROUTE}`, DEFAULT_GET);

await CacheOperations.saveItems(ownItems);
// if (!res.ok) {
// throw new Error((await res.json()).message);
// }

return ownItems;
// const ownItems = await res.json();

// await CacheOperations.saveItems(ownItems);

// return ownItems;
};

// payload = {name, type, description, extra}
Expand All @@ -63,6 +91,27 @@ export const postItem = async ({
extra,
parentId,
} = {}) => {
// -------- TO REMOVE
if (type === ITEM_TYPES.LINK) {
// eslint-disable-next-line no-console
console.log('weiufhskdjn');
const newExtra = extra;
// newExtra.embeddedLinkItem.html = 'someplayer';
newExtra.embeddedLinkItem.thumbnails = ['link'];
newExtra.embeddedLinkItem.icons = ['link'];
const newItem = {
id: uuidv4(),
name,
type,
description,
extra: newExtra,
};
await CacheOperations.createItem({ item: newItem });
return newItem;
}

// -------- TO REMOVE

const res = await fetch(`${API_HOST}/${buildPostItemRoute(parentId)}`, {
...DEFAULT_POST,
body: JSON.stringify({ name, type, description, extra }),
Expand Down
1 change: 1 addition & 0 deletions src/components/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const theme = createMuiTheme({
palette: {
primary: {
main: '#5050d2',
selected: '#cbcbef',
},
secondary: { main: '#ffffff' },
},
Expand Down
34 changes: 34 additions & 0 deletions src/components/item/LinkItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { makeStyles } from '@material-ui/core';
import { Map } from 'immutable';
import PropTypes from 'prop-types';
import React from 'react';

const useStyles = makeStyles(() => ({
iframe: {
width: '100%',
height: '100%',
border: 'none',
},
}));

const LinkItem = ({ item }) => {
const classes = useStyles();

// if available, display specific player
const html = item.getIn(['extra', 'embeddedLinkItem', 'html']);
if (html) {
// eslint-disable-next-line react/no-danger
return <div dangerouslySetInnerHTML={{ __html: html }} />;
}

// default case is an iframe with given link
const url = item.getIn(['extra', 'embeddedLinkItem', 'url']);
const name = item.get('name');
return <iframe className={classes.iframe} title={name} src={url} />;
};

LinkItem.propTypes = {
item: PropTypes.instanceOf(Map).isRequired,
};

export default LinkItem;
72 changes: 72 additions & 0 deletions src/components/item/form/BaseItemForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import { makeStyles, TextField } from '@material-ui/core';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import {
ITEM_FORM_DESCRIPTION_INPUT_ID,
ITEM_FORM_NAME_INPUT_ID,
} from '../../../config/selectors';

const useStyles = makeStyles((theme) => ({
shortInputField: {
width: '50%',
},
addedMargin: {
marginTop: theme.spacing(2),
},
}));

const BaseForm = ({ onChange, item }) => {
const { t } = useTranslation();
const classes = useStyles();

const handleNameInput = (event) => {
onChange({ ...item, name: event.target.value });
// eslint-disable-next-line no-console
console.log(event.target.value);
};

const handleDescriptionInput = (event) => {
onChange({ ...item, description: event.target.value });
};

return (
<>
<TextField
autoFocus
margin="dense"
id={ITEM_FORM_NAME_INPUT_ID}
label={t('Name')}
value={item?.name}
onChange={handleNameInput}
className={classes.shortInputField}
/>
<TextField
id={ITEM_FORM_DESCRIPTION_INPUT_ID}
margin="dense"
label={t('Description')}
value={item?.description}
onChange={handleDescriptionInput}
multiline
rows={4}
rowsMax={4}
fullWidth
/>
</>
);
};

BaseForm.propTypes = {
onChange: PropTypes.string.isRequired,
item: PropTypes.shape({
name: PropTypes.string,
description: PropTypes.string,
extra: PropTypes.shape({
embeddedLinkItem: PropTypes.shape({
url: PropTypes.string,
}),
}),
}).isRequired,
};

export default BaseForm;
51 changes: 51 additions & 0 deletions src/components/item/form/LinkForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { TextField } from '@material-ui/core';
import PropTypes from 'prop-types';
import { useTranslation } from 'react-i18next';
import { isValidURL } from '../../../utils/item';
import BaseForm from './BaseItemForm';

function LinkForm({ onChange, item }) {
const { t } = useTranslation();

const handleLinkInput = (event) => {
// todo: check url validity
onChange({
...item,
extra: { embeddedLinkItem: { url: event.target.value } },
});
};

const { url } = item.extra?.embeddedLinkItem || {};
const isLinkInvalid = url?.length && !isValidURL(url);

return (
<>
<BaseForm onChange={onChange} item={item} />
<TextField
error={isLinkInvalid}
autoFocus
margin="dense"
label={t('Link')}
value={url}
onChange={handleLinkInput}
helperText={Boolean(isLinkInvalid) && t('This link is not valid')}
/>
</>
);
}

LinkForm.propTypes = {
onChange: PropTypes.string.isRequired,
item: PropTypes.shape({
name: PropTypes.string,
description: PropTypes.string,
extra: PropTypes.shape({
embeddedLinkItem: PropTypes.shape({
url: PropTypes.string,
}),
}),
}).isRequired,
};

export default LinkForm;
55 changes: 55 additions & 0 deletions src/components/item/form/SpaceForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { useTranslation, withTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import TextField from '@material-ui/core/TextField';
import { ITEM_TYPES } from '../../../config/constants';
import { ITEM_FORM_IMAGE_INPUT_ID } from '../../../config/selectors';
import BaseItemForm from './BaseItemForm';

const ItemForm = ({ onChange, item }) => {
const { t } = useTranslation();

const handleImageUrlInput = (event) => {
onChange({ ...item, extra: { image: event.target.value } });
};

return (
<>
<BaseItemForm onChange={onChange} item={item} />
{ITEM_TYPES.FILE !== item?.type && (
<TextField
id={ITEM_FORM_IMAGE_INPUT_ID}
margin="dense"
label={t('Image (URL)')}
value={item?.extra?.image}
onChange={handleImageUrlInput}
fullWidth
/>
)}
</>
);
};

ItemForm.propTypes = {
onChange: PropTypes.func.isRequired,
classes: PropTypes.shape({
shortInputField: PropTypes.string.isRequired,
dialogContent: PropTypes.string.isRequired,
addedMargin: PropTypes.string.isRequired,
}).isRequired,
item: PropTypes.shape({
name: PropTypes.string,
description: PropTypes.string,
type: PropTypes.string,
extra: PropTypes.shape({
image: PropTypes.string,
}),
}),
};

ItemForm.defaultProps = {
item: {},
};

const TranslatedComponent = withTranslation()(ItemForm);
export default TranslatedComponent;
9 changes: 6 additions & 3 deletions src/components/main/CustomCardHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,16 @@ const useStyles = makeStyles((theme) => ({
}));

const CustomCardHeader = ({ item }) => {
const { id, creator, name, type } = item;
const { id, creator = {}, name, type } = item;
const classes = useStyles();
const { t } = useTranslation();

const { avatar: creatorAvatar, name: creatorName = t('Unknown') } = creator;

return (
<div className={classes.root}>
<div className={classes.header}>
<Avatar src={creator.avatar} className={classes.avatar} />
<Avatar src={creatorAvatar} className={classes.avatar} />
<div>
<Link to={buildItemPath(id)} className={classes.link}>
<Typography id={buildItemLink(id)} className={classes.title}>
Expand All @@ -54,7 +57,7 @@ const CustomCardHeader = ({ item }) => {
<Typography className={classes.subtitle}>
{t('Type by author', {
type,
author: creator.name || t('Unknown'),
author: creatorName,
})}
</Typography>
</div>
Expand Down
Loading

0 comments on commit 63c81d3

Please sign in to comment.