import { Capacitor } from '@capacitor/core'
import {
CheckCircle,
ContentCopy,
ErrorOutline,
Nfc,
} from '@mui/icons-material'
import {
Box,
Button,
CircularProgress,
IconButton,
Input,
Switch,
Typography,
} from '@mui/joy'
import { useRef, useState } from 'react'
import { useResponsiveModal } from '../../../hooks/useResponsiveModal'
import { startNativeNFCWrite } from '../../../service/NFCWriter'
const pulseKeyframes = `
@keyframes nfc-pulse {
0% { transform: scale(1); opacity: 0.6; }
70% { transform: scale(1.6); opacity: 0; }
100% { transform: scale(1.6); opacity: 0; }
}
@keyframes nfc-pulse-2 {
0% { transform: scale(1); opacity: 0.4; }
70% { transform: scale(2.1); opacity: 0; }
100% { transform: scale(2.1); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.nfc-pulse-ring { animation: none !important; }
}
`
function NFCIcon({ status }) {
const isWaiting = status === 'waiting_for_tag'
const isSuccess = status === 'success'
const isError = status === 'error'
return (
{isWaiting && (
<>
>
)}
{isSuccess ? (
) : isError ? (
) : (
)}
)
}
function WriteNFCModal({ config }) {
const { ResponsiveModal } = useResponsiveModal()
const [nfcStatus, setNfcStatus] = useState('idle')
const [errorMessage, setErrorMessage] = useState('')
const [isAutoCompleteWhenScan, setIsAutoCompleteWhenScan] = useState(false)
const [copied, setCopied] = useState(false)
const cancelScanRef = useRef(null)
const isNative = Capacitor.isNativePlatform()
const getURL = () => {
let url = config.url
if (isAutoCompleteWhenScan) url += '?auto_complete=true'
return url
}
const handleClose = async () => {
if (cancelScanRef.current) {
await cancelScanRef.current()
cancelScanRef.current = null
}
config.onClose()
setNfcStatus('idle')
setErrorMessage('')
}
const handleCancel = async () => {
if (cancelScanRef.current) {
await cancelScanRef.current()
cancelScanRef.current = null
}
setNfcStatus('idle')
}
const handleCopy = () => {
navigator.clipboard.writeText(getURL())
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}
const writeToNFC = async () => {
const url = getURL()
if (isNative) {
setNfcStatus('writing')
const cancel = await startNativeNFCWrite(url, {
onWaiting: () => setNfcStatus('waiting_for_tag'),
onSuccess: () => {
cancelScanRef.current = null
setNfcStatus('success')
},
onError: msg => {
cancelScanRef.current = null
setNfcStatus('error')
setErrorMessage(msg)
},
})
cancelScanRef.current = cancel
} else {
if ('NDEFReader' in window) {
try {
setNfcStatus('writing')
const ndef = new window.NDEFReader()
await ndef.write({ records: [{ recordType: 'url', data: url }] })
setNfcStatus('success')
} catch (error) {
console.error('Error writing to NFC tag:', error)
setNfcStatus('error')
setErrorMessage('Error writing to NFC tag. Please try again.')
}
} else {
setNfcStatus('error')
setErrorMessage(
'NFC is not supported by this browser. Copy the URL and write it to an NFC tag using a compatible device.',
)
}
}
}
const isWaiting = nfcStatus === 'waiting_for_tag' || nfcStatus === 'writing'
const isSuccess = nfcStatus === 'success'
const isError = nfcStatus === 'error'
const title = isSuccess
? 'Tag written!'
: isError
? 'Something went wrong'
: isWaiting
? 'Hold near NFC tag'
: 'Write to NFC'
const subtitle = isSuccess
? 'Your NFC tag is ready to use.'
: isError
? errorMessage
: isWaiting
? 'Keep your device near the tag until complete.'
: 'Encode this task link onto any NFC tag.'
return (
<>
{/* Icon */}
{/* Heading */}
{title}
{subtitle}
{/* Idle / Error: URL + toggle + CTA */}
{!isWaiting && !isSuccess && (
<>
Tag URL
}
/>
{copied && (
Copied!
)}
Auto-complete on scan
Mark task done when tag is tapped
setIsAutoCompleteWhenScan(e.target.checked)}
size='sm'
/>
) : (
)
}
>
{nfcStatus === 'writing' ? 'Starting…' : 'Write tag'}
>
)}
{/* Waiting state */}
{isWaiting && (
)}
{/* Success state */}
{isSuccess && (
)}
>
)
}
export default WriteNFCModal