commit
bfa5fe8ece
7 changed files with 90 additions and 45 deletions
|
@ -5,6 +5,7 @@ import * as React from 'react';
|
||||||
import { isURIValid } from 'lbry-redux';
|
import { isURIValid } from 'lbry-redux';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import ClaimLink from 'component/claimLink';
|
import ClaimLink from 'component/claimLink';
|
||||||
|
import { isLBRYDomain } from 'util/uri';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
href: string,
|
href: string,
|
||||||
|
@ -37,7 +38,10 @@ class ExternalLink extends React.PureComponent<Props> {
|
||||||
title={title || href}
|
title={title || href}
|
||||||
label={children}
|
label={children}
|
||||||
className="button--external-link"
|
className="button--external-link"
|
||||||
onClick={() => openModal(MODALS.CONFIRM_EXTERNAL_RESOURCE, { uri: href })}
|
onClick={() => {
|
||||||
|
const isTrusted = isLBRYDomain(href);
|
||||||
|
openModal(MODALS.CONFIRM_EXTERNAL_RESOURCE, { uri: href, isTrusted: isTrusted });
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
makeSelectDownloadingForUri,
|
makeSelectDownloadingForUri,
|
||||||
makeSelectLoadingForUri,
|
makeSelectLoadingForUri,
|
||||||
makeSelectClaimForUri,
|
makeSelectClaimForUri,
|
||||||
|
makeSelectClaimIsMine,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { makeSelectCostInfoForUri } from 'lbryinc';
|
import { makeSelectCostInfoForUri } from 'lbryinc';
|
||||||
import { doOpenModal } from 'redux/actions/app';
|
import { doOpenModal } from 'redux/actions/app';
|
||||||
|
@ -17,6 +18,7 @@ const select = (state, props) => ({
|
||||||
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
costInfo: makeSelectCostInfoForUri(props.uri)(state),
|
||||||
loading: makeSelectLoadingForUri(props.uri)(state),
|
loading: makeSelectLoadingForUri(props.uri)(state),
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
|
|
@ -8,6 +8,7 @@ import analytics from 'analytics';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
|
claimIsMine: boolean,
|
||||||
uri: string,
|
uri: string,
|
||||||
downloading: boolean,
|
downloading: boolean,
|
||||||
fileInfo: ?{
|
fileInfo: ?{
|
||||||
|
@ -44,7 +45,18 @@ class FileDownloadLink extends React.PureComponent<Props> {
|
||||||
uri: ?string;
|
uri: ?string;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { fileInfo, downloading, uri, openModal, purchaseUri, costInfo, loading, pause, claim } = this.props;
|
const {
|
||||||
|
fileInfo,
|
||||||
|
downloading,
|
||||||
|
uri,
|
||||||
|
openModal,
|
||||||
|
purchaseUri,
|
||||||
|
costInfo,
|
||||||
|
loading,
|
||||||
|
pause,
|
||||||
|
claim,
|
||||||
|
claimIsMine,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (loading || downloading) {
|
if (loading || downloading) {
|
||||||
const progress = fileInfo && fileInfo.written_bytes ? (fileInfo.written_bytes / fileInfo.total_bytes) * 100 : 0;
|
const progress = fileInfo && fileInfo.written_bytes ? (fileInfo.written_bytes / fileInfo.total_bytes) * 100 : 0;
|
||||||
|
@ -81,7 +93,7 @@ class FileDownloadLink extends React.PureComponent<Props> {
|
||||||
icon={ICONS.EXTERNAL}
|
icon={ICONS.EXTERNAL}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
pause();
|
pause();
|
||||||
openModal(MODALS.CONFIRM_EXTERNAL_RESOURCE, { path: fileInfo.download_path });
|
openModal(MODALS.CONFIRM_EXTERNAL_RESOURCE, { path: fileInfo.download_path, isMine: claimIsMine });
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
|
@ -3,12 +3,10 @@ import {
|
||||||
doFocusSearchInput,
|
doFocusSearchInput,
|
||||||
doBlurSearchInput,
|
doBlurSearchInput,
|
||||||
doUpdateSearchQuery,
|
doUpdateSearchQuery,
|
||||||
doSearch,
|
|
||||||
doToast,
|
doToast,
|
||||||
selectSearchValue,
|
selectSearchValue,
|
||||||
selectSearchSuggestions,
|
selectSearchSuggestions,
|
||||||
selectSearchBarFocused,
|
selectSearchBarFocused,
|
||||||
parseURI,
|
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import Wunderbar from './view';
|
import Wunderbar from './view';
|
||||||
|
@ -24,6 +22,7 @@ const select = state => ({
|
||||||
const perform = (dispatch, ownProps) => ({
|
const perform = (dispatch, ownProps) => ({
|
||||||
onSearch: query => {
|
onSearch: query => {
|
||||||
ownProps.history.push({ pathname: `/$/search`, search: `?q=${encodeURIComponent(query)}` });
|
ownProps.history.push({ pathname: `/$/search`, search: `?q=${encodeURIComponent(query)}` });
|
||||||
|
dispatch(doUpdateSearchQuery(query));
|
||||||
analytics.apiLogSearch();
|
analytics.apiLogSearch();
|
||||||
},
|
},
|
||||||
onSubmit: uri => {
|
onSubmit: uri => {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { Modal } from 'modal/modal';
|
import { Modal } from 'modal/modal';
|
||||||
|
import { formatPathForWeb } from 'util/uri';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
upload: Buffer => void,
|
upload: Buffer => void,
|
||||||
|
@ -12,10 +13,7 @@ type Props = {
|
||||||
function ModalAutoGenerateThumbnail(props: Props) {
|
function ModalAutoGenerateThumbnail(props: Props) {
|
||||||
const { closeModal, filePath, upload, showToast } = props;
|
const { closeModal, filePath, upload, showToast } = props;
|
||||||
const playerRef = useRef();
|
const playerRef = useRef();
|
||||||
|
const videoSrc = formatPathForWeb(filePath);
|
||||||
let src = filePath.replace(/\\/g, '/');
|
|
||||||
src = src[0] !== '/' ? `/${src}` : src;
|
|
||||||
src = encodeURI(`file://${src}`).replace(/[?#]/g, encodeURIComponent);
|
|
||||||
|
|
||||||
function uploadImage() {
|
function uploadImage() {
|
||||||
const imageBuffer = captureSnapshot();
|
const imageBuffer = captureSnapshot();
|
||||||
|
@ -73,7 +71,7 @@ function ModalAutoGenerateThumbnail(props: Props) {
|
||||||
>
|
>
|
||||||
<section className="card__content">
|
<section className="card__content">
|
||||||
<p className="card__subtitle">{__('Pause at any time to select a thumbnail from your video')}.</p>
|
<p className="card__subtitle">{__('Pause at any time to select a thumbnail from your video')}.</p>
|
||||||
<video ref={playerRef} src={src} onLoadedMetadata={resize} onError={onError} controls />
|
<video ref={playerRef} src={videoSrc} onLoadedMetadata={resize} onError={onError} controls />
|
||||||
</section>
|
</section>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Modal } from 'modal/modal';
|
import { Modal } from 'modal/modal';
|
||||||
import { formatLbryUriForWeb } from 'util/uri';
|
import { formatPathForWeb } from 'util/uri';
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
import { shell } from 'electron';
|
import { shell } from 'electron';
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: string,
|
uri: string,
|
||||||
|
isTrusted: boolean,
|
||||||
path: string,
|
path: string,
|
||||||
|
isMine: boolean,
|
||||||
closeModal: () => void,
|
closeModal: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalOpenExternalResource extends React.PureComponent<Props> {
|
function ModalOpenExternalResource(props: Props) {
|
||||||
openExternalResource() {
|
const { uri, isTrusted, path, isMine, closeModal } = props;
|
||||||
const { uri, path, closeModal } = this.props;
|
|
||||||
|
if ((uri && isTrusted) || (path && isMine)) {
|
||||||
|
openResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
function openResource() {
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
const { openExternal, openItem, showItemInFolder } = shell;
|
const { openExternal, openItem, showItemInFolder } = shell;
|
||||||
if (uri) {
|
if (uri) {
|
||||||
|
@ -30,43 +37,33 @@ class ModalOpenExternalResource extends React.PureComponent<Props> {
|
||||||
if (uri) {
|
if (uri) {
|
||||||
window.open(uri);
|
window.open(uri);
|
||||||
} else if (path) {
|
} else if (path) {
|
||||||
// Converintg path into uri, like "file://path/to/file"
|
window.open(formatPathForWeb(path));
|
||||||
let _uri = path.replace(/\\/g, '/');
|
|
||||||
// Windows drive letter must be prefixed with a slash
|
|
||||||
if (_uri[0] !== '/') {
|
|
||||||
_uri = `/${_uri}`;
|
|
||||||
}
|
|
||||||
_uri = encodeURI(`file://${_uri}`).replace(/[?#]/g, encodeURIComponent);
|
|
||||||
window.open(_uri);
|
|
||||||
}
|
}
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
closeModal();
|
closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { uri, path, closeModal } = this.props;
|
<Modal
|
||||||
return (
|
isOpen
|
||||||
<Modal
|
title={__('Warning!')}
|
||||||
isOpen
|
contentLabel={__('Confirm External Resource')}
|
||||||
title={__('Warning!')}
|
type="confirm"
|
||||||
contentLabel={__('Confirm External Resource')}
|
confirmButtonLabel={__('Continue')}
|
||||||
type="confirm"
|
onConfirmed={() => openResource()}
|
||||||
confirmButtonLabel={__('Continue')}
|
onAborted={closeModal}
|
||||||
onConfirmed={() => this.openExternalResource()}
|
>
|
||||||
onAborted={closeModal}
|
<section className="card__content">
|
||||||
>
|
<p>
|
||||||
<section className="card__content">
|
{(uri && __('This link leads to an external website.')) ||
|
||||||
<p>
|
(path && __('This file has been shared with you by other people.'))}
|
||||||
{(uri && __('This link leads to an external website.')) ||
|
</p>
|
||||||
(path && __('This file has been shared with you by other people.'))}
|
<blockquote>{uri || path}</blockquote>
|
||||||
</p>
|
<p>{__('LBRY Inc is not responsible for its content, click continue to proceed at your own risk.')}</p>
|
||||||
<blockquote>{uri || path}</blockquote>
|
</section>
|
||||||
<p>{__('LBRY Inc is not responsible for its content, click continue to proceed at your own risk.')}</p>
|
</Modal>
|
||||||
</section>
|
);
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ModalOpenExternalResource;
|
export default ModalOpenExternalResource;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { parseURI } from 'lbry-redux';
|
import { parseURI } from 'lbry-redux';
|
||||||
|
|
||||||
|
const LBRY_INC_DOMAINS = ['lbry.io', 'lbry.com', 'lbry.tv', 'lbry.tech', 'lbry.fund', 'spee.ch'];
|
||||||
|
|
||||||
export const formatLbryUriForWeb = (uri: string) => {
|
export const formatLbryUriForWeb = (uri: string) => {
|
||||||
const { claimName, claimId } = parseURI(uri);
|
const { claimName, claimId } = parseURI(uri);
|
||||||
|
|
||||||
|
@ -11,3 +13,34 @@ export const formatLbryUriForWeb = (uri: string) => {
|
||||||
|
|
||||||
return webUrl;
|
return webUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const formatPathForWeb = (path: string) => {
|
||||||
|
if (!path) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let webUrl = path.replace(/\\/g, '/');
|
||||||
|
|
||||||
|
if (webUrl[0] !== '/') {
|
||||||
|
webUrl = `/${webUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeURI(`file://${webUrl}`).replace(/[?#]/g, encodeURIComponent);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isLBRYDomain = (uri: string) => {
|
||||||
|
if (!uri) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const myURL = new URL(uri);
|
||||||
|
const hostname = myURL.hostname;
|
||||||
|
|
||||||
|
for (let domain of LBRY_INC_DOMAINS) {
|
||||||
|
if (hostname.endsWith(domain)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue