From f51cabc5613cacb078cf0ea976c8a12d9cb83925 Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Tue, 13 Aug 2019 23:04:08 -0400 Subject: [PATCH] add back file view analytics --- src/ui/analytics.js | 13 +++----- src/ui/component/fileViewer/index.js | 2 ++ src/ui/component/fileViewer/view.jsx | 44 +++++++++++++++++++++------- src/ui/page/settings/index.js | 2 ++ src/ui/page/settings/view.jsx | 2 ++ src/ui/redux/actions/app.js | 25 +++++++++++++++- src/ui/util/use-previous.js | 11 +++++++ 7 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 src/ui/util/use-previous.js diff --git a/src/ui/analytics.js b/src/ui/analytics.js index 2c887eeaa..47e6eeb2f 100644 --- a/src/ui/analytics.js +++ b/src/ui/analytics.js @@ -39,7 +39,7 @@ const analytics: Analytics = { analyticsEnabled = enabled; // @endif }, - apiLogView: (uri, outpoint, claimId, timeToStart, onSuccessCb) => { + apiLogView: (uri, outpoint, claimId, timeToStart) => { if (analyticsEnabled) { const params: { uri: string, @@ -52,17 +52,12 @@ const analytics: Analytics = { claim_id: claimId, }; - if (timeToStart) { + // lbry.tv streams from AWS so we don't care about the time to start + if (timeToStart && !IS_WEB) { params.time_to_start = timeToStart; } - Lbryio.call('file', 'view', params) - .then(() => { - if (onSuccessCb) { - onSuccessCb(); - } - }) - .catch(() => {}); + Lbryio.call('file', 'view', params); } }, apiLogSearch: () => { diff --git a/src/ui/component/fileViewer/index.js b/src/ui/component/fileViewer/index.js index 82249b4bb..5551fec66 100644 --- a/src/ui/component/fileViewer/index.js +++ b/src/ui/component/fileViewer/index.js @@ -12,6 +12,7 @@ import { makeSelectIsPlaying, makeSelectShouldObscurePreview, selectPlayingUri } import { makeSelectClientSetting } from 'redux/selectors/settings'; import { doSetPlayingUri } from 'redux/actions/content'; import { withRouter } from 'react-router'; +import { doAnalyticsView } from 'redux/actions/app'; import FileViewer from './view'; const select = (state, props) => { @@ -32,6 +33,7 @@ const select = (state, props) => { const perform = dispatch => ({ clearPlayingUri: () => dispatch(doSetPlayingUri(null)), + triggerAnalyticsView: (uri, timeToStart) => dispatch(doAnalyticsView(uri, timeToStart)), }); export default withRouter( diff --git a/src/ui/component/fileViewer/view.jsx b/src/ui/component/fileViewer/view.jsx index c67f6ff3c..8797e2baf 100644 --- a/src/ui/component/fileViewer/view.jsx +++ b/src/ui/component/fileViewer/view.jsx @@ -1,12 +1,13 @@ // @flow import * as ICONS from 'constants/icons'; -import React, { useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import Button from 'component/button'; import classnames from 'classnames'; import LoadingScreen from 'component/common/loading-screen'; import FileRender from 'component/fileRender'; import UriIndicator from 'component/uriIndicator'; import usePersistedState from 'util/use-persisted-state'; +import usePrevious from 'util/use-previous'; import { FILE_WRAPPER_CLASS } from 'page/file/view'; import Draggable from 'react-draggable'; import Tooltip from 'component/common/tooltip'; @@ -27,6 +28,7 @@ type Props = { title: ?string, floatingPlayerEnabled: boolean, clearPlayingUri: () => void, + triggerAnalyticsView: (string, number) => void, }; export default function FileViewer(props: Props) { @@ -40,7 +42,9 @@ export default function FileViewer(props: Props) { title, clearPlayingUri, floatingPlayerEnabled, + triggerAnalyticsView, } = props; + const [playTime, setPlayTime] = useState(); const [fileViewerRect, setFileViewerRect] = usePersistedState('inline-file-viewer:rect'); const [position, setPosition] = usePersistedState('floating-file-viewer:position', { x: -25, @@ -54,15 +58,24 @@ export default function FileViewer(props: Props) { ? __("It looks like you deleted or moved this file. We're rebuilding it now. It will only take a few seconds.") : __('Loading'); - function handleDrag(e, ui) { - const { x, y } = position; - const newX = x + ui.deltaX; - const newY = y + ui.deltaY; - setPosition({ - x: newX, - y: newY, - }); - } + const previousUri = usePrevious(uri); + const previousIsReadyToPlay = usePrevious(isReadyToPlay); + const isNewView = uri && previousUri !== uri && isPlaying; + const wasntReadyButNowItIs = isReadyToPlay && !previousIsReadyToPlay; + + useEffect(() => { + if (isNewView) { + setPlayTime(Date.now()); + } + }, [isNewView, uri]); + + useEffect(() => { + if (playTime && isReadyToPlay && wasntReadyButNowItIs) { + const timeToStart = Date.now() - playTime; + triggerAnalyticsView(uri, timeToStart); + setPlayTime(null); + } + }, [setPlayTime, triggerAnalyticsView, isReadyToPlay, wasntReadyButNowItIs, playTime, uri]); useEffect(() => { function handleResize() { @@ -85,9 +98,18 @@ export default function FileViewer(props: Props) { } }, [setFileViewerRect, inline]); + function handleDrag(e, ui) { + const { x, y } = position; + const newX = x + ui.deltaX; + const newY = y + ui.deltaY; + setPosition({ + x: newX, + y: newY, + }); + } + const hidePlayer = !isPlaying || !uri || (!inline && (!floatingPlayerEnabled || !isStreamable)); if (hidePlayer) { - // clearPlayingUri(); return null; } diff --git a/src/ui/page/settings/index.js b/src/ui/page/settings/index.js index 2fcc268c7..56a7c7a43 100644 --- a/src/ui/page/settings/index.js +++ b/src/ui/page/settings/index.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import * as settings from 'constants/settings'; import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet } from 'redux/actions/app'; import { doSetDaemonSetting, doSetClientSetting, doGetThemes, doChangeLanguage } from 'redux/actions/settings'; +import { doSetPlayingUri } from 'redux/actions/content'; import { makeSelectClientSetting, selectDaemonSettings, @@ -40,6 +41,7 @@ const perform = dispatch => ({ encryptWallet: () => dispatch(doNotifyEncryptWallet()), decryptWallet: () => dispatch(doNotifyDecryptWallet()), updateWalletStatus: () => dispatch(doWalletStatus()), + clearPlayingUri: () => dispatch(doSetPlayingUri(null)), }); export default connect( diff --git a/src/ui/page/settings/view.jsx b/src/ui/page/settings/view.jsx index c31decd29..06feac772 100644 --- a/src/ui/page/settings/view.jsx +++ b/src/ui/page/settings/view.jsx @@ -168,6 +168,7 @@ class SettingsPage extends React.PureComponent { hideBalance, userBlockedChannelsCount, floatingPlayer, + clearPlayingUri, } = this.props; const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0; @@ -319,6 +320,7 @@ class SettingsPage extends React.PureComponent { name="floating_player" onChange={() => { setClientSetting(SETTINGS.FLOATING_PLAYER, !floatingPlayer); + clearPlayingUri(); }} checked={floatingPlayer} label={__('Floating video player')} diff --git a/src/ui/redux/actions/app.js b/src/ui/redux/actions/app.js index cd05bba2b..ee5114926 100644 --- a/src/ui/redux/actions/app.js +++ b/src/ui/redux/actions/app.js @@ -6,7 +6,14 @@ import { ipcRenderer, remote } from 'electron'; import path from 'path'; import * as ACTIONS from 'constants/action_types'; import * as MODALS from 'constants/modal_types'; -import { Lbry, doBalanceSubscribe, doFetchFileInfosAndPublishedClaims, doError } from 'lbry-redux'; +import { + Lbry, + doBalanceSubscribe, + doFetchFileInfosAndPublishedClaims, + doError, + makeSelectClaimForUri, + makeSelectClaimIsMine, +} from 'lbry-redux'; import Native from 'native'; import { doFetchDaemonSettings } from 'redux/actions/settings'; import { doCheckSubscriptionsInit } from 'redux/actions/subscriptions'; @@ -24,6 +31,7 @@ import { import { doAuthenticate } from 'lbryinc'; import { lbrySettings as config, version as appVersion } from 'package.json'; import { push } from 'connected-react-router'; +import analytics from 'analytics'; // @if TARGET='app' const { autoUpdater } = remote.require('electron-updater'); @@ -410,3 +418,18 @@ export function doToggleSearchExpanded() { type: ACTIONS.TOGGLE_SEARCH_EXPANDED, }; } + +export function doAnalyticsView(uri, timeToStart) { + return (dispatch, getState) => { + const state = getState(); + const { txid, nout, claim_id: claimId } = makeSelectClaimForUri(uri)(state); + const claimIsMine = makeSelectClaimIsMine(uri)(state); + const outpoint = `${txid}:${nout}`; + + if (claimIsMine) { + return; + } + + analytics.apiLogView(uri, outpoint, claimId, timeToStart); + }; +} diff --git a/src/ui/util/use-previous.js b/src/ui/util/use-previous.js new file mode 100644 index 000000000..ed46581cb --- /dev/null +++ b/src/ui/util/use-previous.js @@ -0,0 +1,11 @@ +import { useEffect, useRef } from 'react'; + +export default function usePrevious(value) { + const ref = useRef(); + + useEffect(() => { + ref.current = value; + }, [value]); + + return ref.current; +}