- Add ArtworkShareModal with glassmorphism UI (Facebook, X, Pinterest, Email, Copy Link, Embed Code)
- Add ArtworkShareButton with lazy-loaded modal and native share fallback
- Add useWebShare hook abstracting navigator.share with AbortError handling
- Add ShareToast auto-dismissing notification component
- Add share() endpoint to ArtworkInteractionController (POST /api/artworks/{id}/share)
- Add artwork_shares migration for Phase 2 share tracking
- Refactor ArtworkActionBar to use new ArtworkShareButton component
42 lines
1.2 KiB
JavaScript
42 lines
1.2 KiB
JavaScript
import { useCallback, useMemo } from 'react'
|
||
|
||
/**
|
||
* useWebShare – abstracts native Web Share API with a fallback callback.
|
||
*
|
||
* Usage:
|
||
* const { canNativeShare, share } = useWebShare({ onFallback })
|
||
* share({ title, text, url })
|
||
*
|
||
* If `navigator.share` is available the browser-native share sheet opens.
|
||
* Otherwise `onFallback({ title, text, url })` is called (e.g. open a modal).
|
||
*/
|
||
export default function useWebShare({ onFallback } = {}) {
|
||
const canNativeShare = useMemo(
|
||
() => typeof navigator !== 'undefined' && typeof navigator.share === 'function',
|
||
[],
|
||
)
|
||
|
||
const share = useCallback(
|
||
async ({ title, text, url }) => {
|
||
if (canNativeShare) {
|
||
try {
|
||
await navigator.share({ title, text, url })
|
||
return { shared: true, native: true }
|
||
} catch (err) {
|
||
// User cancelled the native share — don't fall through to modal
|
||
if (err?.name === 'AbortError') {
|
||
return { shared: false, native: true }
|
||
}
|
||
}
|
||
}
|
||
|
||
// Fallback — open modal
|
||
onFallback?.({ title, text, url })
|
||
return { shared: false, native: false }
|
||
},
|
||
[canNativeShare, onFallback],
|
||
)
|
||
|
||
return { canNativeShare, share }
|
||
}
|