diff --git a/ui/component/viewers/videoViewer/index.js b/ui/component/viewers/videoViewer/index.js index 6a6a91089..a89ff3b0e 100644 --- a/ui/component/viewers/videoViewer/index.js +++ b/ui/component/viewers/videoViewer/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import { makeSelectClaimForUri, makeSelectFileInfoForUri, makeSelectThumbnailForUri } from 'lbry-redux'; import { doChangeVolume, doChangeMute, doAnalyticsView } from 'redux/actions/app'; import { selectVolume, selectMute } from 'redux/selectors/app'; -import { savePosition } from 'redux/actions/content'; +import { savePosition, clearPosition } from 'redux/actions/content'; import { makeSelectContentPositionForUri } from 'redux/selectors/content'; import VideoViewer from './view'; import { withRouter } from 'react-router'; @@ -31,6 +31,7 @@ const select = (state, props) => { const perform = dispatch => ({ changeVolume: volume => dispatch(doChangeVolume(volume)), savePosition: (uri, position) => dispatch(savePosition(uri, position)), + clearPosition: uri => dispatch(clearPosition(uri)), changeMute: muted => dispatch(doChangeMute(muted)), doAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)), claimRewards: () => dispatch(doClaimEligiblePurchaseRewards()), diff --git a/ui/component/viewers/videoViewer/internal/videojs.jsx b/ui/component/viewers/videoViewer/internal/videojs.jsx index 90b77048f..5f6746c13 100644 --- a/ui/component/viewers/videoViewer/internal/videojs.jsx +++ b/ui/component/viewers/videoViewer/internal/videojs.jsx @@ -16,6 +16,7 @@ export type Player = { muted: (?boolean) => boolean, dispose: () => void, currentTime: (?number) => number, + ended: () => boolean, }; type Props = { diff --git a/ui/component/viewers/videoViewer/view.jsx b/ui/component/viewers/videoViewer/view.jsx index 41e4c32b6..5d4d70936 100644 --- a/ui/component/viewers/videoViewer/view.jsx +++ b/ui/component/viewers/videoViewer/view.jsx @@ -33,6 +33,7 @@ type Props = { doAnalyticsView: (string, number) => Promise, claimRewards: () => void, savePosition: (string, number) => void, + clearPosition: string => void, }; /* @@ -57,6 +58,7 @@ function VideoViewer(props: Props) { doAnalyticsView, claimRewards, savePosition, + clearPosition, desktopPlayStartTime, } = props; const claimId = claim && claim.claim_id; @@ -115,6 +117,14 @@ function VideoViewer(props: Props) { setIsEndededEmbed(false); } + function handlePosition(player) { + if (player.ended()) { + clearPosition(uri); + } else { + savePosition(uri, player.currentTime()); + } + } + const onPlayerReady = useCallback( (player: Player) => { if (!embedded) { @@ -151,7 +161,7 @@ function VideoViewer(props: Props) { player.on('play', onPlay); player.on('pause', () => { setIsPlaying(false); - savePosition(uri, player.currentTime()); + handlePosition(player); }); player.on('volumechange', () => { if (player && player.volume() !== volume) { @@ -165,7 +175,9 @@ function VideoViewer(props: Props) { if (position) { player.currentTime(position); } - player.on('dispose', () => savePosition(uri, player.currentTime())); + player.on('dispose', () => { + handlePosition(player); + }); }, IS_WEB ? [uri] : [uri, desktopPlayStartTime] ); diff --git a/ui/constants/action_types.js b/ui/constants/action_types.js index bc1939116..7750fa3f5 100644 --- a/ui/constants/action_types.js +++ b/ui/constants/action_types.js @@ -84,6 +84,7 @@ export const PUBLISH_FAILED = 'PUBLISH_FAILED'; export const SET_PLAYING_URI = 'SET_PLAYING_URI'; export const SET_FLOATING_URI = 'SET_FLOATING_URI'; export const SET_CONTENT_POSITION = 'SET_CONTENT_POSITION'; +export const CLEAR_CONTENT_POSITION = 'CLEAR_CONTENT_POSITION'; export const SET_CONTENT_LAST_VIEWED = 'SET_CONTENT_LAST_VIEWED'; export const CLEAR_CONTENT_HISTORY_URI = 'CLEAR_CONTENT_HISTORY_URI'; export const CLEAR_CONTENT_HISTORY_ALL = 'CLEAR_CONTENT_HISTORY_ALL'; diff --git a/ui/redux/actions/content.js b/ui/redux/actions/content.js index 72d2126ce..e96c08625 100644 --- a/ui/redux/actions/content.js +++ b/ui/redux/actions/content.js @@ -249,6 +249,20 @@ export function savePosition(uri: string, position: number) { }; } +export function clearPosition(uri: string) { + return (dispatch: Dispatch, getState: () => any) => { + const state = getState(); + const claim = makeSelectClaimForUri(uri)(state); + const { claim_id: claimId, txid, nout } = claim; + const outpoint = `${txid}:${nout}`; + + dispatch({ + type: ACTIONS.CLEAR_CONTENT_POSITION, + data: { claimId, outpoint }, + }); + }; +} + export function doSetContentHistoryItem(uri: string) { return (dispatch: Dispatch) => { dispatch({ diff --git a/ui/redux/reducers/content.js b/ui/redux/reducers/content.js index 2faf0d431..6f3b98b56 100644 --- a/ui/redux/reducers/content.js +++ b/ui/redux/reducers/content.js @@ -33,6 +33,36 @@ reducers[ACTIONS.SET_CONTENT_POSITION] = (state, action) => { }; }; +reducers[ACTIONS.CLEAR_CONTENT_POSITION] = (state, action) => { + const { claimId, outpoint } = action.data; + + if (state.positions[claimId]) { + const numOutpoints = Object.keys(state.positions[claimId]).length; + if (numOutpoints <= 1) { + let positions = { ...state.positions }; + delete positions[claimId]; + + return { + ...state, + positions: positions, + }; + } else { + let outpoints = { ...state.positions[claimId] }; + delete outpoints[outpoint]; + + return { + ...state, + positions: { + ...state.positions, + [claimId]: outpoints, + }, + }; + } + } else { + return state; + } +}; + reducers[ACTIONS.SET_CONTENT_LAST_VIEWED] = (state, action) => { const { uri, lastViewed } = action.data; const { history } = state;