diff --git a/src/components/Author/AuthorCard/AuthorCard.tsx b/src/components/Author/AuthorCard/AuthorCard.tsx
index 713605de..395498a3 100644
--- a/src/components/Author/AuthorCard/AuthorCard.tsx
+++ b/src/components/Author/AuthorCard/AuthorCard.tsx
@@ -193,12 +193,13 @@ export const AuthorCard = (props: Props) => {
{name()}
- {/*TODO: implement plurals by i18n*/}
{props.author.stat?.shouts} публикаций
+
+ {t('PublicationsWithCount', { count: props.author.stat?.shouts ?? 0 })}
+
) : (
''
)
diff --git a/src/components/Editor/Editor.tsx b/src/components/Editor/Editor.tsx
index f31f4e1f..817a570b 100644
--- a/src/components/Editor/Editor.tsx
+++ b/src/components/Editor/Editor.tsx
@@ -44,6 +44,9 @@ import { EditorFloatingMenu } from './EditorFloatingMenu'
import './Prosemirror.scss'
import { Image } from '@tiptap/extension-image'
import { Footnote } from './extensions/Footnote'
+import { handleFileUpload } from '../../utils/handleFileUpload'
+import { imageProxy } from '../../utils/imageProxy'
+import { useSnackbar } from '../../context/snackbar'
type Props = {
shoutId: number
@@ -51,6 +54,17 @@ type Props = {
onChange: (text: string) => void
}
+const allowedImageTypes = new Set([
+ 'image/bmp',
+ 'image/gif',
+ 'image/jpeg',
+ 'image/jpg',
+ 'image/png',
+ 'image/tiff',
+ 'image/webp',
+ 'image/x-icon'
+])
+
const yDocs: Record = {}
const providers: Record = {}
@@ -61,6 +75,10 @@ export const Editor = (props: Props) => {
const [isCommonMarkup, setIsCommonMarkup] = createSignal(false)
const [shouldShowTextBubbleMenu, setShouldShowTextBubbleMenu] = createSignal(false)
+ const {
+ actions: { showSnackbar }
+ } = useSnackbar()
+
const docName = `shout-${props.shoutId}`
if (!yDocs[docName]) {
@@ -114,12 +132,73 @@ export const Editor = (props: Props) => {
content: 'figcaption image'
})
+ const handleClipboardPaste = async () => {
+ try {
+ const clipboardItems = await navigator.clipboard.read()
+
+ if (clipboardItems.length === 0) return
+ const [clipboardItem] = clipboardItems
+ const { types } = clipboardItem
+ const imageType = types.find((type) => allowedImageTypes.has(type))
+
+ if (!imageType) return
+ const blob = await clipboardItem.getType(imageType)
+ const extension = imageType.split('/')[1]
+ const file = new File([blob], `clipboardImage.${extension}`)
+
+ const uplFile = {
+ source: blob.toString(),
+ name: file.name,
+ size: file.size,
+ file
+ }
+
+ showSnackbar({ body: t('Uploading image') })
+ const result = await handleFileUpload(uplFile)
+
+ editor()
+ .chain()
+ .focus()
+ .insertContent({
+ type: 'capturedImage',
+ content: [
+ {
+ type: 'figcaption',
+ content: [
+ {
+ type: 'text',
+ text: result.originalFilename
+ }
+ ]
+ },
+ {
+ type: 'image',
+ attrs: {
+ src: imageProxy(result.url)
+ }
+ }
+ ]
+ })
+ .run()
+ } catch (error) {
+ console.error('[Paste Image Error]:', error)
+ }
+ }
+
const { initialContent } = props
+
const editor = createTiptapEditor(() => ({
element: editorElRef.current,
editorProps: {
attributes: {
class: 'articleEditor'
+ },
+ transformPastedHTML(html) {
+ return html.replaceAll(//g, '')
+ },
+ handlePaste: () => {
+ handleClipboardPaste()
+ return false
}
},
extensions: [
@@ -246,6 +325,7 @@ export const Editor = (props: Props) => {
TrailingNode,
Article
],
+ enablePasteRules: [Link],
content: initialContent ?? null
}))
diff --git a/src/components/Topic/TopicBadge/TopicBadge.tsx b/src/components/Topic/TopicBadge/TopicBadge.tsx
index ee43e986..3d958645 100644
--- a/src/components/Topic/TopicBadge/TopicBadge.tsx
+++ b/src/components/Topic/TopicBadge/TopicBadge.tsx
@@ -56,7 +56,7 @@ export const TopicBadge = (props: Props) => {
when={props.topic.body}
fallback={
- {props.topic.stat.shouts ?? 0} {t('Publications')}
+ {t('PublicationsWithCount', { count: props.topic.stat.shouts ?? 0 })}
}
>
diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx
index cd479d8f..73b15285 100644
--- a/src/components/Views/Author/Author.tsx
+++ b/src/components/Views/Author/Author.tsx
@@ -35,8 +35,6 @@ export const AuthorView = (props: Props) => {
const { page } = useRouter()
const author = createMemo(() => authorEntities()[props.authorSlug])
-
- console.log('!!! author:', author())
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const [isBioExpanded, setIsBioExpanded] = createSignal(false)
const [followers, setFollowers] = createSignal([])
diff --git a/src/pages/profile/profileSettings.page.tsx b/src/pages/profile/profileSettings.page.tsx
index 02326938..eddb61d0 100644
--- a/src/pages/profile/profileSettings.page.tsx
+++ b/src/pages/profile/profileSettings.page.tsx
@@ -123,7 +123,7 @@ export const ProfileSettingsPage = () => {
{t('Here you can customize your profile the way you want.')}