Card upload
This commit is contained in:
parent
39c99091f9
commit
2ea3216bff
|
@ -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",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"About the project": "О проекте",
|
||||
"Add comment": "Комментировать",
|
||||
"Add image": "Добавить изображение",
|
||||
"Add another image": "Добавить другое изображение",
|
||||
"Add to bookmarks": "Добавить в закладки",
|
||||
"Address on Discourse": "Адрес на Дискурсе",
|
||||
"All": "Все",
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -33,6 +33,9 @@ export const InlineForm = (props: Props) => {
|
|||
} else {
|
||||
setFormValueError(props.errorMessage)
|
||||
}
|
||||
} else {
|
||||
props.onSubmit(formValue())
|
||||
props.onClose()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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')}
|
||||
/>
|
|
@ -1,3 +1,4 @@
|
|||
export { Editor } from './Editor'
|
||||
export { Panel } from './Panel'
|
||||
export { TopicSelect } from './TopicSelect'
|
||||
export { UploadModalContent } from './UploadModalContent'
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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} />
|
||||
</>
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user