// Dependencies import orderBy from 'lodash/orderBy.js' import { cloudflareImageUrl, capitalize, shortDate, horFlexClasses, newPatternUrl, patternUrlFromState, } from '@freesewing/utils' import { urls, control as controlConfig } from '@freesewing/config' // Context import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus' import { ModalContext } from '@freesewing/react/context/Modal' // Hooks import React, { useState, useEffect, useContext } from 'react' import { useAccount } from '@freesewing/react/hooks/useAccount' import { useBackend } from '@freesewing/react/hooks/useBackend' import { useSelection } from '@freesewing/react/hooks/useSelection' // Components import Markdown from 'react-markdown' import { StringInput, MarkdownInput, PassiveImageInput, ListInput, } from '@freesewing/react/components/Input' import { Link as WebLink, AnchorLink } from '@freesewing/react/components/Link' import { BoolNoIcon, BoolYesIcon, CloneIcon, EditIcon, FreeSewingIcon, OkIcon, NoIcon, PatternIcon, ShowcaseIcon, ResetIcon, UploadIcon, } from '@freesewing/react/components/Icon' import { DisplayRow } from './shared.mjs' import { TimeAgo } from '@freesewing/react/components/Time' import { Popout } from '@freesewing/react/components/Popout' import { ModalWrapper } from '@freesewing/react/components/Modal' import { KeyVal } from '@freesewing/react/components/KeyVal' export const Pattern = ({ id, Link }) => { if (!Link) Link = WebLink // Hooks const { account, control } = useAccount() const { setLoadingStatus } = useContext(LoadingStatusContext) const backend = useBackend() // Context const { setModal } = useContext(ModalContext) const [edit, setEdit] = useState(false) const [pattern, setPattern] = useState() // Set fields for editing const [name, setName] = useState(pattern?.name) const [image, setImage] = useState(pattern?.image) const [isPublic, setIsPublic] = useState(pattern?.public ? true : false) const [notes, setNotes] = useState(pattern?.notes || '') // Effect useEffect(() => { const getPattern = async () => { setLoadingStatus([true, 'Loading pattern from backend']) const [status, body] = await backend.getPattern(id) if (status === 200) { setPattern(body.pattern) setName(body.pattern.name) setImage(body.pattern.image) setIsPublic(body.pattern.public ? true : false) setNotes(body.pattern.notes) setLoadingStatus([true, 'Loaded pattern', true, true]) } else setLoadingStatus([true, 'An error occured. Please report this', true, false]) } if (id) getPattern() }, [id]) const save = async () => { setLoadingStatus([true, 'Gathering info']) // Compile data const data = {} if (name || name !== pattern.name) data.name = name if (image || image !== pattern.image) data.img = image if (notes || notes !== pattern.notes) data.notes = notes if ([true, false].includes(isPublic) && isPublic !== pattern.public) data.public = isPublic setLoadingStatus([true, 'Saving pattern']) const [status, body] = await backend.updatePattern(pattern.id, data) if (status === 200 && body.result === 'success') { setPattern(body.pattern) setEdit(false) setLoadingStatus([true, 'Nailed it', true, true]) } else setLoadingStatus([true, 'An error occured. Please report this.', true, false]) } const clone = async () => { setLoadingStatus([true, 'Cloning pattern']) // Compile data const data = { ...pattern } delete data.id delete data.createdAt delete data.data delete data.userId delete data.img data.settings = JSON.parse(data.settings) const [status, body] = await backend.createPattern(data) if (status === 201 && body.result === 'created') { setLoadingStatus([true, 'Loading newly created pattern', true, true]) window.location = `/account/data/patterns/pattern?id=${body.pattern.id}` } else setLoadingStatus([true, 'We failed to create this pattern', true, false]) } const togglePublic = async () => { setLoadingStatus([true, 'Updating pattern']) // Compile data const data = { public: !pattern.public } const [status, body] = await backend.updatePattern(pattern.id, data) if (status === 200 && body.result === 'success') { setPattern(body.pattern) setLoadingStatus([true, 'Nailed it', true, true]) } else setLoadingStatus([true, 'An error occured. Please report this.', true, false]) } if (!pattern) return null const header = ( ) if (!edit) return (
{pattern.public ? (
This is the private view of your pattern

Everyone can access the public view since this is a public pattern.
But only you can access this private view.

Public View

) : null} {header} {control >= controlConfig.account.patterns.notes && ( <>

Notes

{pattern.notes} )}
) return (

Edit pattern {pattern.name}

{/* Name is always shown */} val && val.length > 0} /> {/* img: Control level determines whether or not to show this */} {account.control >= controlConfig.account.sets.img ? ( val.length > 0} /> ) : null} {/* public: Control level determines whether or not to show this */} {account.control >= controlConfig.account.patterns.public ? ( Public Pattern
), desc: 'Public patterns can be shared with other FreeSewing users', }, { val: false, label: (
Private Pattern
), desc: 'Private patterns are yours and yours alone', }, ]} current={isPublic} /> ) : null} {/* notes: Control level determines whether or not to show this */} {account.control >= controlConfig.account.patterns.notes ? ( ) : null}
) } export const PatternCard = ({ pattern, href = false, onClick = false, useA = false, size = 'md', Link = false, }) => { if (!Link) Link = WebLink const sizes = { lg: 96, md: 52, sm: 36, xs: 20, } const s = sizes[size] const wrapperProps = { className: `tw:bg-base-300 tw:w-full tw:mb-2 tw:mx-auto tw:flex tw:flex-col tw:items-start tw:text-center tw:justify-center tw:rounded tw:shadow tw:py-4 tw:w-${s} tw:aspect-square`, style: { backgroundImage: `url(${cloudflareImageUrl({ type: 'w1000', id: pattern.img })})`, backgroundSize: 'cover', backgroundRepeat: 'no-repeat', backgroundPosition: '50%', }, } if (pattern.img === 'default-avatar') wrapperProps.style.backgroundPosition = 'bottom right' const inner = null // Is it a button with an onClick handler? if (onClick) return ( ) // Returns a link to an internal page if (href && !useA) return ( {inner} ) // Returns a link to an external page if (href && useA) return ( {inner} ) // Returns a div return
{inner}
} const BadgeLink = ({ label, href }) => ( {label} ) /** * Helper component to show the pattern title, image, and various buttons */ const PatternHeader = ({ pattern, Link, account, setModal, setEdit, togglePublic, save, clone, }) => ( <>

{pattern.name}

} color="secondary" /> } color="secondary" />
{account.control > 3 && (pattern?.public || pattern.userId === account.id) ? (
) : ( )} {account.control > 3 ? ( ) : null} {pattern.userId === account.id && ( <> Update Pattern )}
)