diff --git a/src/components/index.js b/src/components/index.js index 459ff3f4c..55747394c 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -93,6 +93,7 @@ import { ForegroundNotification } from './foregroundNotification'; import { PostHtmlRenderer } from './postHtmlRenderer'; import { QuickProfileModal } from './organisms'; import QuickReplyModal from './quickReplyModal/quickReplyModalView'; +import VideoPlayer from './videoPlayer/videoPlayerView'; // Basic UI Elements import { @@ -234,4 +235,5 @@ export { PostHtmlRenderer, QuickProfileModal, QuickReplyModal, + VideoPlayer, }; diff --git a/src/components/postHtmlRenderer/postHtmlRenderer.tsx b/src/components/postHtmlRenderer/postHtmlRenderer.tsx index 88437cb71..3853c94b7 100644 --- a/src/components/postHtmlRenderer/postHtmlRenderer.tsx +++ b/src/components/postHtmlRenderer/postHtmlRenderer.tsx @@ -1,27 +1,31 @@ -import React, { memo, } from "react"; -import RenderHTML, { CustomRendererProps, Element, TNode } from "react-native-render-html"; -import styles from "./postHtmlRendererStyles"; -import { LinkData, parseLinkData } from "./linkDataParser"; -import VideoThumb from "./videoThumb"; -import { AutoHeightImage } from "../autoHeightImage/autoHeightImage"; -import { useHtmlIframeProps,iframeModel } from '@native-html/iframe-plugin'; -import WebView from "react-native-webview"; +import React, { memo } from 'react'; +import RenderHTML, { CustomRendererProps, Element, TNode } from 'react-native-render-html'; +import styles from './postHtmlRendererStyles'; +import { LinkData, parseLinkData } from './linkDataParser'; +import VideoThumb from './videoThumb'; +import { AutoHeightImage } from '../autoHeightImage/autoHeightImage'; +import { useHtmlIframeProps, iframeModel } from '@native-html/iframe-plugin'; +import WebView from 'react-native-webview'; +import { View } from 'react-native'; +import YoutubeIframe from 'react-native-youtube-iframe'; +import { VideoPlayer } from '..'; interface PostHtmlRendererProps { - contentWidth:number; - body:string; - onLoaded?:()=>void; - setSelectedImage:(imgUrl:string)=>void; - setSelectedLink:(url:string)=>void; - onElementIsImage:(imgUrl:string)=>void; - handleOnPostPress:(permlink:string, authro:string)=>void; - handleOnUserPress:(username:string)=>void; - handleTagPress:(tag:string, filter?:string)=>void; - handleVideoPress:(videoUrl:string)=>void; - handleYoutubePress:(videoId:string, startTime:number)=>void; + contentWidth: number; + body: string; + onLoaded?: () => void; + setSelectedImage: (imgUrl: string) => void; + setSelectedLink: (url: string) => void; + onElementIsImage: (imgUrl: string) => void; + handleOnPostPress: (permlink: string, authro: string) => void; + handleOnUserPress: (username: string) => void; + handleTagPress: (tag: string, filter?: string) => void; + handleVideoPress: (videoUrl: string) => void; + handleYoutubePress: (videoId: string, startTime: number) => void; } -export const PostHtmlRenderer = memo(({ +export const PostHtmlRenderer = memo( + ({ contentWidth, body, onLoaded, @@ -33,209 +37,174 @@ export const PostHtmlRenderer = memo(({ handleTagPress, handleVideoPress, handleYoutubePress, - }:PostHtmlRendererProps) => { + }: PostHtmlRendererProps) => { + //new renderer functions + body = body.replace(/
/g, '
').replace(/<\/center>/g, '
'); - //new renderer functions - body = body.replace(/
/g, '
').replace(/<\/center>/g,'
'); + console.log('Comment body:', body); - console.log("Comment body:", body); - - const _handleOnLinkPress = (data:LinkData) => { - - if(!data){ - return; - } - - const { - type, - href, - author, - permlink, - tag, - youtubeId, - startTime, - filter, - videoHref, - community - } = data; - - try { - - switch (type) { - case '_external': - case 'markdown-external-link': - setSelectedLink(href); - break; - case 'markdown-author-link': - if (handleOnUserPress) { - handleOnUserPress(author); - } - break; - case 'markdown-post-link': - if (handleOnPostPress) { - handleOnPostPress(permlink, author); - } - break; - case 'markdown-tag-link': - if(handleTagPress){ - handleTagPress(tag, filter); - } - break; - - case 'markdown-video-link': - if(handleVideoPress){ - handleVideoPress(videoHref) - } - break; - case 'markdown-video-link-youtube': - if(handleYoutubePress){ - handleYoutubePress(youtubeId, startTime) - } - - break; - - //unused cases - case 'markdown-witnesses-link': - setSelectedLink(href); - break; - - case 'markdown-proposal-link': - setSelectedLink(href); - break; - - case 'markdown-community-link': - //tag press also handles community by default - if(handleTagPress){ - handleTagPress(community, filter) - } - break; - - default: - break; + const _handleOnLinkPress = (data: LinkData) => { + if (!data) { + return; } - } catch (error) {} - }; - - - const _onElement = (element:Element) => { - if(element.tagName === 'img' && element.attribs.src){ + + const { + type, + href, + author, + permlink, + tag, + youtubeId, + startTime, + filter, + videoHref, + community, + } = data; + + try { + switch (type) { + case '_external': + case 'markdown-external-link': + setSelectedLink(href); + break; + case 'markdown-author-link': + if (handleOnUserPress) { + handleOnUserPress(author); + } + break; + case 'markdown-post-link': + if (handleOnPostPress) { + handleOnPostPress(permlink, author); + } + break; + case 'markdown-tag-link': + if (handleTagPress) { + handleTagPress(tag, filter); + } + break; + + case 'markdown-video-link': + if (handleVideoPress) { + handleVideoPress(videoHref); + } + break; + case 'markdown-video-link-youtube': + if (handleYoutubePress) { + handleYoutubePress(youtubeId, startTime); + } + + break; + + //unused cases + case 'markdown-witnesses-link': + setSelectedLink(href); + break; + + case 'markdown-proposal-link': + setSelectedLink(href); + break; + + case 'markdown-community-link': + //tag press also handles community by default + if (handleTagPress) { + handleTagPress(community, filter); + } + break; + + default: + break; + } + } catch (error) {} + }; + + const _onElement = (element: Element) => { + if (element.tagName === 'img' && element.attribs.src) { const imgUrl = element.attribs.src; - console.log("img element detected", imgUrl); - onElementIsImage(imgUrl) + console.log('img element detected', imgUrl); + onElementIsImage(imgUrl); } }; - - const _anchorRenderer = ({ - InternalRenderer, - tnode, - ...props - }:CustomRendererProps) => { - + const _anchorRenderer = ({ InternalRenderer, tnode, ...props }: CustomRendererProps) => { + const parsedTnode = parseLinkData(tnode); const _onPress = () => { - console.log("Link Pressed:", tnode) + console.log('Link Pressed:', tnode); const data = parseLinkData(tnode); _handleOnLinkPress(data); }; - if (tnode.classes?.indexOf('markdown-video-link') >= 0) { - // get video src - let videoHref = tnode.attributes['data-embed-src'] || tnode.attributes['data-video-href'] || tnode.children[0].attributes['src']; - + if (tnode.classes?.indexOf('markdown-video-link-youtube') >= 0) { return ( - { - console.log('load end'); - }} - onLoadStart={() => { - console.log('load start'); - }} - source={{ uri: videoHref }} - style={{ width: contentWidth, height: (contentWidth * 9) / 16 }} - startInLoadingState={true} - onShouldStartLoadWithRequest={() => true} - mediaPlaybackRequiresUserAction={true} - allowsInlineMediaPlayback={true} + ); } - if(tnode.classes?.indexOf('markdown-video-link') >= 0){ - const imgElement = tnode.children.find((child)=>{ - return child.classes.indexOf('video-thumbnail') > 0 ? true:false - }) - if(!imgElement){ - return ( - - ) + if (tnode.classes?.indexOf('markdown-video-link') >= 0) { + return ( + + ); + } + if (tnode.classes?.indexOf('markdown-video-link') >= 0) { + const imgElement = tnode.children.find((child) => { + return child.classes.indexOf('video-thumbnail') > 0 ? true : false; + }); + if (!imgElement) { + return ; } } - - - return ( - - ) - } + + return ; + }; //this method checks if image is a child of table column //and calculates img width accordingly, //returns full width if img is not part of table - const getMaxImageWidth = (tnode:TNode)=>{ - + const getMaxImageWidth = (tnode: TNode) => { //return full width if not parent exist - if(!tnode.parent || tnode.parent.tagName === 'body'){ + if (!tnode.parent || tnode.parent.tagName === 'body') { return contentWidth; } //return divided width based on number td tags - if(tnode.parent.tagName === 'td'){ - const cols = tnode.parent.parent.children.length - return contentWidth/cols; + if (tnode.parent.tagName === 'td') { + const cols = tnode.parent.parent.children.length; + return contentWidth / cols; } //check next parent return getMaxImageWidth(tnode.parent); - } - - - const _imageRenderer = ({ - tnode, - }:CustomRendererProps) => { - + }; + + const _imageRenderer = ({ tnode }: CustomRendererProps) => { const imgUrl = tnode.attributes.src; const _onPress = () => { - console.log("Image Pressed:", imgUrl) + console.log('Image Pressed:', imgUrl); setSelectedImage(imgUrl); }; - + const isVideoThumb = tnode.classes?.indexOf('video-thumbnail') >= 0; const isAnchored = tnode.parent?.tagName === 'a'; - - if(isVideoThumb){ - return ; - } - else { + if (isVideoThumb) { + return ; + } else { const maxImgWidth = getMaxImageWidth(tnode); return ( - - ) + ); } - - } - + }; /** * the para renderer is designd to remove margins from para @@ -243,31 +212,18 @@ export const PostHtmlRenderer = memo(({ * a weired misalignment of bullet and content * @returns Default Renderer */ - const _paraRenderer = ({ - TDefaultRenderer, - ...props - }:CustomRendererProps) => { + const _paraRenderer = ({ TDefaultRenderer, ...props }: CustomRendererProps) => { + props.style = props.tnode.parent.tagName === 'li' ? styles.pLi : styles.p; - props.style = props.tnode.parent.tagName === 'li' - ? styles.pLi - : styles.p + return ; + }; - return ( - - ) - } - // iframe renderer for rendering iframes in body const _iframeRenderer = function IframeRenderer(props) { const iframeProps = useHtmlIframeProps(props); - // console.log('iframeProps : ', iframeProps); const checkSrcRegex = /(.*?)\.(mp4|webm|ogg)$/gi; const isVideoType = iframeProps.source.uri.match(checkSrcRegex); - // check if source contain video source then wrap it with video tag - // else pass the source directly to webview const src = isVideoType ? { html: ` @@ -300,55 +256,57 @@ export const PostHtmlRenderer = memo(({ /> ); }; - - return ( - - ) - }, (next, prev)=>next.body === prev.body) + + return ( + + ); + }, + (next, prev) => next.body === prev.body, +); diff --git a/src/components/videoPlayer/videoPlayerStyles.ts b/src/components/videoPlayer/videoPlayerStyles.ts new file mode 100644 index 000000000..b64e4c5cf --- /dev/null +++ b/src/components/videoPlayer/videoPlayerStyles.ts @@ -0,0 +1,5 @@ +import EStyleSheet from 'react-native-extended-stylesheet'; + +export default EStyleSheet.create({ + +}); diff --git a/src/components/videoPlayer/videoPlayerView.tsx b/src/components/videoPlayer/videoPlayerView.tsx new file mode 100644 index 000000000..500aa5fbf --- /dev/null +++ b/src/components/videoPlayer/videoPlayerView.tsx @@ -0,0 +1,98 @@ +import React, { useState } from 'react'; +import style from './videoPlayerStyles'; +import { Dimensions } from 'react-native'; +import { View, StyleSheet, ActivityIndicator } from 'react-native'; +import WebView from 'react-native-webview'; +import YoutubeIframe, { InitialPlayerParams } from 'react-native-youtube-iframe'; + +interface VideoPlayerProps { + youtubeVideoId?: string; + videoUrl?: string; + startTime?: number; + contentWidth?: number; +} + +const VideoPlayer = ({ youtubeVideoId, videoUrl, startTime, contentWidth }: VideoPlayerProps) => { + const PLAYER_HEIGHT = Dimensions.get('screen').width * (9 / 16); + + const [shouldPlay, setShouldPlay] = useState(false); + const [loading, setLoading] = useState(true); + + const _onReady = () => { + setLoading(false); + setShouldPlay(true); + console.log('ready'); + }; + + const _onChangeState = (event: string) => { + console.log(event); + setShouldPlay(!(event == 'paused' || event == 'ended')); + }; + + const _onError = () => { + console.log('error!'); + setLoading(false); + }; + + const initialParams: InitialPlayerParams = { + start: startTime, + }; + + return ( + + {youtubeVideoId && ( + + + + )} + {videoUrl && ( + + { + setLoading(false); + }} + onLoadStart={() => { + setLoading(true); + }} + source={{ uri: videoUrl }} + style={{ width: contentWidth, height: (contentWidth * 9) / 16 }} + startInLoadingState={true} + onShouldStartLoadWithRequest={() => true} + mediaPlaybackRequiresUserAction={true} + allowsInlineMediaPlayback={true} + /> + + )} + {loading && } + + ); +}; + +export default VideoPlayer; + +const styles = StyleSheet.create({ + container: { + paddingVertical: 16, + }, + activityIndicator: { + position: 'absolute', + alignItems: 'center', + justifyContent: 'center', + top: 0, + bottom: 0, + left: 0, + right: 0, + }, +});