diff --git a/api/src/functions/identity-signup.js b/api/src/functions/identity-signup.js index b9f3460..ee6fa0a 100644 --- a/api/src/functions/identity-signup.js +++ b/api/src/functions/identity-signup.js @@ -68,11 +68,12 @@ export const handler = async (req, _context) => { const isUnique = !(await db.user.findOne({ where: { userName: seed }, })) - if(isUnique) { + if (isUnique) { return seed } count += 1 - const newSeed = count === 1 ? `${seed}_${count}` : seed.slice(0,-1) + count + const newSeed = + count === 1 ? `${seed}_${count}` : seed.slice(0, -1) + count return generateUniqueUserName(newSeed, count) } const userNameSeed = enforceAlphaNumeric(email.split('@')[0]) @@ -83,7 +84,7 @@ export const handler = async (req, _context) => { name: user.user_metadata && user.user_metadata.full_name, id: user.id, } - await createUserInsecure({input}) + await createUserInsecure({ input }) return { statusCode: 200, diff --git a/api/src/graphql/parts.sdl.js b/api/src/graphql/parts.sdl.js index a7f42e4..31dd970 100644 --- a/api/src/graphql/parts.sdl.js +++ b/api/src/graphql/parts.sdl.js @@ -16,7 +16,7 @@ export const schema = gql` type Query { parts: [Part!]! part(id: String!): Part - partByUserAndTitle(userName: String! partTitle: String!): Part + partByUserAndTitle(userName: String!, partTitle: String!): Part } input CreatePartInput { diff --git a/api/src/lib/auth.js b/api/src/lib/auth.js index 3889bbc..9fbd050 100644 --- a/api/src/lib/auth.js +++ b/api/src/lib/auth.js @@ -142,7 +142,7 @@ export const requireAuth = ({ role } = {}) => { throw new ForbiddenError("You don't have access to do that.") } - if(context.currentUser?.sub === '5cea3906-1e8e-4673-8f0d-89e6a963c096') { + if (context.currentUser?.sub === '5cea3906-1e8e-4673-8f0d-89e6a963c096') { throw new ForbiddenError("That's a local admin ONLY.") } } diff --git a/api/src/lib/owner.js b/api/src/lib/owner.js index 086deeb..dc60aff 100644 --- a/api/src/lib/owner.js +++ b/api/src/lib/owner.js @@ -7,37 +7,38 @@ export const requireOwnership = async ({ userId, userName, partId } = {}) => { if (!context.currentUser) { throw new AuthenticationError("You don't have permission to do that.") } - if(!userId && !userName && !partId) { + if (!userId && !userName && !partId) { throw new ForbiddenError("You don't have access to do that.") } - if(context.currentUser.roles?.includes('admin')) { + if (context.currentUser.roles?.includes('admin')) { return } const netlifyUserId = context.currentUser?.sub - if(userId && userId !== netlifyUserId) { + if (userId && userId !== netlifyUserId) { throw new ForbiddenError("You don't own this resource.") } - if(userName) { + if (userName) { const user = await db.user.findOne({ where: { userName }, }) - if(!user || user.id !== netlifyUserId) { + if (!user || user.id !== netlifyUserId) { throw new ForbiddenError("You don't own this resource.") } } - if(partId) { - const user = await db.part.findOne({ - where: { id: partId }, - }).user() + if (partId) { + const user = await db.part + .findOne({ + where: { id: partId }, + }) + .user() - if(!user || user.id !== netlifyUserId) { + if (!user || user.id !== netlifyUserId) { throw new ForbiddenError("You don't own this resource.") } } - } diff --git a/api/src/services/helpers.js b/api/src/services/helpers.js index d6e21f3..c5611eb 100644 --- a/api/src/services/helpers.js +++ b/api/src/services/helpers.js @@ -12,4 +12,5 @@ export const foreignKeyReplacement = (input) => { return output } -export const enforceAlphaNumeric = (string) => string.replace(/([^a-zA-Z\d_:])/g, '-') +export const enforceAlphaNumeric = (string) => + string.replace(/([^a-zA-Z\d_:])/g, '-') diff --git a/api/src/services/partReactions/partReactions.js b/api/src/services/partReactions/partReactions.js index 4efd545..9116014 100644 --- a/api/src/services/partReactions/partReactions.js +++ b/api/src/services/partReactions/partReactions.js @@ -18,20 +18,24 @@ export const partReaction = ({ id }) => { export const togglePartReaction = async ({ input }) => { // if write fails emote_userId_partId @@unique constraint, then delete it instead requireAuth() - await requireOwnership({userId: input?.userId}) + await requireOwnership({ userId: input?.userId }) const legalReactions = ['❤️', '👍', '😄', '🙌'] // TODO figure out a way of sharing code between FE and BE, so this is consistent with web/src/components/EmojiReaction/EmojiReaction.js - if(!legalReactions.includes(input.emote)) { - throw new UserInputError(`You can't react with '${input.emote}', only the following are allowed: ${legalReactions.join(', ')}`) + if (!legalReactions.includes(input.emote)) { + throw new UserInputError( + `You can't react with '${ + input.emote + }', only the following are allowed: ${legalReactions.join(', ')}` + ) } let dbPromise - const inputClone = {...input} // TODO foreignKeyReplacement mutates input, which I should fix but am lazy right now - try{ + const inputClone = { ...input } // TODO foreignKeyReplacement mutates input, which I should fix but am lazy right now + try { dbPromise = await db.partReaction.create({ data: foreignKeyReplacement(input), }) - } catch(e) { + } catch (e) { dbPromise = db.partReaction.delete({ - where: { emote_userId_partId: inputClone}, + where: { emote_userId_partId: inputClone }, }) } return dbPromise diff --git a/api/src/services/parts/parts.js b/api/src/services/parts/parts.js index 260442c..aad2a72 100644 --- a/api/src/services/parts/parts.js +++ b/api/src/services/parts/parts.js @@ -1,5 +1,8 @@ import { db } from 'src/lib/db' -import { foreignKeyReplacement, enforceAlphaNumeric } from 'src/services/helpers' +import { + foreignKeyReplacement, + enforceAlphaNumeric, +} from 'src/services/helpers' import { requireAuth } from 'src/lib/auth' import { requireOwnership } from 'src/lib/owner' @@ -15,15 +18,15 @@ export const part = ({ id }) => { export const partByUserAndTitle = async ({ userName, partTitle }) => { const user = await db.user.findOne({ where: { - userName - } + userName, + }, }) return db.part.findOne({ where: { title_userId: { title: partTitle, userId: user.id, - } + }, }, }) } @@ -37,8 +40,8 @@ export const createPart = async ({ input }) => { export const updatePart = async ({ id, input }) => { requireAuth() - await requireOwnership({partId: id}) - if(input.title) { + await requireOwnership({ partId: id }) + if (input.title) { input.title = enforceAlphaNumeric(input.title) } return db.part.update({ @@ -59,5 +62,7 @@ export const Part = { Comment: (_obj, { root }) => db.part.findOne({ where: { id: root.id } }).Comment(), Reaction: (_obj, { root }) => - db.part.findOne({ where: { id: root.id } }).Reaction({where: {userId: _obj.userId}}), + db.part + .findOne({ where: { id: root.id } }) + .Reaction({ where: { userId: _obj.userId } }), } diff --git a/api/src/services/users/users.js b/api/src/services/users/users.js index c06e1bf..4292baa 100644 --- a/api/src/services/users/users.js +++ b/api/src/services/users/users.js @@ -23,7 +23,7 @@ export const userName = ({ userName }) => { export const createUser = ({ input }) => { requireAuth({ role: 'admin' }) - createUserInsecure({input}) + createUserInsecure({ input }) } export const createUserInsecure = ({ input }) => { return db.user.create({ @@ -41,12 +41,15 @@ export const updateUser = ({ id, input }) => { export const updateUserByUserName = async ({ userName, input }) => { requireAuth() - await requireOwnership({userName}) - if(input.userName) { + await requireOwnership({ userName }) + if (input.userName) { input.userName = enforceAlphaNumeric(input.userName) } - if(input.userName && ['new', 'edit', 'update'].includes(input.userName)) { //TODO complete this and use a regexp so that it's not case sensitive, don't want someone with the userName eDiT - throw new UserInputError(`You've tried to used a protected word as you userName, try something other than `) + if (input.userName && ['new', 'edit', 'update'].includes(input.userName)) { + //TODO complete this and use a regexp so that it's not case sensitive, don't want someone with the userName eDiT + throw new UserInputError( + `You've tried to used a protected word as you userName, try something other than ` + ) } return db.user.update({ data: input, @@ -63,10 +66,16 @@ export const deleteUser = ({ id }) => { export const User = { Parts: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Part(), - Part: (_obj, { root, ...rest }) => _obj.partTitle && db.part.findOne({where: { title_userId: { - title: _obj.partTitle, - userId: root.id, - }}}), + Part: (_obj, { root, ...rest }) => + _obj.partTitle && + db.part.findOne({ + where: { + title_userId: { + title: _obj.partTitle, + userId: root.id, + }, + }, + }), Reaction: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Reaction(), Comment: (_obj, { root }) => diff --git a/package.json b/package.json index 8f4dc54..ea70fa9 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,7 @@ "@redwoodjs/core": "^0.19.2" }, "eslintConfig": { - "extends": "@redwoodjs/eslint-config", - "workingDirectories": [ - "./api", - "./web/src/components", - "./web/src/layouts", - "./web/src/pages", - "./web/src/index.js", - "./web/src/Routes.js" - ] + "extends": "@redwoodjs/eslint-config" }, "engines": { "node": ">=12", diff --git a/web/src/components/Breadcrumb/Breadcrumb.js b/web/src/components/Breadcrumb/Breadcrumb.js index fe921fb..1057db4 100644 --- a/web/src/components/Breadcrumb/Breadcrumb.js +++ b/web/src/components/Breadcrumb/Breadcrumb.js @@ -1,20 +1,31 @@ -import { getActiveClasses } from "get-active-classes" +import { getActiveClasses } from 'get-active-classes' import InputText from 'src/components/InputText' const Breadcrumb = ({ userName, partTitle, onPartTitleChange, className }) => { return ( -

-
.
- +

+
+ . +
+ {userName} -
.
+
+ . +

) diff --git a/web/src/components/Button/Button.js b/web/src/components/Button/Button.js index 5d76f0f..28b1056 100644 --- a/web/src/components/Button/Button.js +++ b/web/src/components/Button/Button.js @@ -1,21 +1,31 @@ import { getActiveClasses } from 'get-active-classes' import Svg from 'src/components/Svg' -const Button = ({onClick, iconName, children, className, shouldAnimateHover, disabled}) => { +const Button = ({ + onClick, + iconName, + children, + className, + shouldAnimateHover, + disabled, +}) => { return ( - + ) } diff --git a/web/src/components/Button/Button.stories.js b/web/src/components/Button/Button.stories.js index 7d95d2d..115369e 100644 --- a/web/src/components/Button/Button.stories.js +++ b/web/src/components/Button/Button.stories.js @@ -1,10 +1,12 @@ import Button from './Button' export const generated = () => { - return <> - button with icon - - + return ( + <> + button with icon + + + ) } export default { title: 'Components/Button' } diff --git a/web/src/components/EditUser2Cell/EditUser2Cell.js b/web/src/components/EditUser2Cell/EditUser2Cell.js index 850f260..94a7d24 100644 --- a/web/src/components/EditUser2Cell/EditUser2Cell.js +++ b/web/src/components/EditUser2Cell/EditUser2Cell.js @@ -35,8 +35,8 @@ export const Failure = ({ error }) =>
Error: {error.message}
export const Success = ({ user }) => { const { addMessage } = useFlash() const [updateUser, { loading, error }] = useMutation(UPDATE_USER_MUTATION, { - onCompleted: ({updateUserByUserName}) => { - navigate(routes.user2({userName: updateUserByUserName.userName})) + onCompleted: ({ updateUserByUserName }) => { + navigate(routes.user2({ userName: updateUserByUserName.userName })) addMessage('User updated.', { classes: 'rw-flash-success' }) }, }) @@ -45,11 +45,13 @@ export const Success = ({ user }) => { updateUser({ variables: { userName, input } }) } - return + return ( + + ) } diff --git a/web/src/components/EmojiReaction/EmojiReaction.js b/web/src/components/EmojiReaction/EmojiReaction.js index f4dd910..ce254b9 100644 --- a/web/src/components/EmojiReaction/EmojiReaction.js +++ b/web/src/components/EmojiReaction/EmojiReaction.js @@ -1,5 +1,5 @@ import { useState } from 'react' -import { getActiveClasses } from "get-active-classes" +import { getActiveClasses } from 'get-active-classes' import Popover from '@material-ui/core/Popover' import { useAuth } from '@redwoodjs/auth' @@ -7,14 +7,21 @@ import Svg from 'src/components/Svg' const emojiMenu = ['❤️', '👍', '😄', '🙌'] // const emojiMenu = ['🏆', '❤️', '👍', '😊', '😄', '🚀', '👏', '🙌'] -const noEmotes =[{ - emoji: '❤️', - count: 0, -}] +const noEmotes = [ + { + emoji: '❤️', + count: 0, + }, +] -const textShadow = {textShadow: '0 4px 6px rgba(0, 0, 0, 0.3)'} +const textShadow = { textShadow: '0 4px 6px rgba(0, 0, 0, 0.3)' } -const EmojiReaction = ({ emotes, userEmotes, onEmote = () => {}, className }) => { +const EmojiReaction = ({ + emotes, + userEmotes, + onEmote = () => {}, + className, +}) => { const { currentUser } = useAuth() const [isOpen, setIsOpen] = useState(false) const [anchorEl, setAnchorEl] = useState(null) @@ -48,29 +55,40 @@ const EmojiReaction = ({ emotes, userEmotes, onEmote = () => {}, className }) => return ( <> -
-
-
- -
+
+
+
+
+
{(emotes.length ? emotes : noEmotes).map((emote, i) => ( handleEmojiClick(emote.emoji)} > - {emote.emoji}{emote.count} + {emote.emoji} + {emote.count} ))}
@@ -96,7 +114,9 @@ const EmojiReaction = ({ emotes, userEmotes, onEmote = () => {}, className }) => style={textShadow} key={`${emoji}-${i}}`} onClick={() => handleEmojiClick(emoji)} - >{emoji} + > + {emoji} + ))}
diff --git a/web/src/components/IdePartCell/IdePartCell.js b/web/src/components/IdePartCell/IdePartCell.js index 122ed88..703f70b 100644 --- a/web/src/components/IdePartCell/IdePartCell.js +++ b/web/src/components/IdePartCell/IdePartCell.js @@ -35,8 +35,7 @@ export const Success = ({ part }) => { addMessage('Part updated.', { classes: 'rw-flash-success' }) }, }) - console.log({updatePart}) - + console.log({ updatePart }) const saveCode = (input, id) => { console.log(id, input, 'wowow') diff --git a/web/src/components/ImageUploader/ImageUploader.js b/web/src/components/ImageUploader/ImageUploader.js index ca93f5e..3534293 100644 --- a/web/src/components/ImageUploader/ImageUploader.js +++ b/web/src/components/ImageUploader/ImageUploader.js @@ -1,6 +1,6 @@ -import React, { useCallback, useState } from "react"; -import { useDropzone } from "react-dropzone"; -import Button from "@material-ui/core/Button"; +import React, { useCallback, useState } from 'react' +import { useDropzone } from 'react-dropzone' +import Button from '@material-ui/core/Button' import axios from 'axios' import ReactCrop from 'react-image-crop' import { Dialog } from '@material-ui/core' @@ -8,17 +8,17 @@ import { Image as CloudinaryImage } from 'cloudinary-react' import 'react-image-crop/dist/ReactCrop.css' import Svg from 'src/components/Svg/Svg.js' -const CLOUDINARY_UPLOAD_PRESET = "CadHub_project_images"; -const CLOUDINARY_UPLOAD_URL = "https://api.cloudinary.com/v1_1/irevdev/upload"; +const CLOUDINARY_UPLOAD_PRESET = 'CadHub_project_images' +const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/irevdev/upload' export default function ImageUploader({ - onImageUpload, - imageUrl, - aspectRatio, - className, - isEditable, - width=600 - }) { + onImageUpload, + imageUrl, + aspectRatio, + className, + isEditable, + width = 600, +}) { const [isModalOpen, setIsModalOpen] = useState(false) const [file, setFile] = useState() const [cloudinaryId, setCloudinaryId] = useState(imageUrl) @@ -27,17 +27,17 @@ export default function ImageUploader({ aspect: aspectRatio, unit: '%', width: 100, - }); + }) async function handleImageUpload() { const croppedFile = await getCroppedImg(imageObj, crop, 'avatar') - const imageData = new FormData(); - imageData.append('upload_preset', CLOUDINARY_UPLOAD_PRESET); - imageData.append('file', croppedFile); + const imageData = new FormData() + imageData.append('upload_preset', CLOUDINARY_UPLOAD_PRESET) + imageData.append('file', croppedFile) let upload = axios.post(CLOUDINARY_UPLOAD_URL, imageData) try { const { data } = await upload - if (data && data.public_id !== "") { - onImageUpload({cloudinaryPublicId: data.public_id}) + if (data && data.public_id !== '') { + onImageUpload({ cloudinaryPublicId: data.public_id }) setCloudinaryId(data.public_id) setIsModalOpen(false) } @@ -46,62 +46,85 @@ export default function ImageUploader({ } } // Drag and Drop - const onDrop = useCallback(acceptedFiles => { + const onDrop = useCallback((acceptedFiles) => { setIsModalOpen(true) const fileReader = new FileReader() fileReader.onload = () => { setFile(fileReader.result) } fileReader.readAsDataURL(acceptedFiles[0]) - }, []); + }, []) - const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); + const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }) return ( -
+
- {cloudinaryId && isEditable && } + {cloudinaryId && isEditable && ( + + )} {isEditable && } - {(cloudinaryId || !isEditable) &&
- -
} - {!cloudinaryId && } - {!cloudinaryId && isEditable &&
-
- Drop files here ... - or - upload - + {(cloudinaryId || !isEditable) && ( +
+
-
} + )} + {!cloudinaryId && } + {!cloudinaryId && isEditable && ( +
+
+ Drop files here ... or{' '} + + + upload + + +
+
+ )}
- setIsModalOpen(false)} - > + setIsModalOpen(false)}>
- setImageObj(image)} onChange={newCrop => setCrop(newCrop)} /> - + setImageObj(image)} + onChange={(newCrop) => setCrop(newCrop)} + /> +
- ); + ) } function getCroppedImg(image, crop, fileName) { - const canvas = document.createElement('canvas'); - const scaleX = image.naturalWidth / image.width; - const scaleY = image.naturalHeight / image.height; - canvas.width = crop.width; - canvas.height = crop.height; - const ctx = canvas.getContext('2d'); + const canvas = document.createElement('canvas') + const scaleX = image.naturalWidth / image.width + const scaleY = image.naturalHeight / image.height + canvas.width = crop.width + canvas.height = crop.height + const ctx = canvas.getContext('2d') ctx.drawImage( image, crop.x * scaleX, @@ -112,16 +135,20 @@ function getCroppedImg(image, crop, fileName) { 0, crop.width, crop.height - ); + ) // As Base64 string // const base64Image = canvas.toDataURL('image/jpeg'); // As a blob return new Promise((resolve, reject) => { - canvas.toBlob(blob => { - blob.name = fileName; - resolve(blob); - }, 'image/jpeg', 1); - }); + canvas.toBlob( + (blob) => { + blob.name = fileName + resolve(blob) + }, + 'image/jpeg', + 1 + ) + }) } diff --git a/web/src/components/ImageUploader/ImageUploader.stories.js b/web/src/components/ImageUploader/ImageUploader.stories.js index f5b9f65..e2a49bc 100644 --- a/web/src/components/ImageUploader/ImageUploader.stories.js +++ b/web/src/components/ImageUploader/ImageUploader.stories.js @@ -4,35 +4,39 @@ export const generated = () => { return ( <>

AspectRatio:1, no initial image, editable

- < - ImageUploader - onImageUpload={({cloudinaryPublicId}) => console.log(cloudinaryPublicId)} + + console.log(cloudinaryPublicId) + } aspectRatio={1} isEditable={true} - className={"bg-red-400 rounded-half rounded-br-xl"} + className={'bg-red-400 rounded-half rounded-br-xl'} />

AspectRatio 16:9, no initial image, editable

- < - ImageUploader - onImageUpload={({cloudinaryPublicId}) => console.log(cloudinaryPublicId)} - aspectRatio={16/9} + + console.log(cloudinaryPublicId) + } + aspectRatio={16 / 9} isEditable={true} - className={"bg-red-400 rounded-xl"} + className={'bg-red-400 rounded-xl'} imageUrl="CadHub/inakek2urbreynblzhgt" />

AspectRatio:1, no initial image, NOT editable

- < - ImageUploader - onImageUpload={({cloudinaryPublicId}) => console.log(cloudinaryPublicId)} + + console.log(cloudinaryPublicId) + } aspectRatio={1} - className={"rounded-half rounded-br-xl"} + className={'rounded-half rounded-br-xl'} />

AspectRatio ,16:9 no initial image, NOT editable

- < - ImageUploader - onImageUpload={({cloudinaryPublicId}) => console.log(cloudinaryPublicId)} - aspectRatio={16/9} - className={"rounded-xl"} + + console.log(cloudinaryPublicId) + } + aspectRatio={16 / 9} + className={'rounded-xl'} imageUrl="CadHub/inakek2urbreynblzhgt" /> diff --git a/web/src/components/InputText/InputText.js b/web/src/components/InputText/InputText.js index 93826dd..aad823b 100644 --- a/web/src/components/InputText/InputText.js +++ b/web/src/components/InputText/InputText.js @@ -1,9 +1,15 @@ import { getActiveClasses } from 'get-active-classes' -const InputText = ({value, isEditable, onChange ,className}) => { +const InputText = ({ value, isEditable, onChange, className }) => { return ( <> -
+
{ type="text" />
- {value} + + {value} + ) } diff --git a/web/src/components/Part2Cell/Part2Cell.js b/web/src/components/Part2Cell/Part2Cell.js index a9016c4..ecfc449 100644 --- a/web/src/components/Part2Cell/Part2Cell.js +++ b/web/src/components/Part2Cell/Part2Cell.js @@ -5,7 +5,11 @@ import { useAuth } from '@redwoodjs/auth' import PartProfile from 'src/components/PartProfile' export const QUERY = gql` - query FIND_PART_BY_USERNAME_TITLE($userName: String!, $partTitle: String, $currentUserId: String) { + query FIND_PART_BY_USERNAME_TITLE( + $userName: String! + $partTitle: String + $currentUserId: String + ) { userPart: userName(userName: $userName) { id name @@ -42,7 +46,7 @@ export const QUERY = gql` const UPDATE_PART_MUTATION = gql` mutation UpdatePartMutation($id: String!, $input: UpdatePartInput!) { - updatePart:updatePart(id: $id, input: $input) { + updatePart: updatePart(id: $id, input: $input) { id title user { @@ -66,7 +70,7 @@ const CREATE_PART_MUTATION = gql` ` const TOGGLE_REACTION_MUTATION = gql` mutation ToggleReactionMutation($input: TogglePartReactionInput!) { - togglePartReaction(input: $input){ + togglePartReaction(input: $input) { id emote } @@ -87,23 +91,33 @@ export const Empty = () =>
Empty
export const Failure = ({ error }) =>
Error: {error.message}
-export const Success = ({ userPart, variables: {isEditable}, refetch}) => { +export const Success = ({ userPart, variables: { isEditable }, refetch }) => { const { currentUser } = useAuth() const { addMessage } = useFlash() const [updateUser, { loading, error }] = useMutation(UPDATE_PART_MUTATION, { - onCompleted: ({updatePart}) => { - navigate(routes.part2({userName: updatePart.user.userName, partTitle: updatePart.title})) + onCompleted: ({ updatePart }) => { + navigate( + routes.part2({ + userName: updatePart.user.userName, + partTitle: updatePart.title, + }) + ) addMessage('Part updated.', { classes: 'rw-flash-success' }) }, }) const [createUser] = useMutation(CREATE_PART_MUTATION, { - onCompleted: ({createPart}) => { - navigate(routes.part2({userName: createPart?.user?.userName, partTitle: createPart?.title})) + onCompleted: ({ createPart }) => { + navigate( + routes.part2({ + userName: createPart?.user?.userName, + partTitle: createPart?.title, + }) + ) addMessage('Part Created.', { classes: 'rw-flash-success' }) }, }) const onSave = (id, input) => { - if(!id) { + if (!id) { createUser({ variables: { input } }) return } @@ -111,30 +125,42 @@ export const Success = ({ userPart, variables: {isEditable}, refetch}) => { } const [toggleReaction] = useMutation(TOGGLE_REACTION_MUTATION, { - onCompleted: () => refetch() + onCompleted: () => refetch(), }) - const onReaction = (emote) => toggleReaction({variables: {input: { - emote, - userId: currentUser.sub, - partId: userPart?.Part?.id, - }}}) + const onReaction = (emote) => + toggleReaction({ + variables: { + input: { + emote, + userId: currentUser.sub, + partId: userPart?.Part?.id, + }, + }, + }) const [createComment] = useMutation(CREATE_COMMENT_MUTATION, { - onCompleted: () => refetch() + onCompleted: () => refetch(), }) - const onComment = (text) => createComment({variables: {input: { - text, - userId: currentUser.sub, - partId: userPart?.Part?.id, - }}}) + const onComment = (text) => + createComment({ + variables: { + input: { + text, + userId: currentUser.sub, + partId: userPart?.Part?.id, + }, + }, + }) - return + return ( + + ) } diff --git a/web/src/components/PartProfile/PartProfile.js b/web/src/components/PartProfile/PartProfile.js index 9a7facc..00beef0 100644 --- a/web/src/components/PartProfile/PartProfile.js +++ b/web/src/components/PartProfile/PartProfile.js @@ -1,58 +1,69 @@ -import {useState, useEffect} from 'react' +import { useState, useEffect } from 'react' import { useAuth } from '@redwoodjs/auth' import { Link, navigate, routes } from '@redwoodjs/router' -import Editor from "rich-markdown-editor"; +import Editor from 'rich-markdown-editor' import ImageUploader from 'src/components/ImageUploader' import Breadcrumb from 'src/components/Breadcrumb' import EmojiReaction from 'src/components/EmojiReaction' import Button from 'src/components/Button' import { countEmotes } from 'src/helpers/emote' -import { getActiveClasses } from 'get-active-classes'; +import { getActiveClasses } from 'get-active-classes' const PartProfile = ({ - userPart, - isEditable, - onSave, - loading, - error, - onReaction, - onComment, - }) => { + userPart, + isEditable, + onSave, + loading, + error, + onReaction, + onComment, +}) => { const [comment, setComment] = useState('') const { currentUser } = useAuth() const canEdit = currentUser?.sub === userPart.id const part = userPart?.Part const emotes = countEmotes(part?.Reaction) - const userEmotes = part?.userReactions.map(({emote}) => emote) - useEffect(() => {isEditable && - !canEdit && - navigate(routes.part2({userName: userPart.userName, partTitle: part.title}))}, - [currentUser]) + const userEmotes = part?.userReactions.map(({ emote }) => emote) + useEffect(() => { + isEditable && + !canEdit && + navigate( + routes.part2({ userName: userPart.userName, partTitle: part.title }) + ) + }, [currentUser]) const [input, setInput] = useState({ title: part?.title, mainImage: part?.mainImage, description: part?.description, userId: userPart?.id, }) - const setProperty = (property, value) => setInput({ - ...input, - [property]: value, - }) - const onTitleChange = ({target}) => setProperty('title', target.value.replace(/([^a-zA-Z\d_:])/g, '-')) - const onDescriptionChange = (description) => setProperty('description', description()) - const onImageUpload = ({cloudinaryPublicId}) => setProperty('mainImage', cloudinaryPublicId) + const setProperty = (property, value) => + setInput({ + ...input, + [property]: value, + }) + const onTitleChange = ({ target }) => + setProperty('title', target.value.replace(/([^a-zA-Z\d_:])/g, '-')) + const onDescriptionChange = (description) => + setProperty('description', description()) + const onImageUpload = ({ cloudinaryPublicId }) => + setProperty('mainImage', cloudinaryPublicId) const onEditSaveClick = () => { if (isEditable) { input.title && onSave(part?.id, input) return } - navigate(routes.editPart2({userName: userPart.userName, partTitle: part.title})) + navigate( + routes.editPart2({ userName: userPart.userName, partTitle: part.title }) + ) } return ( <> -
- +
{/* Side column */} {/* main project center column */}
- - { !!(input?.mainImage || isEditable) && } -
+ + {!!(input?.mainImage || isEditable) && ( + + )} +
- - {/* comments */} - { !isEditable && <> -
-

Comments

+ {!isEditable && ( + <> +
+

+ Comments +

-
    - {part?.Comment.map(({text, user, id}) => ( -
  • -
    - {}} - aspectRatio={1} - imageUrl={user?.image} - width={50} +
      + {part?.Comment.map(({ text, user, id }) => ( +
    • +
      + {}} + aspectRatio={1} + imageUrl={user?.image} + width={50} + /> +
      +
      +
      + + {user.userName} + +
      +
      + {text} +
      +
      +
    • + ))} +
    + {currentUser && ( + <> +
    +