Card upload

This commit is contained in:
ilya-bkv 2023-05-09 07:58:00 +03:00
parent 39c99091f9
commit 2ea3216bff
10 changed files with 84 additions and 28 deletions

View File

@ -4,6 +4,7 @@
"About the project": "About the project",
"Add comment": "Comment",
"Add image": "Add image",
"Add another image": "Add another image",
"Address on Discourse": "Address on Discourse",
"All": "All",
"All authors": "All authors",

View File

@ -5,6 +5,7 @@
"About the project": "О проекте",
"Add comment": "Комментировать",
"Add image": "Добавить изображение",
"Add another image": "Добавить другое изображение",
"Add to bookmarks": "Добавить в закладки",
"Address on Discourse": "Адрес на Дискурсе",
"All": "Все",

View File

@ -8,9 +8,10 @@ import { useLocalize } from '../../../context/localize'
import { Modal } from '../../Nav/Modal'
import { Menu } from './Menu'
import type { MenuItem } from './Menu/Menu'
import { showModal } from '../../../stores/ui'
import { UploadModalContent } from '../UploadModal'
import { hideModal, showModal } from '../../../stores/ui'
import { UploadModalContent } from '../UploadModalContent'
import { useOutsideClickHandler } from '../../../utils/useOutsideClickHandler'
import { imageProxy } from '../../../utils/imageProxy'
type FloatingMenuProps = {
editor: Editor
@ -35,6 +36,7 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
const { t } = useLocalize()
const [selectedMenuItem, setSelectedMenuItem] = createSignal<MenuItem | undefined>()
const [menuOpen, setMenuOpen] = createSignal<boolean>(false)
const menuRef: { current: HTMLDivElement } = { current: null }
const handleEmbedFormSubmit = async (value: string) => {
// TODO: add support instagram embed (blockquote)
const emb = await embedData(value)
@ -58,8 +60,6 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
setMenuOpen(false)
}
const menuRef: { current: HTMLDivElement } = { current: null }
useOutsideClickHandler({
containerRef: menuRef,
handler: () => {
@ -69,6 +69,15 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
}
})
const renderImage = (src: string) => {
props.editor
.chain()
.focus()
.setImage({ src: imageProxy(src) })
.run()
hideModal()
}
return (
<>
<div ref={props.ref} class={styles.editorFloatingMenu}>
@ -100,7 +109,12 @@ export const EditorFloatingMenu = (props: FloatingMenuProps) => {
</Show>
</div>
<Modal variant="narrow" name="uploadImage" onClose={closeUploadModalHandler}>
<UploadModalContent closeCallback={() => setSelectedMenuItem()} editor={props.editor} />
<UploadModalContent
closeCallback={(value) => {
renderImage(value)
setSelectedMenuItem()
}}
/>
</Modal>
</>
)

View File

@ -33,6 +33,9 @@ export const InlineForm = (props: Props) => {
} else {
setFormValueError(props.errorMessage)
}
} else {
props.onSubmit(formValue())
props.onClose()
}
}

View File

