From 162555a35e51f2e1a08c4afc19b5f27112bdb89f Mon Sep 17 00:00:00 2001 From: Martin Mouritzen Date: Mon, 20 Sep 2021 20:27:50 +0200 Subject: [PATCH] lots of changes --- .../Episode/Comments/EpisodeCommentList.jsx | 89 +++++++++++++++++++ .../Episode/Comments/EpisodeCommentList.scss | 50 +++++++++++ app/components/Player/Player.scss | 4 + app/components/Player/PlayerUI.jsx | 78 +++++++++++----- app/components/Player/ProgressBarSlider.jsx | 1 + app/components/Player/VolumeSlider.jsx | 1 + app/components/PodCastClient.jsx | 2 +- app/library/ActivityPub/ActivityPub.js | 84 +++++++++++++++++ app/library/PodcastFeed.js | 12 ++- app/library/RSSFeed.js | 11 +++ package.json | 2 +- 11 files changed, 306 insertions(+), 28 deletions(-) create mode 100644 app/components/Episode/Comments/EpisodeCommentList.jsx create mode 100644 app/components/Episode/Comments/EpisodeCommentList.scss create mode 100644 app/library/ActivityPub/ActivityPub.js diff --git a/app/components/Episode/Comments/EpisodeCommentList.jsx b/app/components/Episode/Comments/EpisodeCommentList.jsx new file mode 100644 index 00000000..26431efd --- /dev/null +++ b/app/components/Episode/Comments/EpisodeCommentList.jsx @@ -0,0 +1,89 @@ +import React, { useEffect, useState } from 'react'; + +import DOMPurify from 'dompurify'; + +import ActivityPub from 'podfriend-approot/library/ActivityPub/ActivityPub.js'; +import styles from './EpisodeCommentList.scss'; + + +const EpisodeComment = ({ from, content }) => { + const [commentText,setCommentText] = useState('Loading comment.'); + + useEffect(() => { + setCommentText(DOMPurify.sanitize(content,{ + ALLOWED_TAGS: [] // we used to allow 'i','em', but it doesn't work on mobile. I'm not sure I can see a good reason to have them. + })); + },[]); + + return ( +
+ +
+
{from.username}
+
+ {commentText} +
+
+
+ ); +}; +const EpisodeCommentList = ({ commentURL }) => { + const [commentInfo,setCommentInfo] = useState(false); + const [comments,setComments] = useState([]); + + useEffect(() => { + const retrieveCommentInformation = (commentURL) => { + console.log('RSS has Comment URL: '); + console.log(commentURL); + + ActivityPub.getCommentInformation(commentURL.uri) + .then((commentResponse) => { + setCommentInfo(commentResponse); + }) + .catch((error) => { + console.log('error getting comments: '); + console.log(error); + }); + }; + + if (commentURL) { + retrieveCommentInformation(commentURL); + } + },[commentURL]); + + useEffect(() => { + if (commentInfo) { + const retrieveComments = () => { + console.log('retrieveComments()'); + return ActivityPub.getComments(commentInfo,0) + .then((comments) => { + console.log('comments'); + console.log(comments); + setComments(comments); + }); + }; + retrieveComments(); + } + },[commentInfo]); + + + return ( +
+ { Array.isArray(comments) === false && +
+ No comments yet for this episode. +
+ } + { Array.isArray(comments) && comments.map((comment,index) => { + console.log('comment: '); + console.log(comment); + return ; + })} +
+ + +
+
+ ); +}; +export default EpisodeCommentList; \ No newline at end of file diff --git a/app/components/Episode/Comments/EpisodeCommentList.scss b/app/components/Episode/Comments/EpisodeCommentList.scss new file mode 100644 index 00000000..3176577a --- /dev/null +++ b/app/components/Episode/Comments/EpisodeCommentList.scss @@ -0,0 +1,50 @@ +.commentList { + min-height: 100vh; + + .writeCommentContainer { + margin-top: 30px; + + textarea { + display: block; + background-color: rgba(0,0,0,0.1); + border: 0px; + width: 100%; + height: 150px; + padding: 10px; + font-size: 16px; + + margin-bottom: 10px; + + border-radius: 5px; + + outline: 1px solid rgba(0,0,0,0); + + transition: 0.2s all; + } + textarea:focus { + outline: 1px solid rgba(0,0,0,0.3); + } + button { + width: 100%; + } + } +} +.comment { + display: flex; + + .avatar { + width: 30px; + height: 30px; + border-radius: 50%; + margin-right: 10px; + } + .username { + font-weight: bold; + margin-bottom: 5px; + } + .commentContent { + .commentText { + + } + } +} \ No newline at end of file diff --git a/app/components/Player/Player.scss b/app/components/Player/Player.scss index dc4d5c53..fab533d3 100644 --- a/app/components/Player/Player.scss +++ b/app/components/Player/Player.scss @@ -559,6 +559,10 @@ background-color: #FFFFFF; flex-direction: column; } + .segmentCommentsList { + background-color: #FFFFFF; + flex-direction: column; + } .segmentVisible { opacity: 1; } diff --git a/app/components/Player/PlayerUI.jsx b/app/components/Player/PlayerUI.jsx index 017d3319..7bd57042 100644 --- a/app/components/Player/PlayerUI.jsx +++ b/app/components/Player/PlayerUI.jsx @@ -68,6 +68,8 @@ import EpisodeList from 'podfriend-approot/components/Podcast/EpisodeList.jsx'; import Wave from 'podfriend-approot/images/design/blue-wave-1.svg'; import EpisodeChapterList from 'podfriend-approot/components/Episode/Chapters/EpisodeChapterList.jsx'; +import EpisodeCommentList from 'podfriend-approot/components/Episode/Comments/EpisodeCommentList.jsx'; + const Value4ValueModal = lazy(() => import('podfriend-approot/components/Wallet/Value4ValueModal.jsx')); // import Value4ValueModal from 'podfriend-approot/components/Wallet/Value4ValueModal.jsx'; @@ -98,6 +100,8 @@ const PlayerUI = ({ audioController, activePodcast, activeEpisode, title, progre const [error,setError] = useState(false); const [errorText,setErrorText] = useState(false); + const [supportsComments,setSupportsComments] = useState(false); + const [segmentVisible,setSegmentVisible] = useState('playing'); const [rssFeed,setRSSFeed] = useState(false); @@ -118,6 +122,15 @@ const PlayerUI = ({ audioController, activePodcast, activeEpisode, title, progre onAudioElementReady(); },[audioElement]); + useEffect(() => { + if (rssFeed && rssFeed.supportsComments()) { + setSupportsComments(true); + } + else { + setSupportsComments(false); + } + },[rssFeed]); + const onTranscriptSearchClose = () => { setTranscriptSearchOpen(false); }; @@ -445,6 +458,7 @@ const PlayerUI = ({ audioController, activePodcast, activeEpisode, title, progre else { setChaptersLoading(false); } + if (rssFeedCurrentEpisode.transcript) { // console.log('Episode has transcript'); var useTranscript = rssFeedCurrentEpisode.transcript; @@ -469,6 +483,7 @@ const PlayerUI = ({ audioController, activePodcast, activeEpisode, title, progre setRSSFeed(false); setRssFeedCurrentEpisode(false); setDescription(''); + setShowNotes(false); const fetchEpisodeData = async() => { var podcastFeed = new PodcastFeed(activePodcast.feedUrl); @@ -574,28 +589,6 @@ const PlayerUI = ({ audioController, activePodcast, activeEpisode, title, progre }
{ dispatch(showFullPlayer(false)); }} /> - - { fullPlayerOpen && chapters !== false && -
- { setSegmentVisible(e.detail.value); console.log(e.detail.value); }} onClick={(event) => { event.preventDefault(); event.stopPropagation(); }}> - - - Playing - - - { chapters !== false && - - Chapters - - } - {/* - - Social - - */} - -
- }
+ { fullPlayerOpen && (chapters !== false || supportsComments !== false) && +
+ { setSegmentVisible(e.detail.value); console.log(e.detail.value); }} onClick={(event) => { event.preventDefault(); event.stopPropagation(); }}> + + + Playing + + + { chapters !== false && + + Chapters + + } + { supportsComments !== false && + + Comments + + } + {/* + + Social + + */} + +
+ } { fullPlayerOpen && false &&
@@ -791,6 +811,20 @@ const PlayerUI = ({ audioController, activePodcast, activeEpisode, title, progre
} + { fullPlayerOpen && supportsComments !== false && +
+
+ + + +
+
+
+ +
+
+
+ } { fullPlayerOpen === false &&
diff --git a/app/components/Player/ProgressBarSlider.jsx b/app/components/Player/ProgressBarSlider.jsx index 8ef6b112..d27e44d7 100644 --- a/app/components/Player/ProgressBarSlider.jsx +++ b/app/components/Player/ProgressBarSlider.jsx @@ -100,6 +100,7 @@ const ProgressBarSlider = ({progress,duration,fullPlayerOpen,onProgressSliderCha style={{ height: '6px', width: '100%', + borderRadius: '3px', alignSelf: 'center', background: getTrackBackground({ values: [sliderValue], diff --git a/app/components/Player/VolumeSlider.jsx b/app/components/Player/VolumeSlider.jsx index 90836f10..c8d51b9f 100644 --- a/app/components/Player/VolumeSlider.jsx +++ b/app/components/Player/VolumeSlider.jsx @@ -68,6 +68,7 @@ const VolumeSlider = ({ audioElement }) => { style={{ height: '6px', width: '100%', + borderRadius: '3px', alignSelf: 'center', background: getTrackBackground({ values: [volumeLevel], diff --git a/app/components/PodCastClient.jsx b/app/components/PodCastClient.jsx index a83b4024..c37971a4 100644 --- a/app/components/PodCastClient.jsx +++ b/app/components/PodCastClient.jsx @@ -265,7 +265,7 @@ class PodcastClient extends Component { Loading...
}> - { return (); }} /> + { return (); }} /> { return (); }} /> { return (); }} /> { return (); }} /> diff --git a/app/library/ActivityPub/ActivityPub.js b/app/library/ActivityPub/ActivityPub.js new file mode 100644 index 00000000..8e804d3f --- /dev/null +++ b/app/library/ActivityPub/ActivityPub.js @@ -0,0 +1,84 @@ +class ActivityPub { + /** + * + */ + static async __fetchAndReturn(uri) { + var response; + const headers = { + 'Accept': 'application/json' + }; + + try { + response = await fetch(uri,{ + headers: headers + }); + } + catch (exception) { + response = await fetch('https://www.podfriend.com/tmp/rssproxy.php?rssUrl=' + encodeURI(uri),{ + headers: headers + }); + } + const commentResponse = await response.json(); + + // console.log('test1'); + // console.log(commentResponse); + + return commentResponse; + } + /** + * + */ + static async getUserForComment(uri) { + // console.log('Getting user for: ' + uri); + const rawUserInfo = await ActivityPub.__fetchAndReturn(uri); + console.log(rawUserInfo); + return { + avatar: rawUserInfo.icon.url, + username: rawUserInfo.preferredUsername + }; + } + static async getCommentInformation(uri) { + return await ActivityPub.__fetchAndReturn(uri); + } + /** + * + */ + static async getComments(commentInfoResponse,pageNumber = 0) { + if (commentInfoResponse.first) { + const commentResponse = await ActivityPub.__fetchAndReturn(commentInfoResponse.first); + + if (commentResponse.orderedItems) { + console.log('commentResponse.orderedItems'); + var comments = []; + // var commentAuthorPromises = []; + for (var i=0;i { + console.log('authors'); + console.log(authors); + return comments; + }); + */ + } + else { + console.log('no comments for the episode'); + return []; + } + } + console.log('no first element for the comments.'); + return false; + } +} +export default ActivityPub; \ No newline at end of file diff --git a/app/library/PodcastFeed.js b/app/library/PodcastFeed.js index 8fc7fd58..137f4f0c 100644 --- a/app/library/PodcastFeed.js +++ b/app/library/PodcastFeed.js @@ -7,6 +7,8 @@ import XMLParser from 'fast-xml-parser'; class PodcastFeed { feedUrl = this; + __parsed = false; + constructor (feedUrl) { this.feedUrl = feedUrl; } @@ -23,7 +25,7 @@ class PodcastFeed { /** * Converts itunes duration to seconds */ - convertDurationToSeconds(duration) { + __convertDurationToSeconds(duration) { if (Number.isInteger(duration)) { return duration; } @@ -88,6 +90,8 @@ class PodcastFeed { else { rssFeed.link = podcast['link']; } + + rssFeed.guid = podcast['podcast:guid'] ? podcast['podcast:guid'] : false; rssFeed.docs = podcast['docs']; rssFeed.generator = podcast['generator']; @@ -120,8 +124,6 @@ class PodcastFeed { rssFeed.funding = podcast['podcast:funding']; rssFeed.images = podcast['podcast:images']; - - if (podcast['itunes:category']) { var categories = []; if (Array.isArray(podcast['itunes:category'])) { @@ -176,16 +178,18 @@ class PodcastFeed { enclosureType: episode.enclosure ? episode.enclosure.type : '', enclosureLength: episode.enclosure ? episode.enclosure.length : '', enclosureUrl: episode.enclosure ? episode.enclosure.url : '', - duration: this.convertDurationToSeconds(episode['itunes:duration']), + duration: this.__convertDurationToSeconds(episode['itunes:duration']), transcript: episode['podcast:transcript'], transcriptUrl: episode['podcast:transcript'] ? Array.isArray(episode['podcast:transcript']) ? episode['podcast:transcript'][0]['url'] : episode['podcast:transcript']['url'] : false, chaptersUrl: episode['podcast:chapters'] ? episode['podcast:chapters']['url'] : '', chaptersType: episode['podcast:chapters'] ? episode['podcast:chapters']['type'] : '', alternateEnclosures: Array.isArray(episode['podcast:alternateEnclosure']) ? episode['podcast:alternateEnclosure'] : [ episode['podcast:alternateEnclosure'] ], + commentURL: episode['podcast:comments'] ? episode['podcast:comments'] : false }); }); rssFeed.items = episodes; } + this.__parsed = true; return rssFeed; } catch(error) { diff --git a/app/library/RSSFeed.js b/app/library/RSSFeed.js index af3598c8..701c0680 100644 --- a/app/library/RSSFeed.js +++ b/app/library/RSSFeed.js @@ -176,6 +176,17 @@ class RSSFeed { /** * */ + supportsComments() { + for (var i=0;i { return this.generatePodcastItemXML(item,preview); diff --git a/package.json b/package.json index 0fd1a8c2..1e311499 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "podfriend", "productName": "PodFriend", - "version": "0.7.11", + "version": "0.7.12", "description": "PodFriend - Because everyone needs a PodFriend.", "license": "UNLICENSED", "private": true,