ぼざクリタグ広場 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.
 
 
 
 

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