Random fixes #2729

Merged
neb-b merged 12 commits from fixes into master 2019-08-15 05:11:48 +02:00
32 changed files with 249 additions and 101 deletions

View file

@ -1,6 +1,6 @@
{
"name": "LBRY",
"version": "0.34.2",
"version": "0.35.0-rc.1",
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
"keywords": [
"lbry"
@ -125,8 +125,8 @@
"jsmediatags": "^3.8.1",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#05e70648e05c51c51710f6dd698a8e2219b54df2",
"lbryinc": "lbryio/lbryinc#a93596c51c8fb0a226cb84df04c26a6bb60a45fb",
"lbry-redux": "lbryio/lbry-redux#6005fa245a888e2de045d7e42411847de7943f52",
"lbryinc": "lbryio/lbryinc#1ce266b3c52654190b955e9c869b8e302aa5c585",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",
"lodash-es": "^4.17.14",

View file

@ -3,6 +3,7 @@ import { Lbryio } from 'lbryinc';
import ReactGA from 'react-ga';
import { history } from './store';
// @if TARGET='app'
import Native from 'native';
import ElectronCookies from '@exponent/electron-cookies';
// @endif
@ -15,6 +16,9 @@ type Analytics = {
apiLogView: (string, string, string, ?number, ?() => void) => void,
apiLogPublish: () => void,
tagFollowEvent: (string, boolean, string) => void,
emailProvidedEvent: () => void,
emailVerifiedEvent: () => void,
rewardEligibleEvent: () => void,
};
let analyticsEnabled: boolean = true;
@ -26,11 +30,15 @@ const analytics: Analytics = {
},
setUser: userId => {
if (analyticsEnabled && userId) {
ReactGA.event({
category: 'User',
action: 'userId',
value: userId,
ReactGA.set({
userId,
});
// @if TARGET='app'
Native.getAppVersionInfo().then(({ localVersion }) => {
sendGaEvent('Desktop-Version', localVersion);
});
// @endif
}
},
toggle: (enabled: boolean): void => {
@ -39,8 +47,8 @@ const analytics: Analytics = {
analyticsEnabled = enabled;
// @endif
},
apiLogView: (uri, outpoint, claimId, timeToStart, onSuccessCb) => {
if (analyticsEnabled) {
apiLogView: (uri, outpoint, claimId, timeToStart) => {
if (analyticsEnabled && isProduction) {
const params: {
uri: string,
outpoint: string,
@ -52,17 +60,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: () => {
@ -82,23 +85,31 @@ const analytics: Analytics = {
}
},
tagFollowEvent: (tag, following, location) => {
if (analyticsEnabled) {
ReactGA.event({
category: following ? 'Tag-Follow' : 'Tag-Unfollow',
action: tag,
});
}
sendGaEvent(following ? 'Tag-Follow' : 'Tag-Unfollow', tag);
},
channelBlockEvent: (uri, blocked, location) => {
if (analyticsEnabled) {
ReactGA.event({
category: blocked ? 'Channel-Hidden' : 'Channel-Unhidden',
action: uri,
});
}
sendGaEvent(blocked ? 'Channel-Hidden' : 'Channel-Unhidden', uri);
},
emailProvidedEvent: () => {
sendGaEvent('Engagement', 'Email-Provided');
},
emailVerifiedEvent: () => {
sendGaEvent('Engagement', 'Email-Verified');
},
rewardEligibleEvent: () => {
sendGaEvent('Engagement', 'Reward-Eligible');
},
};
kauffj commented 2019-08-15 00:26:25 +02:00 (Migrated from github.com)
Review

Making a sendReactGAEvent or equivalent function would eliminate these repeated if checks and could also help ensure the checks are not accidentally left out in the future.

Making a `sendReactGAEvent` or equivalent function would eliminate these repeated if checks and could also help ensure the checks are not accidentally left out in the future.
neb-b commented 2019-08-15 05:11:31 +02:00 (Migrated from github.com)
Review

Yeah I've been meaning to do this, just did and it's way nicer now.

Yeah I've been meaning to do this, just did and it's way nicer now.
function sendGaEvent(category, action) {
if (analyticsEnabled && isProduction) {
ReactGA.event({
category,
action,
});
}
}
// Initialize google analytics
// Set `debug: true` for debug info
// Will change once we have separate ids for desktop/web
@ -113,6 +124,8 @@ ElectronCookies.enable({
ReactGA.initialize(UA_ID, {
testMode: process.env.NODE_ENV !== 'production',
cookieDomain: 'auto',
// un-comment to see events as they are sent to google
// debug: true,
});
// Manually call the first page view

View file

@ -12,6 +12,7 @@ import useKonamiListener from 'util/enhanced-layout';
import Yrbl from 'component/yrbl';
import FileViewer from 'component/fileViewer';
import { withRouter } from 'react-router';
import usePrevious from 'util/use-previous';
export const MAIN_WRAPPER_CLASS = 'main-wrapper';
@ -21,7 +22,7 @@ type Props = {
language: string,
theme: string,
accessToken: ?string,
user: ?{ id: string, has_verified_email: boolean },
user: ?{ id: string, has_verified_email: boolean, is_reward_approved: boolean },
location: { pathname: string },
fetchRewards: () => void,
fetchRewardedContent: () => void,
@ -35,7 +36,10 @@ function App(props: Props) {
const isEnhancedLayout = useKonamiListener();
const userId = user && user.id;
const hasVerifiedEmail = user && user.has_verified_email;
const isRewardApproved = user && user.is_reward_approved;
const previousUserId = usePrevious(userId);
const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
const previousRewardApproved = usePrevious(isRewardApproved);
const { pathname } = props.location;
const urlParts = pathname.split('/');
const claimName = urlParts[1];
@ -65,10 +69,24 @@ function App(props: Props) {
}, [theme]);
useEffect(() => {
if (userId) {
if (previousUserId === undefined && userId) {
analytics.setUser(userId);
}
}, [userId]);
}, [previousUserId, userId]);
useEffect(() => {
// Check that previousHasVerifiedEmail was not undefined instead of just not truthy
// This ensures we don't fire the emailVerified event on the initial user fetch
if (previousHasVerifiedEmail !== undefined && hasVerifiedEmail) {
analytics.emailVerifiedEvent();
}
}, [previousHasVerifiedEmail, hasVerifiedEmail]);
useEffect(() => {
if (previousRewardApproved !== undefined && isRewardApproved) {
analytics.rewardEligibleEvent();
}
}, [previousRewardApproved, isRewardApproved]);
// @if TARGET='web'
useEffect(() => {

View file

@ -1,3 +1,4 @@
import * as PAGES from 'constants/pages';
import { connect } from 'react-redux';
import {
doResolveUri,
@ -10,11 +11,15 @@ import {
makeSelectClaimIsNsfw,
selectBlockedChannels,
selectChannelIsBlocked,
doClearPublish,
doPrepareEdit,
} from 'lbry-redux';
import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc';
import { selectShowMatureContent } from 'redux/selectors/settings';
import { makeSelectHasVisitedUri } from 'redux/selectors/content';
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
import { push } from 'connected-react-router';
import ClaimPreview from './view';
const select = (state, props) => ({
@ -36,6 +41,11 @@ const select = (state, props) => ({
const perform = dispatch => ({
resolveUri: uri => dispatch(doResolveUri(uri)),
beginPublish: name => {
dispatch(doClearPublish());
dispatch(doPrepareEdit({ name }));
dispatch(push(`/$/${PAGES.PUBLISH}`));
},
});
export default connect(

View file

@ -44,6 +44,7 @@ type Props = {
blockedChannelUris: Array<string>,
channelIsBlocked: boolean,
isSubscribed: boolean,
beginPublish: string => void,
};
const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
@ -68,6 +69,7 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
showUserBlocked,
channelIsBlocked,
isSubscribed,
beginPublish,
} = props;
const haventFetched = claim === undefined;
const abandoned = !isResolvingUri && !claim;
@ -77,8 +79,9 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
const hideActions = type === 'small' || type === 'tooltip';
let isValid;
let name;
try {
parseURI(uri);
({ claimName: name } = parseURI(uri));
isValid = true;
} catch (e) {
isValid = false;
@ -191,7 +194,11 @@ const ClaimPreview = forwardRef<any, {}>((props: Props, ref: any) => {
<Fragment>
<div>{__('Publish something and claim this spot!')}</div>
<div className="card__actions">
<Button button="primary" label={`${__('Publish to')} ${uri}`} />
<Button
onClick={() => beginPublish(name)}
button="primary"
label={`${__('Publish to')} ${uri}`}
/>
</div>
</Fragment>
)}

View file

@ -25,7 +25,7 @@ class IconComponent extends React.PureComponent<Props> {
case ICONS.FEATURED:
return __('Featured content. Earn rewards for watching.');
case ICONS.DOWNLOAD:
return __('This file is downloaded.');
return __('This file is in your library.');
case ICONS.SUBSCRIBE:
return __('You are subscribed to this channel.');
case ICONS.SETTINGS:

View file

@ -16,9 +16,7 @@ type Props = {
class FileActions extends React.PureComponent<Props> {
render() {
const { fileInfo, uri, openModal, claimIsMine, claimId } = this.props;
const showDelete =
claimIsMine ||
(fileInfo && (fileInfo.written_bytes > 0 || fileInfo.blobs_completed === fileInfo.blobs_in_stream));
const showDelete = claimIsMine || (fileInfo && (fileInfo.written_bytes > 0 || fileInfo.blobs_completed > 0));
return (
<React.Fragment>
{showDelete && (

View file

@ -77,6 +77,7 @@ class FileDetails extends PureComponent<Props> {
{': '}
<Button
button="link"
className="button--download-link"
onClick={() => {
if (downloadPath) {
openFolder(downloadPath);

View file

@ -20,12 +20,10 @@ type Props = {
function FileDownloadLink(props: Props) {
const { fileInfo, downloading, loading, openModal, pause, claimIsMine, download, uri } = props;
if (loading || downloading) {
if (downloading || loading) {
const progress = fileInfo && fileInfo.written_bytes > 0 ? (fileInfo.written_bytes / fileInfo.total_bytes) * 100 : 0;
const label =
fileInfo && fileInfo.written_bytes > 0
? __('Downloading: ') + progress.toFixed(0) + __('% complete')
: __('Connecting...');
fileInfo && fileInfo.written_bytes > 0 ? progress.toFixed(0) + __('% downloaded') : __('Connecting...');
return <span>{label}</span>;
}

View file

@ -1,12 +1,12 @@
import { connect } from 'react-redux';
import { makeSelectFileInfoForUri, makeSelectClaimIsMine } from 'lbry-redux';
import { makeSelectFilePartlyDownloaded, makeSelectClaimIsMine } from 'lbry-redux';
import { selectRewardContentClaimIds } from 'lbryinc';
import { makeSelectIsSubscribed, makeSelectIsNew } from 'redux/selectors/subscriptions';
import FileProperties from './view';
const select = (state, props) => ({
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
downloaded: !!makeSelectFileInfoForUri(props.uri)(state),
downloaded: makeSelectFilePartlyDownloaded(props.uri)(state),
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
isNew: makeSelectIsNew(props.uri)(state),
claimIsMine: makeSelectClaimIsMine(props.uri)(state),

View file

@ -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(

View file

@ -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,
kauffj commented 2019-08-15 00:28:26 +02:00 (Migrated from github.com)
Review

😂

:joy:
y: newY,
});
}
const hidePlayer = !isPlaying || !uri || (!inline && (!floatingPlayerEnabled || !isStreamable));
neb-b commented 2019-08-14 20:12:29 +02:00 (Migrated from github.com)
Review

@tzarebczan brought up that we might not want to do play time (or keep play time separate) for non-streamable content.

@tzarebczan brought up that we might not want to do play time (or keep play time separate) for non-streamable content.
kauffj commented 2019-08-15 00:28:05 +02:00 (Migrated from github.com)
Review

I'm not particularly concerned about this, I think it can be cleaned up in internal analytics.

I'm not particularly concerned about this, I think it can be cleaned up in internal analytics.
if (hidePlayer) {
clearPlayingUri();
return null;
}

View file

@ -25,7 +25,18 @@ type Props = {
};
export default function FileViewer(props: Props) {
const { play, mediaType, isPlaying, fileInfo, uri, obscurePreview, insufficientCredits, thumbnail, autoplay } = props;
const {
play,
mediaType,
isPlaying,
fileInfo,
uri,
obscurePreview,
insufficientCredits,
thumbnail,
autoplay,
isStreamable,
} = props;
const isPlayable = ['audio', 'video'].indexOf(mediaType) !== -1;
const fileStatus = fileInfo && fileInfo.status;
@ -64,10 +75,10 @@ export default function FileViewer(props: Props) {
useEffect(() => {
const videoOnPage = document.querySelector('video');
if (autoplay && !videoOnPage) {
if (autoplay && !videoOnPage && isStreamable) {
viewFile();
}
}, [autoplay, viewFile]);
}, [autoplay, viewFile, isStreamable]);
return (
<div

View file

@ -3,6 +3,7 @@ import * as React from 'react';
import { FormField, Form } from 'component/common/form';
import Button from 'component/button';
import { Lbryio } from 'lbryinc';
import analytics from 'analytics';
type Props = {
cancelButton: React.Node,
@ -37,6 +38,7 @@ class UserEmailNew extends React.PureComponent<Props, State> {
const { email } = this.state;
const { addUserEmail } = this.props;
addUserEmail(email);
analytics.emailProvidedEvent();
// @if TARGET='web'
Lbryio.call('user_tag', 'edit', { add: 'lbrytv' });

View file

@ -1,11 +1,12 @@
import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app';
import { doToast, doUploadThumbnail } from 'lbry-redux';
import fs from 'fs';
import ModalAutoGenerateThumbnail from './view';
const perform = dispatch => ({
closeModal: () => dispatch(doHideModal()),
upload: buffer => dispatch(doUploadThumbnail(null, buffer)),
upload: buffer => dispatch(doUploadThumbnail(null, buffer, null, fs)),
showToast: options => dispatch(doToast(options)),
kauffj commented 2019-08-15 00:29:01 +02:00 (Migrated from github.com)
Review

should these 3rd and 4th params be re-ordered? (meh)

should these 3rd and 4th params be re-ordered? (meh)
neb-b commented 2019-08-15 04:57:51 +02:00 (Migrated from github.com)
Review

The third param is the android equivalent of fs

The third param is the android equivalent of `fs`
});

View file

@ -1,11 +1,12 @@
import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app';
import { doUploadThumbnail, doUpdatePublishForm } from 'lbry-redux';
import fs from 'fs';
import ModalConfirmThumbnailUpload from './view';
const perform = dispatch => ({
closeModal: () => dispatch(doHideModal()),
upload: path => dispatch(doUploadThumbnail(path)),
upload: path => dispatch(doUploadThumbnail(path, null, null, fs)),
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
kauffj commented 2019-08-15 00:29:31 +02:00 (Migrated from github.com)
Review

same question as above, if fs is regularly provided but other params are not, consider moving fs up in param order

when does JS get named parameters?!

same question as above, if `fs` is regularly provided but other params are not, consider moving fs up in param order when does JS get named parameters?!
});

View file

@ -1,25 +1,19 @@
import { connect } from 'react-redux';
import { doDeleteFileAndMaybeGoBack } from 'redux/actions/file';
import {
makeSelectTitleForUri,
makeSelectClaimIsMine,
makeSelectFileInfoForUri,
makeSelectClaimForUri,
} from 'lbry-redux';
import { makeSelectTitleForUri, makeSelectClaimIsMine, makeSelectClaimForUri } from 'lbry-redux';
import { doHideModal } from 'redux/actions/app';
import ModalRemoveFile from './view';
const select = (state, props) => ({
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
claim: makeSelectClaimForUri(props.uri)(state),
});
const perform = dispatch => ({
closeModal: () => dispatch(doHideModal()),
deleteFile: (fileInfo, deleteFromComputer, abandonClaim) => {
dispatch(doDeleteFileAndMaybeGoBack(fileInfo, deleteFromComputer, abandonClaim));
deleteFile: (uri, deleteFromComputer, abandonClaim) => {
dispatch(doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim));
},
});

View file

@ -6,6 +6,7 @@ import Button from 'component/button';
import usePersistedState from 'util/use-persisted-state';
type Props = {
uri: string,
claim: StreamClaim,
claimIsMine: boolean,
closeModal: () => void,
@ -17,11 +18,9 @@ type Props = {
};
function ModalRemoveFile(props: Props) {
const { claim, claimIsMine, closeModal, deleteFile, fileInfo, title } = props;
const { uri, claimIsMine, closeModal, deleteFile, title } = props;
const [deleteChecked, setDeleteChecked] = usePersistedState('modal-remove-file:delete', true);
const [abandonChecked, setAbandonChecked] = usePersistedState('modal-remove-file:abandon', true);
const { txid, nout } = claim;
const outpoint = fileInfo ? fileInfo.outpoint : `${txid}:${nout}`;
return (
<Modal isOpen title="Remove File" contentLabel={__('Confirm File Remove')} type="custom" onAborted={closeModal}>
@ -30,7 +29,7 @@ function ModalRemoveFile(props: Props) {
{__("Are you sure you'd like to remove")} <cite>{`"${title}"`}</cite> {__('from the LBRY app?')}
</p>
</section>
<Form onSubmit={() => deleteFile(outpoint || '', deleteChecked, abandonChecked)}>
<Form onSubmit={() => deleteFile(uri, deleteChecked, claimIsMine ? abandonChecked : false)}>
<FormField
name="file_delete"
label={__('Also delete this file from my computer')}
@ -49,13 +48,7 @@ function ModalRemoveFile(props: Props) {
/>
)}
<div className="card__actions">
<Button
autoFocus
button="primary"
label={__('OK')}
disabled={!deleteChecked && !abandonChecked}
onClick={() => deleteFile(outpoint || '', deleteChecked, abandonChecked)}
/>
<Button type="submit" button="primary" label={__('OK')} disabled={!deleteChecked && !abandonChecked} />
<Button button="link" label={__('Cancel')} onClick={closeModal} />
</div>
</Form>

View file

@ -22,6 +22,7 @@ import { doFetchViewCount, makeSelectViewCountForUri, makeSelectCostInfoForUri,
import { selectShowMatureContent, makeSelectClientSetting } from 'redux/selectors/settings';
import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions';
import { doOpenModal } from 'redux/actions/app';
import fs from 'fs';
import FilePage from './view';
const select = (state, props) => ({
@ -47,7 +48,7 @@ const perform = dispatch => ({
fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)),
fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)),
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
prepareEdit: (publishData, uri, fileInfo) => dispatch(doPrepareEdit(publishData, uri, fileInfo)),
prepareEdit: (publishData, uri, fileInfo) => dispatch(doPrepareEdit(publishData, uri, fileInfo, fs)),
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
setViewed: uri => dispatch(doSetContentHistoryItem(uri)),
markSubscriptionRead: (channel, uri) => dispatch(doRemoveUnreadSubscription(channel, uri)),

View file

@ -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(

View file

@ -168,6 +168,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
hideBalance,
userBlockedChannelsCount,
floatingPlayer,
clearPlayingUri,
} = this.props;
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
@ -319,6 +320,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
name="floating_player"
onChange={() => {
setClientSetting(SETTINGS.FLOATING_PLAYER, !floatingPlayer);
clearPlayingUri();
}}
checked={floatingPlayer}
label={__('Floating video player')}

View file

@ -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);
};
}

View file

@ -225,6 +225,11 @@ export function doPlayUri(uri: string, skipCostCheck: boolean = false, saveFileO
}
}
if (fileInfo && saveFile && (!fileInfo.download_path || !fileInfo.written_bytes)) {
beginGetFile();
return;
}
if (cost === 0 || skipCostCheck) {
beginGetFile();
return;

View file

@ -2,9 +2,11 @@ import * as ACTIONS from 'constants/action_types';
// @if TARGET='app'
import { shell } from 'electron';
// @endif
import { Lbry, batchActions, doAbandonClaim, selectMyClaimsOutpoints } from 'lbry-redux';
import { Lbry, batchActions, doAbandonClaim, selectMyClaimsOutpoints, makeSelectFileInfoForUri } from 'lbry-redux';
import { doHideModal } from 'redux/actions/app';
import { goBack } from 'connected-react-router';
import { doSetPlayingUri } from 'redux/actions/content';
import { selectPlayingUri } from 'redux/selectors/content';
export function doOpenFileInFolder(path) {
return () => {
@ -47,14 +49,23 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
};
}
export function doDeleteFileAndMaybeGoBack(fileInfo, deleteFromComputer, abandonClaim) {
return dispatch => {
export function doDeleteFileAndMaybeGoBack(uri, deleteFromComputer, abandonClaim) {
return (dispatch, getState) => {
const state = getState();
const playingUri = selectPlayingUri(state);
const { outpoint } = makeSelectFileInfoForUri(uri)(state);
const actions = [];
actions.push(doHideModal());
actions.push(doDeleteFile(fileInfo, deleteFromComputer, abandonClaim));
dispatch(batchActions(...actions));
if (abandonClaim) {
dispatch(goBack());
actions.push(doDeleteFile(outpoint, deleteFromComputer, abandonClaim));
if (playingUri === uri) {
actions.push(doSetPlayingUri(null));
}
if (abandonClaim) {
actions.push(goBack());
}
dispatch(batchActions(...actions));
};
}

View file

@ -266,7 +266,11 @@ export const makeSelectIsSubscribed = uri =>
}
// If we couldn't get a channel uri from the claim uri, the uri passed in might be a channel already
const { isChannel } = parseURI(uri);
let isChannel;
try {
({ isChannel } = parseURI(uri));
} catch (e) {}
if (isChannel) {
const uriWithPrefix = uri.startsWith('lbry://') ? uri : `lbry://${uri}`;
return subscriptions.some(sub => sub.uri === uriWithPrefix);

View file

@ -94,6 +94,16 @@
align-items: flex-start;
}
// Quick fix because this is a pain
// There is something weird with wrapping buttons. Some places we want to wrap and others we want to ellips
kauffj commented 2019-08-15 00:30:46 +02:00 (Migrated from github.com)
Review

Good job noting why the rules are being broken so a future dev and/or future you can understand

Good job noting why the rules are being broken so a future dev and/or future you can understand
// Probably requires some nested style cleanup
.button--download-link {
.button__label {
white-space: normal;
text-align: left;
}
}
.button__content {
display: flex;
align-items: center;

View file

@ -85,11 +85,7 @@
}
.content__loading {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
display: flex;
align-items: center;
flex-direction: column;

View file

@ -95,4 +95,11 @@
.vjs-big-play-button {
display: none;
}
.vjs-modal-dialog .vjs-modal-dialog-content {
position: relative;
padding-top: 5rem;
// Make sure no videojs message interferes with overlaying buttons
pointer-events: none;
}
}

View file

@ -98,6 +98,11 @@ hr {
}
}
img,
a {
kauffj commented 2019-08-15 00:31:16 +02:00 (Migrated from github.com)
Review

Confident in no consequences to this rule being so universal?

Confident in no consequences to this rule being so universal?
neb-b commented 2019-08-15 04:59:16 +02:00 (Migrated from github.com)
Review

97% confidence

97% confidence
-webkit-user-drag: none;
}
.columns {
display: flex;
justify-content: space-between;

View file

@ -12,7 +12,7 @@ export default function usePersistedState(key, firstTimeDefault) {
parsedItem = JSON.parse(item);
} catch (e) {}
if (parsedItem) {
if (parsedItem !== undefined) {
defaultValue = parsedItem;
} else {
defaultValue = item;

View file

@ -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;
}

View file

@ -6762,18 +6762,18 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
yargs "^13.2.2"
zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#05e70648e05c51c51710f6dd698a8e2219b54df2:
lbry-redux@lbryio/lbry-redux#6005fa245a888e2de045d7e42411847de7943f52:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/05e70648e05c51c51710f6dd698a8e2219b54df2"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/6005fa245a888e2de045d7e42411847de7943f52"
dependencies:
mime "^2.4.4"
proxy-polyfill "0.1.6"
reselect "^3.0.0"
uuid "^3.3.2"
lbryinc@lbryio/lbryinc#a93596c51c8fb0a226cb84df04c26a6bb60a45fb:
lbryinc@lbryio/lbryinc#1ce266b3c52654190b955e9c869b8e302aa5c585:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/a93596c51c8fb0a226cb84df04c26a6bb60a45fb"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/1ce266b3c52654190b955e9c869b8e302aa5c585"
dependencies:
reselect "^3.0.0"