'use client'
import { useEffect, useMemo, useState } from 'react'
import Image from 'next/image'
import styles from './page.module.css'
import { Box, Alert, Button, TextField, Typography, InputAdornment, IconButton, FormControl, InputLabel, OutlinedInput, FormHelperText,
Divider
} from '@mui/material'
import { ContentCopy as ContentCopyIcon } from '@mui/icons-material'
import { getMobileOS, getBrowser } from '@/utils'
import useWebpush from './useWebpush'
import LockIcon from '@mui/icons-material/Lock';
import { useIndexedDB } from '@/hooks'
import dynamic from 'next/dynamic'
import { diffTimeCalc } from '@/utils/date'
// ITPについい
// https://webkit.org/tracking-prevention/#intelligent-tracking-prevention-itp
const URL = 'https://mobile.raikyakun.app/'
export default function Home() {
const [os, setOS] = useState('Other')
const [browser, setBrowser] = useState('Other')
const [available, setAvailable] = useState(false)
const [now, setNow] = useState<Date>(new Date())
const [isLatestCallActive, setIsLatestCallActive] = useState(false)
const { latestCall, callList, update } = useIndexedDB()
useEffect(()=> {
const os = getMobileOS()
const browser = getBrowser()
setOS(os)
setBrowser(browser)
setAvailable( (os == 'Android' && browser == 'Chrome') || (os == 'iOS' && browser == 'Safari'))
}, [])
useEffect(() => {
update()
setInterval(()=> {
setNow(new Date())
}, 5000)
const handleVisibilityChange = () => {
if (document.visibilityState === 'visible') {
update()
}
}
document.addEventListener('visibilitychange', handleVisibilityChange)
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange)
}
}, [])
useEffect(()=>{
if(!latestCall || latestCall.responder) return
// ISO 8601形式の日付文字列をDateオブジェクトに変換
const callDate = new Date(latestCall.createdAt)
if (isNaN(callDate.getTime())) {
console.error('Invalid date format')
return
}
// 両者の差をミリ秒単位で計算し、秒単位に変換
const now = new Date()
const durationInSeconds = (now.getTime() - callDate.getTime()) / 1000
// 現在時刻から60秒以内の場合はページを切り替え
if(durationInSeconds >= 60) return
if(latestCall.responder == null){
setIsLatestCallActive(true)
} else {
location.href = `/actions?data=${JSON.stringify(latestCall)}`
}
}, [latestCall])
const copyToClipboard = async () => {
await global.navigator.clipboard.writeText(URL)
}
const { Subscribe, isSubscribed, errorMessage } = useWebpush()
return (
<main className={styles.main}>
<div style={{padding: '2rem'}}>
<Typography sx={{textAlign: 'center'}} variant="h5" component="h1">らいきゃくん通知{os != 'Other' ? <><br />for {os}</> : ''}</Typography>
</div>
{latestCall &&
<Box
sx={{
mt: 2,
border: `2px solid ${isLatestCallActive?'royalblue':'lightgray'}`,
width: '80%',
display: 'flex',
justifyContent: "space-between",
borderRadius: 2,
padding: 3,
boxShadow: `0px 0px 6px ${isLatestCallActive?'lightskyblue':'gray'} inset`
}}
onClick={()=>{if(isLatestCallActive) location.href = `/actions?data=${JSON.stringify(latestCall)}`}}
>
<Typography variant="h5">
<span style={{fontWeight: 'bold', fontSize: '80%'}}>最後の呼び出し</span><br />
<span style={{fontSize: '50%'}}>宛 先:</span>[{latestCall.dst.group}] {latestCall.dst.name}
{latestCall.visitor && <><br /><span style={{fontSize: '50%'}}>来訪者:</span> {latestCall.visitor}</>}
{latestCall.responder && <><br /><span style={{fontSize: '50%'}}>対応者: {latestCall.responder}</span></>}
</Typography>
<Box sx={{color: 'gray'}}>
{diffTimeCalc(now, new Date(latestCall.createdAt))}
</Box>
</Box>}
<div style={{padding: '2rem'}}>
<div>
{os == 'Other' && <Alert severity="warning">このページを
スマートフォンで開いてください</Alert>}
{os == 'Android' && browser != 'Chrome' && <Alert severity="warning">このページはChromeで開いてください</Alert>}
{os == 'iOS' && browser != 'Safari' && <Alert severity="warning">このページはSafariで開いてください</Alert>}
</div>
<div style={{textAlign: 'center'}}>
{os == 'Other' && <img style={{width:'40%'}} src="url.png" />}
{!available && <FormControl
fullWidth
variant="outlined"
>
<InputLabel htmlFor="url">URLをコピー</InputLabel>
<OutlinedInput
id="url"
value={URL}
endAdornment={
<InputAdornment position="end">
<IconButton
edge="end"
onClick={copyToClipboard}
>
<ContentCopyIcon />
</IconButton>
</InputAdornment>
}
label="URLをコピー"
/>
</FormControl>}
</div>
<div>
{os == 'iOS' && browser == 'Safari' && isSubscribed == false && <img style={{width:'100%'}} src="/images/install_for_ios.png" />}
</div>
</div>
{ Subscribe }
{errorMessage != '' && <Alert severity="error">{errorMessage}</Alert>}
{errorMessage != '' && os == 'iOS' && browser == 'Safari' && <FormHelperText sx={{margin: 2, padding: 2, border: '1px solid gray', borderRadius: 5}}>
通知許可ダイアログで「許可しない」を選択してしまった場合は<br />
<span style={{verticalAlign: 'middle'}}>「<img src="/images/iphone-settings-icon.png" width={20} height={20} style={{verticalAlign: 'middle'}} />設定」アプリ
>「<img src="/images/iphone-bell-icon.png" width={20} height={20} style={{verticalAlign: 'middle'}} />通知」>「通知スタイル」
>「<img src="/images/maskable_icon_x144.png" width={20} height={20} style={{verticalAlign: 'middle'}} />らいきゃくん通知」>「通知を許可」から許可してください</span>
<a target="_blank" href="https://support.apple.com/ja-jp/guide/iphone/iph7c3d96bab/ios" ><Button size="small">詳しくはこちら</Button></a>
</FormHelperText>}
{errorMessage != '' && os == 'Android' && browser == 'Chrome' && <FormHelperText sx={{margin: 2, padding: 2, border: '1px solid gray', borderRadius: 5}}>
<span style={{verticalAlign: 'middle'}}>通知許可ダイアログで「ブロック」を選択してしまった場合は<br />
アドレスバーの左にある<img src="/images/android-chrome.png" width={20} height={20} style={{verticalAlign: 'middle'}} />アイコンをクリックして権限をご確認ください</span>
</FormHelperText>}
{errorMessage != '' && os == 'Other' && browser == 'Chrome' && <FormHelperText sx={{margin: 2, padding: 2, border: '1px solid gray', borderRadius: 5}}>
<span style={{verticalAlign: 'middle'}}>通知許可ダイアログで「ブロック」を選択してしまった場合は<br />
アドレスバーの左にある<LockIcon sx={{verticalAlign: 'middle'}}/>鍵アイコンをクリックして通知設定をご確認ください</span>
</FormHelperText>}
{errorMessage != '' && os == 'Other' && browser == 'Safari' && <FormHelperText sx={{margin: 2, padding: 2, border: '1px solid gray', borderRadius: 5}}>
通知許可ダイアログで「許可しない」を選択してしまった場合は<br />
左上メニューバーから「Safari」>「設定…」>「Webサイト」>一般「通知」と進み「mobile.raikyakun.app」を許可に設定してください
</FormHelperText>}
{callList.length > 0 && <><Typography variant="h4" sx={{mt: 7}}>通知履歴</Typography>
<FormHelperText>受付履歴とは異なります</FormHelperText></>}
{callList.map(call =>
<Box key={call.callId} sx={{width: '100%', display: 'flex', justifyContent: "space-between", mb: 2, p: 2, borderBottom: '1px solid lightgray'}}>
<Typography variant="h5">
<span style={{fontSize: '50%'}}>宛 先:</span>[{call.dst.group}] {call.dst.name}
{call.visitor && <><br /><span style={{fontSize: '50%'}}>来訪者:</span> {call.visitor}</>}
{call.responder && <><br /><span style={{fontSize: '50%'}}>対応者: {call.responder}</span></>}
</Typography>
<Box sx={{color: 'gray'}}>
{diffTimeCalc(now, new Date(call.createdAt))}
</Box>
</Box>)}
</main>
)
}
const ServiceWorkerStatus: React.FC = () => {
const [registrations, setRegistrations] = useState<string[]>(['確認中...']);
useEffect(() => {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(regs => {
if (regs.length === 0) {
setRegistrations(['サービスワーカーは登録されていません。']);
} else {
const swInfo = regs.map((reg, index) =>
`(${index + 1}) スコープ: ${reg.scope}, 状態: ${reg.active ? 'アクティブ' : '非アクティブ'}`
);
setRegistrations(swInfo);
}
});
} else {
setRegistrations(['このブラウザはサービスワーカーをサポートしていません。']);
}
}, []);
return (
<div>
<h1>Service Workerの状態</h1>
<ul>
{registrations.map((reg, index) => (
<li key={index}>{reg}</li>
))}
</ul>
</div>
);
};