mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-03-01 12:44:17 +00:00
refactored share-button
This commit is contained in:
parent
ef6643e0f3
commit
afc5b2ae97
@ -1,20 +1,12 @@
|
||||
import { QrCodeIcon, ShareIcon } from '@heroicons/react/24/solid'
|
||||
import { QrCodeIcon } from '@heroicons/react/24/solid'
|
||||
import { LuNavigation } from 'react-icons/lu'
|
||||
|
||||
import ChevronSVG from '#assets/chevron.svg'
|
||||
import ClipboardSVG from '#assets/share/clipboard.svg'
|
||||
import FacebookSVG from '#assets/share/facebook.svg'
|
||||
import LinkedinSVG from '#assets/share/linkedin.svg'
|
||||
import TelegramSVG from '#assets/share/telegram.svg'
|
||||
import TwitterSVG from '#assets/share/twitter.svg'
|
||||
import WhatsappSVG from '#assets/share/whatsapp.svg'
|
||||
import XingSVG from '#assets/share/xing.svg'
|
||||
import { useMyProfile } from '#components/Map/hooks/useMyProfile'
|
||||
|
||||
import { useNavigationUrl, useShareLogic } from './hooks'
|
||||
import { useNavigationUrl } from './hooks'
|
||||
import { ShareButton } from './ShareButton'
|
||||
|
||||
import type { Item } from '#types/Item'
|
||||
import type { PlatformConfig, SharePlatformConfigs } from './types'
|
||||
|
||||
interface ActionButtonsProps {
|
||||
item: Item
|
||||
@ -26,46 +18,6 @@ export function ActionButtons({ item, onQrModalOpen }: ActionButtonsProps) {
|
||||
const { getNavigationUrl, isMobile, isIOS } = useNavigationUrl(
|
||||
item.position?.coordinates as [number, number] | undefined,
|
||||
)
|
||||
const { shareUrl, shareTitle, copyLink, getShareUrl } = useShareLogic(item)
|
||||
|
||||
const platformConfigs: SharePlatformConfigs = {
|
||||
facebook: {
|
||||
shareUrl: 'https://www.facebook.com/sharer/sharer.php?u={url}',
|
||||
icon: <img src={FacebookSVG} alt='Facebook' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Facebook',
|
||||
bgColor: '#3b5998',
|
||||
},
|
||||
twitter: {
|
||||
shareUrl: 'https://twitter.com/intent/tweet?text={title}:%20{url}',
|
||||
icon: <img src={TwitterSVG} alt='Twitter' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Twitter',
|
||||
bgColor: '#55acee',
|
||||
},
|
||||
linkedin: {
|
||||
shareUrl: 'http://www.linkedin.com/shareArticle?mini=true&url={url}&title={title}',
|
||||
icon: <img src={LinkedinSVG} alt='Linkedin' className='tw:w-4 tw:h-4' />,
|
||||
label: 'LinkedIn',
|
||||
bgColor: '#4875b4',
|
||||
},
|
||||
whatsapp: {
|
||||
shareUrl: 'https://api.whatsapp.com/send?text={title}%20{url}',
|
||||
icon: <img src={WhatsappSVG} alt='Whatsapp' className='tw:w-4 tw:h-4' />,
|
||||
label: 'WhatsApp',
|
||||
bgColor: '#25D366',
|
||||
},
|
||||
telegram: {
|
||||
shareUrl: 'https://t.me/share/url?url={url}&text={title}',
|
||||
icon: <img src={TelegramSVG} alt='Telegram' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Telegram',
|
||||
bgColor: '#0088cc',
|
||||
},
|
||||
xing: {
|
||||
shareUrl: 'https://www.xing-share.com/app/user?op=share;sc_p=xing-share;url={url}',
|
||||
icon: <img src={XingSVG} alt='Xing' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Xing',
|
||||
bgColor: '#026466',
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -92,72 +44,7 @@ export function ActionButtons({ item, onQrModalOpen }: ActionButtonsProps) {
|
||||
<QrCodeIcon className='tw:h-4 tw:w-4' />
|
||||
</button>
|
||||
)}
|
||||
{myProfile.myProfile?.id !== item.id && (
|
||||
<div className='tw:dropdown tw:dropdown-end'>
|
||||
<div
|
||||
tabIndex={0}
|
||||
role='button'
|
||||
className='tw:btn tw:px-3 tw:tooltip tw:tooltip-top'
|
||||
data-tip='Share'
|
||||
>
|
||||
<ShareIcon className='tw:w-4 tw:h-4' />
|
||||
</div>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className='tw:dropdown-content tw:menu tw:bg-base-100 tw:rounded-box tw:z-[1] tw:p-2 tw:shadow-sm'
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
onClick={copyLink}
|
||||
className='tw:flex tw:items-center tw:gap-3'
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
<div
|
||||
className='tw:w-6 tw:h-6 tw:rounded-full tw:flex tw:items-center tw:justify-center'
|
||||
style={{ backgroundColor: '#888' }}
|
||||
>
|
||||
<img src={ClipboardSVG} className='tw:w-3 tw:h-3' alt='Copy' />
|
||||
</div>
|
||||
Copy Link
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`mailto:?subject=${encodeURIComponent(shareTitle)}&body=${encodeURIComponent(shareUrl)}`}
|
||||
className='tw:flex tw:items-center tw:gap-3'
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
<div
|
||||
className='tw:w-6 tw:h-6 tw:rounded-full tw:flex tw:items-center tw:justify-center tw:text-white'
|
||||
style={{ backgroundColor: '#444' }}
|
||||
>
|
||||
<img src={ChevronSVG} className='tw:w-3 tw:h-3' alt='Copy' />
|
||||
</div>
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
{Object.entries(platformConfigs).map(([platform, config]) => (
|
||||
<li key={platform}>
|
||||
<a
|
||||
href={getShareUrl(platform as keyof SharePlatformConfigs, platformConfigs)}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='tw:flex tw:items-center tw:gap-3'
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
<div
|
||||
className='tw:w-6 tw:h-6 tw:rounded-full tw:flex tw:items-center tw:justify-center'
|
||||
style={{ backgroundColor: (config as PlatformConfig).bgColor }}
|
||||
>
|
||||
{(config as PlatformConfig).icon}
|
||||
</div>
|
||||
{(config as PlatformConfig).label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{myProfile.myProfile?.id !== item.id && <ShareButton item={item} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -23,6 +23,10 @@ export function ConnectionStatus({ item }: ConnectionStatusProps) {
|
||||
r.related_items_id === myProfile.myProfile?.id,
|
||||
)
|
||||
|
||||
if (!item.layer?.itemType.show_cta_button) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isConnected) {
|
||||
return <p className='tw:flex tw:items-center tw:mr-2'>✅ Connected</p>
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import QRCode from 'react-qr-code'
|
||||
|
||||
import ClipboardSVG from '#assets/share/clipboard.svg'
|
||||
import DialogModal from '#components/Templates/DialogModal'
|
||||
|
||||
import { useShareLogic } from './hooks'
|
||||
import { ShareButton } from './ShareButton'
|
||||
|
||||
import type { Item } from '#types/Item'
|
||||
|
||||
@ -14,7 +14,7 @@ interface QRModalProps {
|
||||
}
|
||||
|
||||
export function QRModal({ item, isOpen, onClose }: QRModalProps) {
|
||||
const { inviteLink, copyLink } = useShareLogic(item)
|
||||
const { inviteLink } = useShareLogic(item)
|
||||
|
||||
return (
|
||||
<DialogModal
|
||||
@ -24,21 +24,15 @@ export function QRModal({ item, isOpen, onClose }: QRModalProps) {
|
||||
className='tw:w-[calc(100vw-2rem)] tw:max-w-96'
|
||||
>
|
||||
<div onClick={(e) => e.stopPropagation()} className='tw:text-center tw:p-4'>
|
||||
<p className='tw:text-xl'>Share your profile with others to expand your network.</p>
|
||||
<p className='tw:text-xl tw:font-bold'>Share your Profile to expand your Network!</p>
|
||||
|
||||
<div className='tw:p-8 tw:my-8 tw:rounded-lg tw:inline-block tw:border-base-300 tw:border-2 '>
|
||||
<div className='tw:p-8 tw:my-8 tw:rounded-lg tw:inline-block tw:border-base-300 tw:bg-base-200 tw:border-1 '>
|
||||
<QRCode value={inviteLink} size={192} />
|
||||
</div>
|
||||
|
||||
<div className='tw:flex tw:items-center tw:gap-2 tw:w-full tw:border-base-300 tw:border-2 tw:rounded-lg tw:p-3'>
|
||||
<span className='tw:text-sm tw:truncate tw:flex-1 tw:min-w-0'>{inviteLink}</span>
|
||||
<button
|
||||
onClick={copyLink}
|
||||
className='tw:btn tw:btn-primary tw:btn-sm tw:flex-shrink-0'
|
||||
title='Link kopieren'
|
||||
>
|
||||
<img src={ClipboardSVG} className='tw:w-4 tw:h-4' alt='Copy' />
|
||||
</button>
|
||||
<ShareButton item={item} dropdownDirection='up' />
|
||||
</div>
|
||||
</div>
|
||||
</DialogModal>
|
||||
|
||||
@ -0,0 +1,171 @@
|
||||
import { useRef } from 'react'
|
||||
|
||||
import { ShareIcon } from '@heroicons/react/24/solid'
|
||||
|
||||
import ChevronSVG from '#assets/chevron.svg'
|
||||
import ClipboardSVG from '#assets/share/clipboard.svg'
|
||||
import FacebookSVG from '#assets/share/facebook.svg'
|
||||
import LinkedinSVG from '#assets/share/linkedin.svg'
|
||||
import TelegramSVG from '#assets/share/telegram.svg'
|
||||
import TwitterSVG from '#assets/share/twitter.svg'
|
||||
import WhatsappSVG from '#assets/share/whatsapp.svg'
|
||||
import XingSVG from '#assets/share/xing.svg'
|
||||
|
||||
import { useShareLogic } from './hooks'
|
||||
|
||||
import type { Item } from '#types/Item'
|
||||
import type { PlatformConfig, SharePlatformConfigs } from './types'
|
||||
|
||||
interface ShareButtonProps {
|
||||
item: Item
|
||||
dropdownDirection?: 'up' | 'down'
|
||||
}
|
||||
|
||||
export function ShareButton({ item, dropdownDirection = 'down' }: ShareButtonProps) {
|
||||
const { shareUrl, shareTitle, copyLink, getShareUrl } = useShareLogic(item)
|
||||
const detailsRef = useRef<HTMLDetailsElement>(null)
|
||||
|
||||
const closeDropdown = () => {
|
||||
if (detailsRef.current) {
|
||||
detailsRef.current.open = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleCopyLink = () => {
|
||||
copyLink()
|
||||
closeDropdown()
|
||||
}
|
||||
|
||||
const handleNativeShare = async () => {
|
||||
if (navigator.share) {
|
||||
try {
|
||||
await navigator.share({
|
||||
title: shareTitle,
|
||||
url: shareUrl,
|
||||
})
|
||||
closeDropdown()
|
||||
} catch (error) {
|
||||
// User cancelled or error occurred
|
||||
console.log('Share cancelled or failed:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const canUseNativeShare = typeof navigator !== 'undefined' && navigator.share
|
||||
|
||||
const platformConfigs: SharePlatformConfigs = {
|
||||
facebook: {
|
||||
shareUrl: 'https://www.facebook.com/sharer/sharer.php?u={url}',
|
||||
icon: <img src={FacebookSVG} alt='Facebook' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Facebook',
|
||||
bgColor: '#3b5998',
|
||||
},
|
||||
twitter: {
|
||||
shareUrl: 'https://twitter.com/intent/tweet?text={title}:%20{url}',
|
||||
icon: <img src={TwitterSVG} alt='Twitter' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Twitter',
|
||||
bgColor: '#55acee',
|
||||
},
|
||||
linkedin: {
|
||||
shareUrl: 'http://www.linkedin.com/shareArticle?mini=true&url={url}&title={title}',
|
||||
icon: <img src={LinkedinSVG} alt='Linkedin' className='tw:w-4 tw:h-4' />,
|
||||
label: 'LinkedIn',
|
||||
bgColor: '#4875b4',
|
||||
},
|
||||
whatsapp: {
|
||||
shareUrl: 'https://api.whatsapp.com/send?text={title}%20{url}',
|
||||
icon: <img src={WhatsappSVG} alt='Whatsapp' className='tw:w-4 tw:h-4' />,
|
||||
label: 'WhatsApp',
|
||||
bgColor: '#25D366',
|
||||
},
|
||||
telegram: {
|
||||
shareUrl: 'https://t.me/share/url?url={url}&text={title}',
|
||||
icon: <img src={TelegramSVG} alt='Telegram' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Telegram',
|
||||
bgColor: '#0088cc',
|
||||
},
|
||||
xing: {
|
||||
shareUrl: 'https://www.xing-share.com/app/user?op=share;sc_p=xing-share;url={url}',
|
||||
icon: <img src={XingSVG} alt='Xing' className='tw:w-4 tw:h-4' />,
|
||||
label: 'Xing',
|
||||
bgColor: '#026466',
|
||||
},
|
||||
}
|
||||
|
||||
const dropdownClass = dropdownDirection === 'up' ? 'tw:dropdown-top' : ''
|
||||
|
||||
// If native share is available, render a simple button instead of dropdown
|
||||
if (canUseNativeShare) {
|
||||
return (
|
||||
<button
|
||||
onClick={handleNativeShare}
|
||||
className='tw:btn tw:px-3 tw:tooltip tw:tooltip-top'
|
||||
data-tip='Share'
|
||||
>
|
||||
<ShareIcon className='tw:w-4 tw:h-4' />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
// Otherwise, render the dropdown with manual share options
|
||||
return (
|
||||
<details ref={detailsRef} className={`tw:dropdown tw:dropdown-end ${dropdownClass}`}>
|
||||
<summary className='tw:btn tw:px-3 tw:tooltip tw:tooltip-top' data-tip='Share'>
|
||||
<ShareIcon className='tw:w-4 tw:h-4' />
|
||||
</summary>
|
||||
<ul className='tw:dropdown-content tw:menu tw:bg-base-100 tw:rounded-box tw:z-[1] tw:p-2 tw:shadow-sm'>
|
||||
<li>
|
||||
<a
|
||||
onClick={handleCopyLink}
|
||||
className='tw:flex tw:items-center tw:gap-3'
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
<div
|
||||
className='tw:w-6 tw:h-6 tw:rounded-full tw:flex tw:items-center tw:justify-center'
|
||||
style={{ backgroundColor: '#888' }}
|
||||
>
|
||||
<img src={ClipboardSVG} className='tw:w-3 tw:h-3' alt='Copy' />
|
||||
</div>
|
||||
Copy Link
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`mailto:?subject=${encodeURIComponent(shareTitle)}&body=${encodeURIComponent(shareUrl)}`}
|
||||
onClick={closeDropdown}
|
||||
className='tw:flex tw:items-center tw:gap-3'
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
<div
|
||||
className='tw:w-6 tw:h-6 tw:rounded-full tw:flex tw:items-center tw:justify-center tw:text-white'
|
||||
style={{ backgroundColor: '#444' }}
|
||||
>
|
||||
<img src={ChevronSVG} className='tw:w-3 tw:h-3' alt='Copy' />
|
||||
</div>
|
||||
Email
|
||||
</a>
|
||||
</li>
|
||||
{Object.entries(platformConfigs).map(([platform, config]) => (
|
||||
<li key={platform}>
|
||||
<a
|
||||
href={getShareUrl(platform as keyof SharePlatformConfigs, platformConfigs)}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
onClick={closeDropdown}
|
||||
className='tw:flex tw:items-center tw:gap-3'
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
<div
|
||||
className='tw:w-6 tw:h-6 tw:rounded-full tw:flex tw:items-center tw:justify-center'
|
||||
style={{ backgroundColor: (config as PlatformConfig).bgColor }}
|
||||
>
|
||||
{(config as PlatformConfig).icon}
|
||||
</div>
|
||||
{(config as PlatformConfig).label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</details>
|
||||
)
|
||||
}
|
||||
@ -35,10 +35,7 @@ export const useNavigationUrl = (coordinates?: [number, number]) => {
|
||||
export const useShareLogic = (item?: Item) => {
|
||||
const shareUrl = window.location.href
|
||||
const shareTitle = item?.name ?? 'Utopia Map Item'
|
||||
const inviteLink =
|
||||
item?.secrets && item.secrets.length > 0
|
||||
? `${window.location.origin}/invite/${item.secrets[0].secret}`
|
||||
: shareUrl
|
||||
const inviteLink = shareUrl
|
||||
|
||||
const copyLink = () => {
|
||||
navigator.clipboard
|
||||
|
||||
@ -8,25 +8,19 @@ import type { Item } from '#types/Item'
|
||||
*/
|
||||
export const StartEndView = ({ item }: { item?: Item }) => {
|
||||
return (
|
||||
<div className='tw:flex tw:flex-row tw:mb-4 tw:mt-4 tw:bg-base-300 tw:px-4 tw:py-3 tw:rounded-selector tw:w-full'>
|
||||
<div className='tw:basis-2/5 tw:flex tw:flex-row tw:font-bold'>
|
||||
<CalendarIcon className='tw:h-4 tw:w-4 tw:mr-2' />
|
||||
<time
|
||||
className='tw:align-middle'
|
||||
dateTime={item && item.start ? item.start.substring(0, 10) : ''}
|
||||
>
|
||||
<div className='tw:flex tw:flex-row tw:mb-2.5 tw:mt-2.5 tw:bg-base-200 tw:px-3 tw:py-2.5 tw:rounded-selector tw:w-full'>
|
||||
<div className='tw:basis-2/5 tw:flex tw:flex-row tw:items-center tw:font-bold'>
|
||||
<CalendarIcon className='tw:h-5 tw:w-5 tw:mr-2' />
|
||||
<time dateTime={item && item.start ? item.start.substring(0, 10) : ''}>
|
||||
{item && item.start ? new Date(item.start).toLocaleDateString() : ''}
|
||||
</time>
|
||||
</div>
|
||||
<div className='tw:basis-1/5 tw:place-content-center'>
|
||||
<div className='tw:basis-1/5 tw:flex tw:items-center tw:justify-center'>
|
||||
<span>-</span>
|
||||
</div>
|
||||
<div className='tw:basis-2/5 tw:flex tw:flex-row tw:font-bold'>
|
||||
<CalendarIcon className='tw:h-4 tw:w-4 tw:mr-2' />
|
||||
<time
|
||||
className='tw:align-middle'
|
||||
dateTime={item && item.end ? item.end.substring(0, 10) : ''}
|
||||
>
|
||||
<div className='tw:basis-2/5 tw:flex tw:flex-row tw:items-center tw:font-bold'>
|
||||
<CalendarIcon className='tw:h-5 tw:w-5 tw:mr-2' />
|
||||
<time dateTime={item && item.end ? item.end.substring(0, 10) : ''}>
|
||||
{item && item.end ? new Date(item.end).toLocaleDateString() : ''}
|
||||
</time>
|
||||
</div>
|
||||
|
||||
1
lib/src/types/ItemType.d.ts
vendored
1
lib/src/types/ItemType.d.ts
vendored
@ -24,4 +24,5 @@ export interface ItemType {
|
||||
cta_button_label?: string
|
||||
show_address?: boolean
|
||||
cta_relation?: string
|
||||
show_cta_button?: boolean
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user