mirror of
https://github.com/dergigi/boris.git
synced 2025-12-20 16:14:20 +01:00
feat(reader): embed external videos in /r/ using react-player; add vimeo/dailymotion detection
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import React, { useMemo, useState, useEffect, useRef } from 'react'
|
import React, { useMemo, useState, useEffect, useRef } from 'react'
|
||||||
|
import ReactPlayer from 'react-player'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
import remarkGfm from 'remark-gfm'
|
import remarkGfm from 'remark-gfm'
|
||||||
import rehypeRaw from 'rehype-raw'
|
import rehypeRaw from 'rehype-raw'
|
||||||
@@ -29,6 +30,7 @@ import {
|
|||||||
} from '../services/reactionService'
|
} from '../services/reactionService'
|
||||||
import AuthorCard from './AuthorCard'
|
import AuthorCard from './AuthorCard'
|
||||||
import { faBooks } from '../icons/customIcons'
|
import { faBooks } from '../icons/customIcons'
|
||||||
|
import { classifyUrl } from '../utils/helpers'
|
||||||
|
|
||||||
interface ContentPanelProps {
|
interface ContentPanelProps {
|
||||||
loading: boolean
|
loading: boolean
|
||||||
@@ -136,6 +138,7 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
|
|
||||||
// Determine if we're on a nostr-native article (/a/) or external URL (/r/)
|
// Determine if we're on a nostr-native article (/a/) or external URL (/r/)
|
||||||
const isNostrArticle = selectedUrl && selectedUrl.startsWith('nostr:')
|
const isNostrArticle = selectedUrl && selectedUrl.startsWith('nostr:')
|
||||||
|
const isExternalVideo = !isNostrArticle && !!selectedUrl && ['youtube', 'video'].includes(classifyUrl(selectedUrl).type)
|
||||||
|
|
||||||
// Get article links for menu
|
// Get article links for menu
|
||||||
const getArticleLinks = () => {
|
const getArticleLinks = () => {
|
||||||
@@ -312,7 +315,31 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
|
|||||||
highlights={relevantHighlights}
|
highlights={relevantHighlights}
|
||||||
highlightVisibility={highlightVisibility}
|
highlightVisibility={highlightVisibility}
|
||||||
/>
|
/>
|
||||||
{markdown || html ? (
|
{isExternalVideo ? (
|
||||||
|
<>
|
||||||
|
<div className="reader-video">
|
||||||
|
<ReactPlayer url={selectedUrl as string} controls width="100%" height="60vh" />
|
||||||
|
</div>
|
||||||
|
{activeAccount && (
|
||||||
|
<div className="mark-as-read-container">
|
||||||
|
<button
|
||||||
|
className={`mark-as-read-btn ${isMarkedAsRead ? 'marked' : ''} ${showCheckAnimation ? 'animating' : ''}`}
|
||||||
|
onClick={handleMarkAsRead}
|
||||||
|
disabled={isMarkedAsRead || isCheckingReadStatus}
|
||||||
|
title={isMarkedAsRead ? 'Already Marked as Read' : 'Mark as Read'}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={isCheckingReadStatus ? faSpinner : isMarkedAsRead ? faCheckCircle : faBooks}
|
||||||
|
spin={isCheckingReadStatus}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
{isCheckingReadStatus ? 'Checking...' : isMarkedAsRead ? 'Marked as Read' : 'Mark as Read'}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : markdown || html ? (
|
||||||
<>
|
<>
|
||||||
{markdown ? (
|
{markdown ? (
|
||||||
renderedMarkdownHtml && finalHtml ? (
|
renderedMarkdownHtml && finalHtml ? (
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ export const classifyUrl = (url: string | undefined): UrlClassification => {
|
|||||||
return { type: 'youtube' }
|
return { type: 'youtube' }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for popular video hosts
|
||||||
|
const videoHosts = ['vimeo.com', 'dailymotion.com', 'dai.ly', 'video.twimg.com']
|
||||||
|
if (videoHosts.some(host => urlLower.includes(host))) {
|
||||||
|
return { type: 'video' }
|
||||||
|
}
|
||||||
|
|
||||||
// Check for video extensions
|
// Check for video extensions
|
||||||
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv', '.m4v']
|
const videoExtensions = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv', '.m4v']
|
||||||
if (videoExtensions.some(ext => urlLower.includes(ext))) {
|
if (videoExtensions.some(ext => urlLower.includes(ext))) {
|
||||||
|
|||||||
Reference in New Issue
Block a user