From a5d6cf04cff771073468a6c0ee2c2ca8c659f2ae Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Jul 2021 12:36:03 +0200 Subject: [PATCH] Custom icons for bookmarks --- CHANGELOG.md | 4 + client/.env | 2 +- .../src/components/Apps/AppForm/AppForm.tsx | 13 +- .../Bookmarks/BookmarkCard/BookmarkCard.tsx | 9 +- .../BookmarkForm/BookmarkForm.module.css | 7 + .../Bookmarks/BookmarkForm/BookmarkForm.tsx | 130 ++++++++++++++---- client/src/store/actions/bookmark.ts | 23 ++-- controllers/bookmark.js | 24 +++- routes/bookmark.js | 5 +- 9 files changed, 169 insertions(+), 48 deletions(-) create mode 100644 client/src/components/Bookmarks/BookmarkForm/BookmarkForm.module.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 44a7ec8..d1ed220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### v1.6.1 (2021-07-28) +- Added option to upload custom icons for bookmarks ([#52](https://github.com/pawelmalak/flame/issues/52)) +- Fixed custom icons not updating ([#58](https://github.com/pawelmalak/flame/issues/58)) + ### v1.6 (2021-07-17) - Added support for Steam URLs ([#62](https://github.com/pawelmalak/flame/issues/62)) - Fixed bug with custom CSS not persisting ([#64](https://github.com/pawelmalak/flame/issues/64)) diff --git a/client/.env b/client/.env index 036129f..f56a185 100644 --- a/client/.env +++ b/client/.env @@ -1 +1 @@ -REACT_APP_VERSION=1.6.0 \ No newline at end of file +REACT_APP_VERSION=1.6.1 \ No newline at end of file diff --git a/client/src/components/Apps/AppForm/AppForm.tsx b/client/src/components/Apps/AppForm/AppForm.tsx index d8f3923..72d8db2 100644 --- a/client/src/components/Apps/AppForm/AppForm.tsx +++ b/client/src/components/Apps/AppForm/AppForm.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, ChangeEvent, SyntheticEvent } from 'react'; +import { useState, useEffect, ChangeEvent, SyntheticEvent } from 'react'; import { connect } from 'react-redux'; import { addApp, updateApp } from '../../../store/actions'; import { App, NewApp } from '../../../interfaces'; @@ -25,14 +25,6 @@ const AppForm = (props: ComponentProps): JSX.Element => { icon: '' }); - const inputRef = useRef(null); - - useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, [inputRef]) - useEffect(() => { if (props.app) { setFormData({ @@ -98,6 +90,8 @@ const AppForm = (props: ComponentProps): JSX.Element => { url: '', icon: '' }) + + setCustomIcon(null); } return ( @@ -115,7 +109,6 @@ const AppForm = (props: ComponentProps): JSX.Element => { required value={formData.name} onChange={(e) => inputChangeHandler(e)} - ref={inputRef} /> diff --git a/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx b/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx index f2535b5..fe2198b 100644 --- a/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx +++ b/client/src/components/Bookmarks/BookmarkCard/BookmarkCard.tsx @@ -24,7 +24,14 @@ const BookmarkCard = (props: ComponentProps): JSX.Element => { key={`bookmark-${bookmark.id}`}> {bookmark.icon && (
- + {(/.(jpeg|jpg|png)$/i).test(bookmark.icon) + ? {`${bookmark.name} + : + }
)} {bookmark.name} diff --git a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.module.css b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.module.css new file mode 100644 index 0000000..66b15a0 --- /dev/null +++ b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.module.css @@ -0,0 +1,7 @@ +.Switch { + text-decoration: underline; +} + +.Switch:hover { + cursor: pointer; +} \ No newline at end of file diff --git a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx index eb83013..67059ae 100644 --- a/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx +++ b/client/src/components/Bookmarks/BookmarkForm/BookmarkForm.tsx @@ -7,6 +7,7 @@ import { Bookmark, Category, GlobalState, NewBookmark, NewCategory, NewNotificat import { ContentType } from '../Bookmarks'; import { getCategories, addCategory, addBookmark, updateCategory, updateBookmark, createNotification } from '../../../store/actions'; import Button from '../../UI/Buttons/Button/Button'; +import classes from './BookmarkForm.module.css'; interface ComponentProps { modalHandler: () => void; @@ -15,13 +16,22 @@ interface ComponentProps { category?: Category; bookmark?: Bookmark; addCategory: (formData: NewCategory) => void; - addBookmark: (formData: NewBookmark) => void; + addBookmark: (formData: NewBookmark | FormData) => void; updateCategory: (id: number, formData: NewCategory) => void; - updateBookmark: (id: number, formData: NewBookmark, previousCategoryId: number) => void; + updateBookmark: ( + id: number, + formData: NewBookmark | FormData, + category: { + prev: number, + curr: number + } + ) => void; createNotification: (notification: NewNotification) => void; } const BookmarkForm = (props: ComponentProps): JSX.Element => { + const [useCustomIcon, toggleUseCustomIcon] = useState(false); + const [customIcon, setCustomIcon] = useState(null); const [categoryName, setCategoryName] = useState({ name: '' }) @@ -64,6 +74,18 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { const formSubmitHandler = (e: SyntheticEvent): void => { e.preventDefault(); + const createFormData = (): FormData => { + const data = new FormData(); + if (customIcon) { + data.append('icon', customIcon); + } + data.append('name', formData.name); + data.append('url', formData.url); + data.append('categoryId', `${formData.categoryId}`); + + return data; + } + if (!props.category && !props.bookmark) { // Add new if (props.contentType === ContentType.category) { @@ -79,14 +101,22 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { }) return; } - - props.addBookmark(formData); + + if (customIcon) { + const data = createFormData(); + props.addBookmark(data); + } else { + props.addBookmark(formData); + } + setFormData({ name: '', url: '', categoryId: formData.categoryId, icon: '' }) + + setCustomIcon(null) } } else { // Update @@ -96,13 +126,35 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { setCategoryName({ name: '' }); } else if (props.contentType === ContentType.bookmark && props.bookmark) { // Update bookmark - props.updateBookmark(props.bookmark.id, formData, props.bookmark.categoryId); + if (customIcon) { + const data = createFormData(); + props.updateBookmark( + props.bookmark.id, + data, + { + prev: props.bookmark.categoryId, + curr: formData.categoryId + } + ) + } else { + props.updateBookmark( + props.bookmark.id, + formData, + { + prev: props.bookmark.categoryId, + curr: formData.categoryId + } + ); + } + setFormData({ name: '', url: '', categoryId: -1, icon: '' }) + + setCustomIcon(null) } props.modalHandler(); @@ -123,6 +175,12 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { }) } + const fileChangeHandler = (e: ChangeEvent): void => { + if (e.target.files) { + setCustomIcon(e.target.files[0]); + } + } + let button = if (!props.category && !props.bookmark) { @@ -216,25 +274,49 @@ const BookmarkForm = (props: ComponentProps): JSX.Element => { })}
- - - inputChangeHandler(e)} - /> - - Use icon name from MDI. - - {' '}Click here for reference - - - + {!useCustomIcon + // mdi + ? ( + + inputChangeHandler(e)} + /> + + Use icon name from MDI. + + {' '}Click here for reference + + + toggleUseCustomIcon(!useCustomIcon)} + className={classes.Switch}> + Switch to custom icon upload + + ) + // custom + : ( + + fileChangeHandler(e)} + accept='.jpg,.jpeg,.png' + /> + toggleUseCustomIcon(!useCustomIcon)} + className={classes.Switch}> + Switch to MDI + + ) + } ) } diff --git a/client/src/store/actions/bookmark.ts b/client/src/store/actions/bookmark.ts index 0398bbb..8707062 100644 --- a/client/src/store/actions/bookmark.ts +++ b/client/src/store/actions/bookmark.ts @@ -69,15 +69,15 @@ export interface AddBookmarkAction { payload: Bookmark } -export const addBookmark = (formData: NewBookmark) => async (dispatch: Dispatch) => { +export const addBookmark = (formData: NewBookmark | FormData) => async (dispatch: Dispatch) => { try { const res = await axios.post>('/api/bookmarks', formData); - + console.log(res.data.data) dispatch({ type: ActionTypes.createNotification, payload: { title: 'Success', - message: `Bookmark ${formData.name} created` + message: `Bookmark created` } }) @@ -225,7 +225,14 @@ export interface UpdateBookmarkAction { payload: Bookmark } -export const updateBookmark = (bookmarkId: number, formData: NewBookmark, previousCategoryId: number) => async (dispatch: Dispatch) => { +export const updateBookmark = ( + bookmarkId: number, + formData: NewBookmark | FormData, + category: { + prev: number, + curr: number + } +) => async (dispatch: Dispatch) => { try { const res = await axios.put>(`/api/bookmarks/${bookmarkId}`, formData); @@ -233,12 +240,12 @@ export const updateBookmark = (bookmarkId: number, formData: NewBookmark, previo type: ActionTypes.createNotification, payload: { title: 'Success', - message: `Bookmark ${formData.name} updated` + message: `Bookmark updated` } }) // Check if category was changed - const categoryWasChanged = formData.categoryId !== previousCategoryId; + const categoryWasChanged = category.curr !== category.prev; if (categoryWasChanged) { // Delete bookmark from old category @@ -246,7 +253,7 @@ export const updateBookmark = (bookmarkId: number, formData: NewBookmark, previo type: ActionTypes.deleteBookmark, payload: { bookmarkId, - categoryId: previousCategoryId + categoryId: category.prev } }) @@ -256,7 +263,7 @@ export const updateBookmark = (bookmarkId: number, formData: NewBookmark, previo payload: res.data.data }) } else { - // Else update only name/url + // Else update only name/url/icon dispatch({ type: ActionTypes.updateBookmark, payload: res.data.data diff --git a/controllers/bookmark.js b/controllers/bookmark.js index 08b2fca..8077a8c 100644 --- a/controllers/bookmark.js +++ b/controllers/bookmark.js @@ -7,7 +7,18 @@ const { Sequelize } = require('sequelize'); // @route POST /api/bookmarks // @access Public exports.createBookmark = asyncWrapper(async (req, res, next) => { - const bookmark = await Bookmark.create(req.body); + let bookmark; + + let _body = { + ...req.body, + categoryId: parseInt(req.body.categoryId) + }; + + if (req.file) { + _body.icon = req.file.filename; + } + + bookmark = await Bookmark.create(_body); res.status(201).json({ success: true, @@ -59,7 +70,16 @@ exports.updateBookmark = asyncWrapper(async (req, res, next) => { return next(new ErrorResponse(`Bookmark with id of ${req.params.id} was not found`, 404)); } - bookmark = await bookmark.update({ ...req.body }); + let _body = { + ...req.body, + categoryId: parseInt(req.body.categoryId) + }; + + if (req.file) { + _body.icon = req.file.filename; + } + + bookmark = await bookmark.update(_body); res.status(200).json({ success: true, diff --git a/routes/bookmark.js b/routes/bookmark.js index f0d62f4..c594738 100644 --- a/routes/bookmark.js +++ b/routes/bookmark.js @@ -1,5 +1,6 @@ const express = require('express'); const router = express.Router(); +const upload = require('../middleware/multer'); const { createBookmark, @@ -11,13 +12,13 @@ const { router .route('/') - .post(createBookmark) + .post(upload, createBookmark) .get(getBookmarks); router .route('/:id') .get(getBookmark) - .put(updateBookmark) + .put(upload, updateBookmark) .delete(deleteBookmark); module.exports = router; \ No newline at end of file