ぼざクリタグ広場 https://hub.nizika.monster
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

97 lines
2.3 KiB

  1. import { useState } from 'react'
  2. import YoutubeEmbed from 'react-youtube'
  3. import NicoViewer from '@/components/NicoViewer'
  4. import TwitterEmbed from '@/components/TwitterEmbed'
  5. import type { FC, RefObject } from 'react'
  6. import type { NiconicoMetadata, NiconicoVideoInfo, NiconicoViewerHandle, Post } from '@/types'
  7. type Props = {
  8. ref?: RefObject<NiconicoViewerHandle | null>
  9. post: Post
  10. onLoadComplete?: (info: NiconicoVideoInfo) => void
  11. onMetadataChange?: (meta: NiconicoMetadata) => void }
  12. export default (({ ref, post, onLoadComplete, onMetadataChange }: Props) => {
  13. const url = new URL (post.url)
  14. switch (url.hostname.split ('.').slice (-2).join ('.'))
  15. {
  16. case 'nicovideo.jp':
  17. {
  18. const mVideoId = url.pathname.match (/(?<=\/watch\/)[a-zA-Z0-9]+?(?=\/|$)/)
  19. if (!(mVideoId))
  20. break
  21. const [videoId] = mVideoId
  22. return (
  23. <NicoViewer
  24. ref={ref}
  25. id={videoId}
  26. width={640}
  27. height={360}
  28. onLoadComplete={onLoadComplete}
  29. onMetadataChange={onMetadataChange}/>)
  30. }
  31. case 'twitter.com':
  32. case 'x.com':
  33. {
  34. const mUserId = url.pathname.match (/(?<=\/)[^\/]+?(?=\/|$|\?)/)
  35. const mStatusId = url.pathname.match (/(?<=\/status\/)\d+?(?=\/|$|\?)/)
  36. if (!(mUserId) || !(mStatusId))
  37. break
  38. const [userId] = mUserId
  39. const [statusId] = mStatusId
  40. return <TwitterEmbed userId={userId} statusId={statusId}/>
  41. }
  42. case 'youtube.com':
  43. {
  44. const videoId = url.searchParams.get ('v')
  45. if (!(videoId))
  46. break
  47. return (
  48. <YoutubeEmbed videoId={videoId} opts={{ playerVars: {
  49. playsinline: 1,
  50. autoplay: 1,
  51. mute: 0,
  52. loop: 1,
  53. width: '640',
  54. height: '360' } }}/>)
  55. }
  56. }
  57. const [framed, setFramed] = useState (false)
  58. return (
  59. <>
  60. {framed
  61. ? (
  62. <iframe
  63. src={post.url}
  64. title={post.title || post.url}
  65. width={640}
  66. height={360}/>)
  67. : (
  68. <div>
  69. <a href="#" onClick={e => {
  70. e.preventDefault ()
  71. setFramed (confirm ('未確認の外部ページを表示します。\n'
  72. + '悪意のあるスクリプトが実行される可能性があります。\n'
  73. + '表示しますか?'))
  74. return
  75. }}>
  76. 外部ページを表示
  77. </a>
  78. </div>)}
  79. </>)
  80. }) satisfies FC<Props>