@ -14,8 +14,7 @@ import { verifyImg } from '../../../utils/verifyImg'
import { imageProxy } from '../../../utils/imageProxy'
type Props = {
editor: Editor
closeCallback: () => void
closeCallback: (imgUrl?: string) => void
}
export const UploadModalContent = (props: Props) => {
@ -25,27 +24,17 @@ export const UploadModalContent = (props: Props) => {
const [dragActive, setDragActive] = createSignal(false)
const [dragError, setDragError] = createSignal<string | undefined>()
const renderImage = (src: string) => {
props.editor
.chain()
.focus()
.setImage({ src: imageProxy(src) })
.run()
hideModal()
}
const { selectFiles } = createFileUploader({ multiple: false, accept: 'image/*' })
const runUpload = async (file) => {
try {
setIsUploading(true)
const fileUrl = await handleFileUpload(file)
setIsUploading(false)
props.closeCallback()
renderImage(fileUrl)
props.closeCallback(fileUrl)
} catch (error) {
console.error('[upload image] error', error)
setIsUploading(false)
setUploadError(t('Error'))
console.error('[runUpload]', error)
}
}
@ -53,7 +42,7 @@ export const UploadModalContent = (props: Props) => {
try {
const data = await fetch(value)
const blob = await data.blob()
const file = new File([blob], 'convertedFromUrl', { type: data.headers.get('Content-Type') })
const file = await new File([blob], 'convertedFromUrl', { type: data.headers.get('Content-Type') })
const fileToUpload: UploadFile = {
source: blob.toString(),
name: file.name,
@ -126,7 +115,7 @@ export const UploadModalContent = (props: Props) => {
hideModal()
props.closeCallback()
}}
validate={(value) => verifyImg(value)}
// validate={(value) => verifyImg(value)}
onSubmit={handleImageFormSubmit}
errorMessage={t('Invalid image link')}
/>

View File

@ -1,3 +1,4 @@
export { Editor } from './Editor'
export { Panel } from './Panel'
export { TopicSelect } from './TopicSelect'
export { UploadModalContent } from './UploadModalContent'

View File

@ -11,6 +11,32 @@
align-items: flex-start;
box-sizing: border-box;
.shoutCardCoverContainer {
position: relative;
width: 100%;
}
.shoutCardCover {
height: 0;
margin-bottom: 1.6rem;
overflow: hidden;
padding-bottom: 56.2%;
position: relative;
img {
height: 100%;
object-fit: cover;
position: absolute;
transform-origin: 50% 50%;
transition: transform 1s ease-in-out;
width: 100%;
}
&:hover img {
transform: scale(1.1);
}
}
.shoutCardTitle {
@include font-size(2.2rem);
@ -89,7 +115,7 @@
.close {
filter: invert(1);
margin: -1.6rem 0 0 -1.6rem;
margin: -1.6rem 0 0 -2.8rem;
}
section {

View File

@ -6,11 +6,13 @@ import type { Shout, Topic } from '../../graphql/types.gen'
import { apiClient } from '../../utils/apiClient'
import { useRouter } from '../../stores/router'
import { useEditorContext } from '../../context/editor'
import { Editor, Panel, TopicSelect } from '../Editor'
import { Editor, Panel, TopicSelect, UploadModalContent } from '../Editor'
import { Icon } from '../_shared/Icon'
import { Button } from '../_shared/Button'
import styles from './Edit.module.scss'
import { useSession } from '../../context/session'
import { Modal } from '../Nav/Modal'
import { hideModal, showModal } from '../../stores/ui'
type EditViewProps = {
shout: Shout
@ -22,14 +24,13 @@ export const EditView = (props: EditViewProps) => {
const [isScrolled, setIsScrolled] = createSignal(false)
const [topics, setTopics] = createSignal<Topic[]>(null)
const [coverImage, setCoverImage] = createSignal<string>(null)
const { page } = useRouter()
const {
form,
formErrors,
actions: { setForm, setFormErrors }
} = useEditorContext()
const [isSlugChanged, setIsSlugChanged] = createSignal(false)
setForm({
@ -88,7 +89,14 @@ export const EditView = (props: EditViewProps) => {
behavior: 'smooth'
})
}
console.log('!!! :')
const handleSetCover = (imgUrl: string) => {
hideModal()
console.log('!!! imgUrl:', imgUrl)
setCoverImage(imgUrl)
setForm('coverImageUrl', imgUrl)
}
return (
<>
<button
@ -223,8 +231,18 @@ export const EditView = (props: EditViewProps) => {
)}
</p>
<div class={styles.articlePreview}>
<Button variant="primary" onClick={() => ''} value={t('Add image')} />
<Button
variant="primary"
onClick={() => showModal('uploadImage')}
value={coverImage() ? t('Add another image') : t('Add image')}
/>
<Show when={coverImage() ?? form.coverImageUrl}>
<div class={styles.shoutCardCoverContainer}>
<div class={styles.shoutCardCover}>
<img src={coverImage() || form.coverImageUrl} alt={form.title} loading="lazy" />
</div>
</div>
</Show>
<div class={styles.shoutCardTitle}>{form.title}</div>
<div class={styles.shoutCardSubtitle}>{form.subtitle}</div>
<div class={styles.shoutAuthor}>{user().name}</div>
@ -235,6 +253,9 @@ export const EditView = (props: EditViewProps) => {
</div>
</form>
</div>
<Modal variant="narrow" name="uploadImage">
<UploadModalContent closeCallback={(value) => handleSetCover(value)} />
</Modal>
<Panel shoutSlug={props.shout.slug} />
</>
)