props.onChange(option)}>
+
props.onChange(option)}
+ >
{option.title}
)}
diff --git a/src/components/_shared/Popup/Popup.module.scss b/src/components/_shared/Popup/Popup.module.scss
index f69995be..1d0c7d80 100644
--- a/src/components/_shared/Popup/Popup.module.scss
+++ b/src/components/_shared/Popup/Popup.module.scss
@@ -15,7 +15,7 @@
position: absolute;
text-align: left;
top: calc(100% + 8px);
- z-index: 100;
+ z-index: 101;
ul {
margin-bottom: 0;
@@ -103,23 +103,6 @@
vertical-align: middle;
}
}
-
- .shareControl {
- text-align: left;
- transition:
- color 0.3s,
- background-color 0.3s;
- white-space: nowrap;
-
- &:hover {
- background: #000;
- color: #fff;
-
- .icon img {
- filter: invert(0);
- }
- }
- }
}
// TODO: animation
diff --git a/src/components/_shared/ShareLinks/ShareLinks.module.scss b/src/components/_shared/ShareLinks/ShareLinks.module.scss
new file mode 100644
index 00000000..d65ee4aa
--- /dev/null
+++ b/src/components/_shared/ShareLinks/ShareLinks.module.scss
@@ -0,0 +1,82 @@
+.ShareLinks {
+ .shareControl {
+ text-align: left;
+ transition:
+ color 0.3s,
+ background-color 0.3s;
+ white-space: nowrap;
+
+ &:hover {
+ background: #000;
+ color: #fff;
+
+ .icon img {
+ filter: invert(0);
+ }
+ }
+
+ .icon {
+ display: inline-block;
+ width: 3.6rem;
+
+ img {
+ display: inline-block;
+ filter: invert(1);
+ max-height: 2rem;
+ max-width: 2rem;
+ transition: filter 0.3s;
+ vertical-align: middle;
+ }
+ }
+ }
+
+ &.inModal {
+ li {
+ margin: 0;
+ }
+
+ .shareControl {
+ font-size: 18px;
+ font-weight: 600;
+ padding: 1rem;
+ margin: 0 -12px;
+ width: calc(100% + 24px);
+ transition: unset;
+ }
+ }
+
+ .linkInput {
+ position: relative;
+ margin-top: 2rem;
+ margin-bottom: -2rem !important;
+
+ input {
+ margin-bottom: 0;
+ padding-right: 40px;
+ box-sizing: border-box;
+ }
+
+ .copyButton {
+ position: absolute;
+ top: 2px;
+ bottom: 2px;
+ right: 2px;
+ width: 40px;
+ padding: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ }
+ }
+
+ .isCopied {
+ @include font-size(1.6rem);
+
+ position: absolute;
+ top: 100%;
+ left: 1.2rem;
+ margin-top: 0.5rem;
+ color: var(--blue-500);
+ }
+}
diff --git a/src/components/_shared/ShareLinks/ShareLinks.tsx b/src/components/_shared/ShareLinks/ShareLinks.tsx
new file mode 100644
index 00000000..b27c24c6
--- /dev/null
+++ b/src/components/_shared/ShareLinks/ShareLinks.tsx
@@ -0,0 +1,101 @@
+import { createSocialShare, FACEBOOK, TELEGRAM, TWITTER, VK } from '@solid-primitives/share'
+import { clsx } from 'clsx'
+import { createSignal, Show } from 'solid-js'
+
+import { useLocalize } from '../../../context/localize'
+import { useSnackbar } from '../../../context/snackbar'
+import { Icon } from '../Icon'
+import { Popover } from '../Popover'
+
+import styles from './ShareLinks.module.scss'
+
+type Props = {
+ title: string
+ description: string
+ shareUrl: string
+ imageUrl?: string
+ class?: string
+ variant: 'inModal' | 'inPopup'
+}
+
+export const ShareLinks = (props: Props) => {
+ const { t } = useLocalize()
+ const [isLinkCopied, setIsLinkCopied] = createSignal(false)
+ const {
+ actions: { showSnackbar },
+ } = useSnackbar()
+
+ const [share] = createSocialShare(() => ({
+ title: props.title,
+ url: props.shareUrl,
+ description: props.description,
+ }))
+ const copyLink = async () => {
+ await navigator.clipboard.writeText(props.shareUrl)
+ if (props.variant === 'inModal') {
+ setIsLinkCopied(true)
+ setTimeout(() => setIsLinkCopied(false), 3000)
+ } else {
+ showSnackbar({ body: t('Link copied') })
+ }
+ }
+
+ return (
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ {t('Copy link')}
+
+ }
+ >
+
+
+
+
+
+ )
+}
diff --git a/src/components/_shared/ShareLinks/index.ts b/src/components/_shared/ShareLinks/index.ts
new file mode 100644
index 00000000..0d99048a
--- /dev/null
+++ b/src/components/_shared/ShareLinks/index.ts
@@ -0,0 +1 @@
+export { ShareLinks } from './ShareLinks'
diff --git a/src/components/_shared/ShareModal/ShareModal.tsx b/src/components/_shared/ShareModal/ShareModal.tsx
new file mode 100644
index 00000000..0d9c9427
--- /dev/null
+++ b/src/components/_shared/ShareModal/ShareModal.tsx
@@ -0,0 +1,27 @@
+import { useLocalize } from '../../../context/localize'
+import { Modal } from '../../Nav/Modal'
+import { ShareLinks } from '../ShareLinks'
+
+type Props = {
+ modalTitle?: string
+ shareUrl?: string
+ title: string
+ imageUrl: string
+ description: string
+}
+export const ShareModal = (props: Props) => {
+ const { t } = useLocalize()
+
+ return (
+
+ {t('Share publication')}
+
+
+ )
+}
diff --git a/src/components/_shared/ShareModal/index.ts b/src/components/_shared/ShareModal/index.ts
new file mode 100644
index 00000000..6a586378
--- /dev/null
+++ b/src/components/_shared/ShareModal/index.ts
@@ -0,0 +1 @@
+export { ShareModal } from './ShareModal'
diff --git a/src/stores/ui.ts b/src/stores/ui.ts
index fd6e93be..551fe226 100644
--- a/src/stores/ui.ts
+++ b/src/stores/ui.ts
@@ -25,6 +25,7 @@ export type ModalType =
| 'following'
| 'search'
| 'inviteCoAuthors'
+ | 'share'
export const MODALS: Record
= {
auth: 'auth',
@@ -42,6 +43,7 @@ export const MODALS: Record = {
following: 'following',
inviteCoAuthors: 'inviteCoAuthors',
search: 'search',
+ share: 'share',
}
const [modal, setModal] = createSignal(null)
diff --git a/src/utils/getImageUrl.ts b/src/utils/getImageUrl.ts
index d91e23db..eb01be07 100644
--- a/src/utils/getImageUrl.ts
+++ b/src/utils/getImageUrl.ts
@@ -1,5 +1,7 @@
import { thumborUrl } from './config'
+const thumborPrefix = `${thumborUrl}/unsafe/`
+
const getSizeUrlPart = (options: { width?: number; height?: number } = {}) => {
const widthString = options.width ? options.width.toString() : ''
const heightString = options.height ? options.height.toString() : ''
@@ -19,3 +21,27 @@ export const getImageUrl = (src: string, options: { width?: number; height?: num
`${thumborUrl}/unsafe/${sizeUrlPart}${sourceUrl}`.replace('https://', '').replace('//', '/')
)
}
+
+export const getOpenGraphImageUrl = (
+ src: string,
+ options: {
+ topic: string
+ title: string
+ author: string
+ width?: number
+ height?: number
+ },
+) => {
+ const sizeUrlPart = getSizeUrlPart(options)
+
+ const filtersPart = `filters:discourstext('${encodeURIComponent(options.topic)}','${encodeURIComponent(
+ options.author,
+ )}','${encodeURIComponent(options.title)}')/`
+
+ if (src.startsWith(thumborPrefix)) {
+ const thumborKey = src.replace(thumborPrefix, '')
+ return `${thumborUrl}/unsafe/${sizeUrlPart}${filtersPart}${thumborKey}`
+ }
+
+ return `${thumborUrl}/unsafe/${sizeUrlPart}${filtersPart}${src}`
+}