From 2abf4ff9d2c281c5e6fd58a5ab58116c9985ace6 Mon Sep 17 00:00:00 2001 From: Jordan Miller Date: Sun, 16 Oct 2022 13:49:01 -0400 Subject: [PATCH] fixed played position when scrolled on mobile, added contine button --- src/App.js | 2 + src/component/Resume.jsx | 90 ++++++++++++++++++++++++++++++ src/component/arconnect_loader.jsx | 11 ++-- src/component/index.jsx | 2 +- src/component/podcast.jsx | 56 +++++++++++++++++-- src/utils/useUnload.js | 14 +++++ 6 files changed, 164 insertions(+), 11 deletions(-) create mode 100644 src/component/Resume.jsx create mode 100644 src/utils/useUnload.js diff --git a/src/App.js b/src/App.js index b2815da..5216b79 100644 --- a/src/App.js +++ b/src/App.js @@ -3,6 +3,7 @@ import NavBar from "./component/navbar.jsx"; import Podcast from "./component/podcast.jsx"; import Index from "./component/index.jsx"; import PodcastRss from "./component/podcast_rss.jsx"; +import Resume from "./component/Resume.jsx"; export default function App() { return ( @@ -20,6 +21,7 @@ export default function App() { path="/podcasts/:podcastId/rss" render={({ match }) => } /> + ); diff --git a/src/component/Resume.jsx b/src/component/Resume.jsx new file mode 100644 index 0000000..cc7e4eb --- /dev/null +++ b/src/component/Resume.jsx @@ -0,0 +1,90 @@ +import React, { useState, useEffect } from 'react'; +import { withRouter } from 'react-router-dom' +// import PodcastHtml from './podcast_html.jsx' +// import { MESON_ENDPOINT } from '../utils/arweave.js' +// import { useTranslation } from 'react-i18next' +// import { fetchPodcasts, sortPodcasts } from '../utils/podcast.js' +// import { Dropdown } from '../component/podcast_utils.jsx' +import ArConnectLoader from './arconnect_loader' +import { isDarkMode } from '../utils/theme' +import { themeChange } from "theme-change"; +import { useTranslation } from 'react-i18next' +import { Disclosure } from '@headlessui/react' +import { TranslateIcon } from '@heroicons/react/outline' +import { MenuIcon, XIcon } from "@heroicons/react/outline"; +import Swal from 'sweetalert2'; + +export default withRouter(function Resume(props) { + + const [darkMode, setDarkMode] = useState(isDarkMode()) + const [cover, setCover] = useState(""); + const [dName, setDName] = useState(""); + const [history, setHistory] = useState(""); + const [time, setTime] = useState(0); + const [visible, setVisible] = useState(false) + + + useEffect(() => { + themeChange(false); + // 👆 false parameter is required for react project + }, []); + + + + // const { t, i18n } = useTranslation(); + + // const changeLanguage = (lng) => { + // i18n.changeLanguage(lng); + // }; + + // const [loading, setLoading] = useState(false) + // const history = useHistory(); + React.useEffect(() => { + if (typeof window.localStorage.getItem('lastPlayed') === "string" && + typeof JSON.parse(window.localStorage.getItem('lastPlayed')) === "object" + ) { + const castDetails = JSON.parse(window.localStorage.getItem("lastPlayed")); + // Swal.fire({ + // // title: 'Continue where you left off.', + // html: castDetails.hasOwnProperty("currentTime") ? `

Continue where you left off?

+ //

Would you like to resume listening to

${castDetails.e.episodeName}

at

${castDetails.currentTime}

`: + // `

Continue where you left off?

+ //

Would you like to resume listening to

${castDetails.e.episodeName}

`, + // showCancelButton: true, + // customClass: { + // validationMessage: 'font-mono', + // cancelButton: 'font-mono', + // confirmButton: 'border-prim2 font-mono', + // }, + // }).then((result) => { + // if(result.isConfirmed === true){ + // console.log(castDetails.location.pathname) + // props.history.push(castDetails.location.pathname + "?continue=true"); + // } + // }) + if (castDetails.hasOwnProperty("podcast")) { + setCover(`https://pz-prepnb.meson.network/${castDetails.podcast.cover}`); + setDName(castDetails.podcastName); + setHistory(castDetails.location.pathname + "?continue=true") + if (castDetails.hasOwnProperty("currentTime")) { + setTime(castDetails.currentTime) + } + setVisible(true); + } + } + }, []) + + return ( + visible ?
+ + {(props.location.pathname.includes("/podcasts/") === false) ?
props.history.push(history)}> + {`${dName} + + {time <= 0 ? "Revisit?" : "Continue?"} + + +
: <>} +
: <> + ) +}) + diff --git a/src/component/arconnect_loader.jsx b/src/component/arconnect_loader.jsx index 8fba5c0..af4a3e6 100644 --- a/src/component/arconnect_loader.jsx +++ b/src/component/arconnect_loader.jsx @@ -62,9 +62,9 @@ export default function Header() { const getAddr = () => window.arweaveWallet.getActiveAddress() - const shortenAddress = (addr) => { + const shortenAddress = (addr, len = 4) => { if (addr) { - return addr.substring(0, 4) + '...' + addr.substring(addr.length - 4) + return addr.substring(0, len) + '...' + addr.substring(addr.length - len) } return addr } @@ -110,7 +110,7 @@ export default function Header() { } return ( - <> +
{(walletConnected && ( <> @@ -119,11 +119,10 @@ export default function Header() { onClick={arconnectDisconnect} > - {ansData?.currentLabel ? `${ansData?.currentLabel}.ar` : shortenAddress(address)} + {ansData?.currentLabel ? `${(ansData?.currentLabel.length > 8) ? shortenAddress(ansData?.currentLabel, 2) : ansData?.currentLabel}.ar` : shortenAddress(address)} {(ansData?.avatar === "") ?
: - // }
Profile
} @@ -138,7 +137,7 @@ export default function Header() { 🦔 {t("connector.login")}
)} - + ) } \ No newline at end of file diff --git a/src/component/index.jsx b/src/component/index.jsx index 6199dc7..e72917e 100644 --- a/src/component/index.jsx +++ b/src/component/index.jsx @@ -1,4 +1,4 @@ -import { React, useState, useEffect } from 'react' +import React,{ useState, useEffect } from 'react' import PodcastHtml from './podcast_html.jsx' import { MESON_ENDPOINT } from '../utils/arweave.js' import { useTranslation } from 'react-i18next' diff --git a/src/component/podcast.jsx b/src/component/podcast.jsx index 8c2cd24..e95758b 100644 --- a/src/component/podcast.jsx +++ b/src/component/podcast.jsx @@ -9,6 +9,8 @@ import { MESON_ENDPOINT } from '../utils/arweave.js' import { isDarkMode } from '../utils/theme.js' import { fetchPodcasts } from '../utils/podcast.js'; import { useTranslation } from 'react-i18next'; +import useUnload from '../utils/useUnload.js'; +import { useLocation, useHistory } from 'react-router-dom'; export default function Podcast(props) { const [loading, setLoading] = useState(true) @@ -180,8 +182,8 @@ export default function Podcast(props) { }) } - const showPlayer = (podcast, e) => { - const player = new Shikwasa({ + const showPlayer = (podcast, e, startAt = 0) => { + const player = window.PlayerState = new Shikwasa({ container: () => document.querySelector('.podcast-player'), themeColor: 'gray', theme: `${isDarkMode() ? 'dark' : 'light'}`, @@ -191,13 +193,59 @@ export default function Podcast(props) { artist: podcast.podcastName, cover: `${MESON_ENDPOINT}/${podcast.cover}`, src: `${MESON_ENDPOINT}/${e.contentTx}`, + startAt: startAt }, download: true }) - player.play() + // + player.play().then(()=>{ + if (startAt !== 0) { + player.seek(startAt); + console.log(startAt); + } + if(window.localStorage){ + window.localStorage.setItem("lastPlayed", JSON.stringify({e, podcast})); + } + }) + window.scrollTo(0, document.body.scrollHeight) } + + //save current time of video playback. + const location = useLocation(); + const history = useHistory(); + useUnload((e) => { + e.preventDefault(); + const currentTime = window.PlayerState.currentTime; + if(location.pathname.includes('/podcasts/')) + try { + const episodeData = JSON.parse(window.localStorage.getItem('lastPlayed')); + window.localStorage.setItem("lastPlayed", JSON.stringify({ + ...episodeData, + currentTime: currentTime, + location: location, + })); + } catch (error) { + + } + // const exit = window.confirm('Are you sure you want to leave?'); + // if (exit) window.close(); + }) + + useEffect(() => { + if(location.search.includes("continue=true")){ + if(typeof window.localStorage.getItem('lastPlayed') === "string" && + typeof JSON.parse(window.localStorage.getItem('lastPlayed')) === "object" + ){ + const castDetails = JSON.parse(window.localStorage.getItem("lastPlayed")) + showPlayer(castDetails.podcast, castDetails.e, castDetails.currentTime) + } + window.localStorage.removeItem('lastPlayed'); + history.push(location.pathname) + } + }, [location, history]) + useEffect(() => { async function fetchData() { setLoading(true) @@ -224,7 +272,7 @@ export default function Podcast(props) {
{podcastEpisodes}
{!loading && (thePodcast.owner === addr || thePodcast.superAdmins.includes(addr)) && } - < div className="podcast-player sticky bottom-0 w-screen" /> + < div className="podcast-player fixed bottom-0 w-screen" /> ) diff --git a/src/utils/useUnload.js b/src/utils/useUnload.js new file mode 100644 index 0000000..c300fc1 --- /dev/null +++ b/src/utils/useUnload.js @@ -0,0 +1,14 @@ +import React from 'react'; + +const useUnload = fn => { + const cb = React.useRef(fn); + + React.useEffect(() => { + const onUnload = cb.current; + window.addEventListener('beforeunload', onUnload); + return () => { + window.removeEventListener('beforeunload', onUnload); + }; + }, [cb]); + }; + export default useUnload; \ No newline at end of file