0.36.0 sdk changes

This commit is contained in:
Sean Yesmunt 2019-04-24 10:02:08 -04:00
parent c6a33c7a90
commit 2d95c5a337
71 changed files with 525 additions and 843 deletions

View file

@ -1,10 +1,10 @@
[ignore]
node_modules/
[include]
[libs]
./flow-typed
node_modules/lbry-redux/flow-typed/
[lints]

View file

@ -1,6 +1,6 @@
// @flow
export type UrlLocation = {
declare type UrlLocation = {
search: string,
hash: string,
host: string,

View file

@ -1,59 +0,0 @@
// flow-typed signature: cca4916b0213065533df8335c3285a4a
// flow-typed version: cab04034e7/redux_v3.x.x/flow_>=v0.55.x
declare module 'redux' {
/*
S = State
A = Action
D = Dispatch
*/
declare export type DispatchAPI<A> = (action: A) => A;
declare export type Dispatch<A: { type: $Subtype<string> }> = DispatchAPI<A>;
declare export type MiddlewareAPI<S, A, D = Dispatch<A>> = {
dispatch: D;
getState(): S;
};
declare export type Store<S, A, D = Dispatch<A>> = {
// rewrite MiddlewareAPI members in order to get nicer error messages (intersections produce long messages)
dispatch: D;
getState(): S;
subscribe(listener: () => void): () => void;
replaceReducer(nextReducer: Reducer<S, A>): void
};
declare export type Reducer<S, A> = (state: S | void, action: A) => S;
declare export type CombinedReducer<S, A> = (state: $Shape<S> & {} | void, action: A) => S;
declare export type Middleware<S, A, D = Dispatch<A>> =
(api: MiddlewareAPI<S, A, D>) =>
(next: D) => D;
declare export type StoreCreator<S, A, D = Dispatch<A>> = {
(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
(reducer: Reducer<S, A>, preloadedState: S, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
};
declare export type StoreEnhancer<S, A, D = Dispatch<A>> = (next: StoreCreator<S, A, D>) => StoreCreator<S, A, D>;
declare export function createStore<S, A, D>(reducer: Reducer<S, A>, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
declare export function createStore<S, A, D>(reducer: Reducer<S, A>, preloadedState?: S, enhancer?: StoreEnhancer<S, A, D>): Store<S, A, D>;
declare export function applyMiddleware<S, A, D>(...middlewares: Array<Middleware<S, A, D>>): StoreEnhancer<S, A, D>;
declare export type ActionCreator<A, B> = (...args: Array<B>) => A;
declare export type ActionCreators<K, A> = { [key: K]: ActionCreator<A, any> };
declare export function bindActionCreators<A, C: ActionCreator<A, any>, D: DispatchAPI<A>>(actionCreator: C, dispatch: D): C;
declare export function bindActionCreators<A, K, C: ActionCreators<K, A>, D: DispatchAPI<A>>(actionCreators: C, dispatch: D): C;
declare export function combineReducers<O: Object, A>(reducers: O): CombinedReducer<$ObjMap<O, <S>(r: Reducer<S, any>) => S>, A>;
declare export var compose: $Compose;
}

52
flow-typed/publish.js vendored Normal file
View file

@ -0,0 +1,52 @@
// @flow
declare type UpdatePublishFormData = {
filePath?: string,
contentIsFree?: boolean,
price?: {
amount: number,
currency: string,
},
title?: string,
thumbnail_url?: string,
uploadThumbnailStatus?: string,
thumbnailPath?: string,
description?: string,
language?: string,
channel?: string,
channelId?: string,
name?: string,
nameError?: string,
bid?: number,
bidError?: string,
otherLicenseDescription?: string,
licenseUrl?: string,
licenseType?: string,
uri?: string,
};
declare type PublishParams = {
name: ?string,
bid: ?number,
filePath?: string,
description: ?string,
language: string,
publishingLicense?: string,
publishingLicenseUrl?: string,
thumbnail: ?string,
channel: string,
channelId?: string,
title: string,
contentIsFree: boolean,
uri?: string,
license: ?string,
licenseUrl: ?string,
fee?: {
currency: string,
amount: number,
},
// This is bad.
// Will be removed for tags soon
nsfw: boolean,
};

View file

@ -1,6 +1,6 @@
// @flow
export type Reward = {
declare type Reward = {
created_at: string,
id: number,
reward_amount: number,

View file

@ -1,5 +1,4 @@
// @flow
import type { Dispatch as ReduxDispatch } from 'types/redux';
import * as ACTIONS from 'constants/action_types';
import {
DOWNLOADED,
@ -12,7 +11,7 @@ import {
SUGGESTED_FEATURED,
} from 'constants/subscriptions';
export type Subscription = {
declare type Subscription = {
channelName: string, // @CryptoCandor,
uri: string, // lbry://@CryptoCandor#9152f3b054f692076a6882d1b58a30e8781cc8e6
latest?: string, // substratum#b0ab143243020e7831fd070d9f871e1fda948620
@ -21,26 +20,26 @@ export type Subscription = {
// Tracking for new content
// i.e. If a subscription has a DOWNLOADING type, we will trigger an OS notification
// to tell users there is new content from their subscriptions
export type SubscriptionNotificationType = DOWNLOADED | DOWNLOADING | NOTIFY_ONLY;
declare type SubscriptionNotificationType = DOWNLOADED | DOWNLOADING | NOTIFY_ONLY;
export type UnreadSubscription = {
declare type UnreadSubscription = {
type: SubscriptionNotificationType,
uris: Array<string>,
};
export type UnreadSubscriptions = {
declare type UnreadSubscriptions = {
[string]: UnreadSubscription,
};
export type ViewMode = VIEW_LATEST_FIRST | VIEW_ALL;
declare type ViewMode = VIEW_LATEST_FIRST | VIEW_ALL;
export type SuggestedType = SUGGESTED_TOP_BID | SUGGESTED_TOP_SUBSCRIBED | SUGGESTED_FEATURED;
declare type SuggestedType = SUGGESTED_TOP_BID | SUGGESTED_TOP_SUBSCRIBED | SUGGESTED_FEATURED;
export type SuggestedSubscriptions = {
declare type SuggestedSubscriptions = {
[SuggestedType]: string,
};
export type SubscriptionState = {
declare type SubscriptionState = {
subscriptions: Array<Subscription>,
unread: UnreadSubscriptions,
loading: boolean,
@ -54,17 +53,17 @@ export type SubscriptionState = {
//
// Action types
//
export type DoChannelSubscribe = {
declare type DoChannelSubscribe = {
type: ACTIONS.CHANNEL_SUBSCRIBE,
data: Subscription,
};
export type DoChannelUnsubscribe = {
declare type DoChannelUnsubscribe = {
type: ACTIONS.CHANNEL_UNSUBSCRIBE,
data: Subscription,
};
export type DoUpdateSubscriptionUnreads = {
declare type DoUpdateSubscriptionUnreads = {
type: ACTIONS.UPDATE_SUBSCRIPTION_UNREADS,
data: {
channel: string,
@ -73,7 +72,7 @@ export type DoUpdateSubscriptionUnreads = {
},
};
export type DoRemoveSubscriptionUnreads = {
declare type DoRemoveSubscriptionUnreads = {
type: ACTIONS.REMOVE_SUBSCRIPTION_UNREADS,
data: {
channel: string,
@ -81,7 +80,7 @@ export type DoRemoveSubscriptionUnreads = {
},
};
export type SetSubscriptionLatest = {
declare type SetSubscriptionLatest = {
type: ACTIONS.SET_SUBSCRIPTION_LATEST,
data: {
subscription: Subscription,
@ -89,25 +88,25 @@ export type SetSubscriptionLatest = {
},
};
export type CheckSubscriptionStarted = {
declare type CheckSubscriptionStarted = {
type: ACTIONS.CHECK_SUBSCRIPTION_STARTED,
};
export type CheckSubscriptionCompleted = {
declare type CheckSubscriptionCompleted = {
type: ACTIONS.CHECK_SUBSCRIPTION_COMPLETED,
};
export type FetchedSubscriptionsSucess = {
declare type FetchedSubscriptionsSucess = {
type: ACTIONS.FETCH_SUBSCRIPTIONS_SUCCESS,
data: Array<Subscription>,
};
export type SetViewMode = {
declare type SetViewMode = {
type: ACTIONS.SET_VIEW_MODE,
data: ViewMode,
};
export type GetSuggestedSubscriptionsSuccess = {
declare type GetSuggestedSubscriptionsSuccess = {
type: ACTIONS.GET_SUGGESTED_SUBSCRIPTIONS_START,
data: SuggestedSubscriptions,
};

View file

@ -93,7 +93,6 @@
"electron-webpack": "^2.6.2",
"electron-window-state": "^4.1.1",
"eslint": "^5.15.2",
"eslint-config-airbnb": "^16.1.0",
"eslint-config-prettier": "^2.9.0",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-jsx": "^6.0.2",
@ -106,7 +105,7 @@
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-react": "^7.7.0",
"eslint-plugin-standard": "^4.0.0",
"flow-bin": "^0.94.0",
"flow-bin": "^0.97.0",
"flow-typed": "^2.3.0",
"formik": "^0.10.4",
"hast-util-sanitize": "^1.1.2",
@ -115,8 +114,8 @@
"jsmediatags": "^3.8.1",
"json-loader": "^0.5.4",
"lbry-format": "https://github.com/lbryio/lbry-format.git",
"lbry-redux": "lbryio/lbry-redux#d4c7dea65f7179974e9b96c863022fe7b049ff7d",
"lbryinc": "lbryio/lbryinc#4f2d4a50986bffab0b05d9f6cd7c2f0a856a0e02",
"lbry-redux": "lbryio/lbry-redux#cc42856676541120b088e4228c04246ba8ff3274",
"lbryinc": "lbryio/lbryinc#9665f2d1c818f1a86b2e5daab642f6879746f25f",
"lint-staged": "^7.0.2",
"localforage": "^1.7.1",
"lodash-es": "^4.17.11",
@ -186,7 +185,7 @@
"yarn": "^1.3"
},
"lbrySettings": {
"lbrynetDaemonVersion": "0.34.0",
"lbrynetDaemonVersion": "0.36.0",
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-OSNAME.zip",
"lbrynetDaemonDir": "static/daemon",
"lbrynetDaemonFileName": "lbrynet"

View file

@ -113,7 +113,12 @@ class Button extends React.PureComponent<Props> {
exact
to={path}
title={title}
onClick={e => e.stopPropagation()}
onClick={e => {
e.stopPropagation();
if (onClick) {
onClick();
}
}}
className={combinedClassName}
activeClassName={activeClass}
>

View file

@ -4,7 +4,6 @@ import CardMedia from 'component/cardMedia';
import TruncatedText from 'component/common/truncated-text';
import classnames from 'classnames';
import SubscribeButton from 'component/subscribeButton';
import type { Claim } from 'types/claim';
import { withRouter } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri';
@ -13,7 +12,7 @@ type Props = {
isResolvingUri: boolean,
totalItems: number,
size: string,
claim: ?Claim,
claim: ?ChannelClaim,
resolveUri: string => void,
history: { push: string => void },
};
@ -44,7 +43,7 @@ class ChannelTile extends React.PureComponent<Props> {
let subscriptionUri;
if (claim) {
channelName = claim.name;
subscriptionUri = `lbry://${claim.permanent_url}`;
subscriptionUri = claim.permanent_url;
}
const onClick = () => history.push(formatLbryUriForWeb(uri));

View file

@ -1,11 +1,15 @@
// @flow
import * as React from 'react';
import type { Price } from 'page/settings';
import { FormField } from './form-field';
type FormPrice = {
amount: ?number,
currency: string,
};
type Props = {
price: Price,
onChange: Price => void,
price: FormPrice,
onChange: FormPrice => void,
placeholder: number,
min: number,
disabled: boolean,
@ -23,7 +27,7 @@ export class FormFieldPrice extends React.PureComponent<Props> {
handleAmountChange(event: SyntheticInputEvent<*>) {
const { price, onChange } = this.props;
const amount = event.target.value ? parseFloat(event.target.value) : '';
const amount = event.target.value ? parseFloat(event.target.value) : undefined;
onChange({
currency: price.currency,
amount,

View file

@ -1,4 +1,5 @@
// @flow
import type { ElementRef } from 'react';
import React, { Suspense } from 'react';
import ReactDOMServer from 'react-dom/server';
import MarkdownPreview from 'component/common/markdown-preview';
@ -6,24 +7,24 @@ import 'easymde/dist/easymde.min.css';
import Toggle from 'react-toggle';
import { openEditorMenu, stopContextMenu } from 'util/context-menu';
const SimpleMDE = React.lazy(() => import(
/* webpackChunkName: "SimpleMDE" */
'react-simplemde-editor'
));
const SimpleMDE = React.lazy(() =>
import(/* webpackChunkName: "SimpleMDE" */
'react-simplemde-editor')
);
type Props = {
name: string,
label?: string,
render?: () => React.Node,
render?: () => React$Node,
prefix?: string,
postfix?: string,
error?: string | boolean,
helper?: string | React.Node,
helper?: string | React$Node,
type?: string,
onChange?: any => any,
defaultValue?: string | number,
placeholder?: string | number,
children?: React.Node,
children?: React$Node,
stretch?: boolean,
affixClass?: string, // class applied to prefix/postfix label
firstInList?: boolean, // at the top of a list, no padding top
@ -32,7 +33,7 @@ type Props = {
inputProps?: {
disabled?: boolean,
},
inputButton?: React.Node,
inputButton?: React$Node,
blockWrap: boolean,
};
@ -42,7 +43,7 @@ export class FormField extends React.PureComponent<Props> {
blockWrap: true,
};
input: { current: React.ElementRef<any> };
input: { current: ElementRef<any> };
constructor(props: Props) {
super(props);
@ -136,7 +137,7 @@ export class FormField extends React.PureComponent<Props> {
<div className="form-field--SimpleMDE" onContextMenu={stopContextMenu}>
<fieldset-section>
<label htmlFor={name}>{label}</label>
<Suspense fallback={<div></div>}>
<Suspense fallback={<div />}>
<SimpleMDE
{...inputProps}
id={name}

View file

@ -1,5 +1,4 @@
// @flow
import type { Node } from 'react';
import * as ICONS from 'constants/icons';
import React from 'react';
@ -9,7 +8,7 @@ type IconProps = {
};
// Returns a react component
const buildIcon = (iconStrokes: Node, options?: {} = {}) => (props: IconProps) => {
const buildIcon = (iconStrokes: React$Node, options?: {} = {}) => (props: IconProps) => {
const { size = 24, color = 'currentColor', ...otherProps } = props;
return (
<svg

View file

@ -1,5 +1,5 @@
// @flow
import React, { PureComponent, Node } from 'react';
import React, { PureComponent } from 'react';
import classnames from 'classnames';
import Button from 'component/button';
@ -8,7 +8,7 @@ import Button from 'component/button';
// add props for collapsed height
type Props = {
children: Node | Array<Node>,
children: React$Node | Array<React$Node>,
};
type State = {

View file

@ -7,6 +7,9 @@ import {
makeSelectIsUriResolving,
makeSelectClaimIsMine,
makeSelectClaimIsPending,
makeSelectThumbnailForUri,
makeSelectTitleForUri,
makeSelectClaimIsNsfw,
} from 'lbry-redux';
import { selectRewardContentClaimIds } from 'lbryinc';
import { makeSelectContentPositionForUri } from 'redux/selectors/content';
@ -27,6 +30,9 @@ const select = (state, props) => ({
position: makeSelectContentPositionForUri(props.uri)(state),
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
isNew: makeSelectIsNew(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
nsfw: makeSelectClaimIsNsfw(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -1,5 +1,4 @@
// @flow
import type { Claim, Metadata } from 'types/claim';
import * as icons from 'constants/icons';
import * as React from 'react';
import { normalizeURI, convertToShareLink } from 'lbry-redux';
@ -13,12 +12,13 @@ import { openCopyLinkMenu } from 'util/context-menu';
import DateTime from 'component/dateTime';
import { withRouter } from 'react-router-dom';
import { formatLbryUriForWeb } from 'util/uri';
import get from 'lodash.get';
type Props = {
uri: string,
claim: ?Claim,
claim: ?StreamClaim,
fileInfo: ?{},
metadata: ?Metadata,
metadata: ?StreamMetadata,
rewardedContentClaimIds: Array<string>,
obscureNsfw: boolean,
claimIsMine: boolean,
@ -30,6 +30,9 @@ type Props = {
placeholder: boolean,
preventResolve: boolean,
history: { push: string => void },
thumbnail: string,
title: string,
nsfw: boolean,
};
class FileCard extends React.PureComponent<Props> {
@ -72,6 +75,9 @@ class FileCard extends React.PureComponent<Props> {
isResolvingUri,
placeholder,
history,
thumbnail,
title,
nsfw,
} = this.props;
const abandoned = !isResolvingUri && !claim && !pending && !placeholder;
@ -92,14 +98,13 @@ class FileCard extends React.PureComponent<Props> {
);
}
const shouldHide = !claimIsMine && !pending && obscureNsfw && metadata && metadata.nsfw;
// fix to use tags - one of many nsfw tags...
const shouldHide = !claimIsMine && !pending && obscureNsfw && nsfw;
if (shouldHide) {
return null;
}
const uri = !pending ? normalizeURI(this.props.uri) : this.props.uri;
const title = metadata && metadata.title ? metadata.title : uri;
const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null;
const isRewardContent = claim && rewardedContentClaimIds.includes(claim.claim_id);
const height = claim && claim.height;
const handleContextMenu = event => {

View file

@ -1,7 +1,4 @@
// @flow
import type { Claim, Metadata } from 'types/claim';
import type { FileInfo } from 'types/file_info';
import type { Node } from 'react';
import React, { Fragment, PureComponent } from 'react';
import { Lbryio } from 'lbryinc';
import MarkdownPreview from 'component/common/markdown-preview';
@ -10,13 +7,13 @@ import Expandable from 'component/expandable';
import path from 'path';
type Props = {
claim: Claim,
fileInfo: FileInfo,
metadata: Metadata,
claim: StreamClaim,
fileInfo: FileListItem,
metadata: StreamMetadata,
openFolder: string => void,
contentType: string,
clickCommentButton: () => void,
showSnackBar: Node => void,
showSnackBar: React$Node => void,
hasClickedComment: boolean,
user: ?any,
};
@ -59,7 +56,7 @@ class FileDetails extends PureComponent<Props> {
);
}
const { description, language, license } = metadata;
const { description, languages, license } = metadata;
const mediaType = contentType || 'unknown';
let downloadPath =
@ -90,9 +87,9 @@ class FileDetails extends PureComponent<Props> {
{mediaType}
</div>
<div>
{__('Language')}
{__('Languages')}
{': '}
{language}
{languages ? languages.join(' ') : null}
</div>
<div>
{__('License')}
@ -106,7 +103,11 @@ class FileDetails extends PureComponent<Props> {
<Button
constrict
button="link"
onClick={() => openFolder(downloadPath)}
onClick={() => {
if (downloadPath) {
openFolder(downloadPath);
}
}}
label={downloadNote || downloadPath}
/>
</div>

View file

@ -1,5 +1,4 @@
// @flow
import type { Claim } from 'types/claim';
import * as ICONS from 'constants/icons';
import React from 'react';
import Button from 'component/button';
@ -7,7 +6,7 @@ import ToolTip from 'component/common/tooltip';
import analytics from 'analytics';
type Props = {
claim: Claim,
claim: StreamClaim,
uri: string,
downloading: boolean,
fileInfo: ?{

View file

@ -3,13 +3,12 @@ import * as React from 'react';
import { buildURI, SORT_OPTIONS } from 'lbry-redux';
import { FormField, Form } from 'component/common/form';
import FileCard from 'component/fileCard';
import type { FileInfo } from 'types/file_info';
type Props = {
hideFilter: boolean,
sortByHeight?: boolean,
claimsById: Array<{}>,
fileInfos: Array<FileInfo>,
claimsById: Array<StreamClaim>,
fileInfos: Array<FileListItem>,
sortBy: string,
page?: string,
setFileListSort: (?string, string) => void,
@ -29,47 +28,47 @@ class FileList extends React.PureComponent<Props> {
[SORT_OPTIONS.DATE_NEW]: fileInfos =>
this.props.sortByHeight
? fileInfos.sort((fileInfo1, fileInfo2) => {
if (fileInfo1.confirmations < 1) {
return -1;
} else if (fileInfo2.confirmations < 1) {
return 1;
}
if (fileInfo1.confirmations < 1) {
return -1;
} else if (fileInfo2.confirmations < 1) {
return 1;
}
const height1 = this.props.claimsById[fileInfo1.claim_id]
? this.props.claimsById[fileInfo1.claim_id].height
: 0;
const height2 = this.props.claimsById[fileInfo2.claim_id]
? this.props.claimsById[fileInfo2.claim_id].height
: 0;
const height1 = this.props.claimsById[fileInfo1.claim_id]
? this.props.claimsById[fileInfo1.claim_id].height
: 0;
const height2 = this.props.claimsById[fileInfo2.claim_id]
? this.props.claimsById[fileInfo2.claim_id].height
: 0;
if (height1 !== height2) {
// flipped because heigher block height is newer
return height2 - height1;
}
if (height1 !== height2) {
// flipped because heigher block height is newer
return height2 - height1;
}
if (fileInfo1.absolute_channel_position && fileInfo2.absolute_channel_position) {
return fileInfo1.absolute_channel_position - fileInfo2.absolute_channel_position;
}
if (fileInfo1.absolute_channel_position && fileInfo2.absolute_channel_position) {
return fileInfo1.absolute_channel_position - fileInfo2.absolute_channel_position;
}
return 0;
})
return 0;
})
: [...fileInfos].reverse(),
[SORT_OPTIONS.DATE_OLD]: fileInfos =>
this.props.sortByHeight
? fileInfos.slice().sort((fileInfo1, fileInfo2) => {
const height1 = this.props.claimsById[fileInfo1.claim_id]
? this.props.claimsById[fileInfo1.claim_id].height
: 999999;
const height2 = this.props.claimsById[fileInfo2.claim_id]
? this.props.claimsById[fileInfo2.claim_id].height
: 999999;
if (height1 < height2) {
return -1;
} else if (height1 > height2) {
return 1;
}
return 0;
})
const height1 = this.props.claimsById[fileInfo1.claim_id]
? this.props.claimsById[fileInfo1.claim_id].height
: 999999;
const height2 = this.props.claimsById[fileInfo2.claim_id]
? this.props.claimsById[fileInfo2.claim_id].height
: 999999;
if (height1 < height2) {
return -1;
} else if (height1 > height2) {
return 1;
}
return 0;
})
: fileInfos,
[SORT_OPTIONS.TITLE]: fileInfos =>
fileInfos.slice().sort((fileInfo1, fileInfo2) => {
@ -80,7 +79,7 @@ class FileList extends React.PureComponent<Props> {
return metadata.title || claimName;
} else if (value) {
// published claim
const { title } = value.stream.metadata;
const { title } = value.stream;
return title || name;
}
// Invalid claim
@ -109,14 +108,11 @@ class FileList extends React.PureComponent<Props> {
};
}
getChannelSignature = (fileInfo: FileInfo) => {
getChannelSignature = (fileInfo: { pending: boolean } & FileListItem) => {
if (fileInfo.pending) {
return undefined;
}
if (fileInfo.value) {
return fileInfo.value.publisherSignature.certificateId;
}
return fileInfo.channel_claim_id;
};

View file

@ -1,46 +1,45 @@
// @flow
import type { Claim } from 'types/claim';
import { remote } from 'electron';
import React, { Suspense } from 'react';
import LoadingScreen from 'component/common/loading-screen';
import VideoViewer from 'component/viewers/videoViewer';
const AudioViewer = React.lazy(() => import(
/* webpackChunkName: "audioViewer" */
'component/viewers/audioViewer'
));
const AudioViewer = React.lazy<*>(() =>
import(/* webpackChunkName: "audioViewer" */
'component/viewers/audioViewer')
);
const DocumentViewer = React.lazy(() => import(
/* webpackChunkName: "documentViewer" */
'component/viewers/documentViewer'
));
const DocumentViewer = React.lazy<*>(() =>
import(/* webpackChunkName: "documentViewer" */
'component/viewers/documentViewer')
);
const DocxViewer = React.lazy(() => import(
/* webpackChunkName: "docxViewer" */
'component/viewers/docxViewer'
));
const DocxViewer = React.lazy<*>(() =>
import(/* webpackChunkName: "docxViewer" */
'component/viewers/docxViewer')
);
const HtmlViewer = React.lazy(() => import(
/* webpackChunkName: "htmlViewer" */
'component/viewers/htmlViewer'
));
const HtmlViewer = React.lazy<*>(() =>
import(/* webpackChunkName: "htmlViewer" */
'component/viewers/htmlViewer')
);
const PdfViewer = React.lazy(() => import(
/* webpackChunkName: "pdfViewer" */
'component/viewers/pdfViewer'
));
const PdfViewer = React.lazy<*>(() =>
import(/* webpackChunkName: "pdfViewer" */
'component/viewers/pdfViewer')
);
// @if TARGET='app'
const ThreeViewer = React.lazy(() => import(
/* webpackChunkName: "threeViewer" */
'component/viewers/threeViewer'
));
const ThreeViewer = React.lazy<*>(() =>
import(/* webpackChunkName: "threeViewer" */
'component/viewers/threeViewer')
);
// @endif
type Props = {
mediaType: string,
poster?: string,
claim: Claim,
claim: StreamClaim,
source: {
stream: string => void,
fileName: string,
@ -119,8 +118,6 @@ class FileRender extends React.PureComponent<Props> {
renderViewer() {
const { source, mediaType, currentTheme, poster, claim } = this.props;
console.log('mediaType', mediaType);
// Extract relevant data to render file
const { stream, fileType, contentType, downloadPath, fileName } = source;
@ -155,11 +152,7 @@ class FileRender extends React.PureComponent<Props> {
/>
),
audio: (
<AudioViewer
claim={claim}
source={{ downloadPath, fileName }}
contentType={contentType}
/>
<AudioViewer claim={claim} source={{ downloadPath, fileName }} contentType={contentType} />
),
// Add routes to viewer...
};
@ -182,7 +175,7 @@ class FileRender extends React.PureComponent<Props> {
// @if TARGET='web'
// temp workaround to disabled paid content on web
if (claim && claim.value.stream.metadata.fee && claim.value.stream.metadata.fee.amount > 0) {
if (claim && claim.value.fee && claim.value.fee.amount > 0) {
const paidMessage = __(
'Currently, only free content is available on lbry.tv. Try viewing it in the desktop app.'
);
@ -200,12 +193,9 @@ class FileRender extends React.PureComponent<Props> {
}
render() {
console.log('RENDER')
return (
<div className="file-render">
<React.Suspense fallback={<div></div>}>
{this.renderViewer()}
</React.Suspense>
<React.Suspense fallback={<div />}>{this.renderViewer()}</React.Suspense>
</div>
);
}

View file

@ -6,6 +6,9 @@ import {
makeSelectFileInfoForUri,
makeSelectIsUriResolving,
makeSelectClaimIsMine,
makeSelectThumbnailForUri,
makeSelectTitleForUri,
makeSelectClaimIsNsfw,
} from 'lbry-redux';
import { selectRewardContentClaimIds } from 'lbryinc';
import { selectShowNsfw } from 'redux/selectors/settings';
@ -23,6 +26,9 @@ const select = (state, props) => ({
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
isSubscribed: makeSelectIsSubscribed(props.uri)(state),
isNew: makeSelectIsNew(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
nsfw: makeSelectClaimIsNsfw(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -1,5 +1,4 @@
// @flow
import type { Claim, Metadata } from 'types/claim';
import * as ICONS from 'constants/icons';
import React, { Fragment } from 'react';
import { normalizeURI, parseURI } from 'lbry-redux';
@ -22,8 +21,8 @@ type Props = {
uri: string,
isResolvingUri: boolean,
rewardedContentClaimIds: Array<string>,
claim: ?Claim,
metadata: ?Metadata,
claim: ?StreamClaim,
metadata: ?StreamMetadata,
resolveUri: string => void,
clearPublish: () => void,
updatePublishForm: ({}) => void,
@ -33,6 +32,9 @@ type Props = {
isSubscribed: boolean,
isNew: boolean,
history: { push: string => void },
thumbnail: ?string,
title: ?string,
nsfw: boolean,
};
class FileTile extends React.PureComponent<Props> {
@ -93,6 +95,9 @@ class FileTile extends React.PureComponent<Props> {
displayHiddenMessage,
size,
history,
thumbnail,
title,
nsfw,
} = this.props;
if (!claim && isResolvingUri) {
@ -112,7 +117,7 @@ class FileTile extends React.PureComponent<Props> {
);
}
const shouldHide = !claimIsMine && obscureNsfw && metadata && metadata.nsfw;
const shouldHide = !claimIsMine && obscureNsfw && nsfw;
if (shouldHide) {
return displayHiddenMessage ? (
<span className="help">
@ -126,9 +131,6 @@ class FileTile extends React.PureComponent<Props> {
const uri = normalizeURI(this.props.uri);
const isClaimed = !!claim;
const description = isClaimed && metadata && metadata.description ? metadata.description : '';
const title =
isClaimed && metadata && metadata.title ? metadata.title : parseURI(uri).contentName;
const thumbnail = metadata && metadata.thumbnail ? metadata.thumbnail : null;
let height;
let name;
@ -211,7 +213,7 @@ class FileTile extends React.PureComponent<Props> {
clearPublish(); // to remove any existing publish data
updatePublishForm({ name: claimName }); // to populate the name
history.push('/publish');
history.push('/$/publish');
}}
/>
}

View file

@ -13,6 +13,8 @@ import {
makeSelectDownloadingForUri,
selectSearchBarFocused,
makeSelectFirstRecommendedFileForUri,
makeSelectClaimIsNsfw,
makeSelectThumbnailForUri,
} from 'lbry-redux';
import { makeSelectClientSetting, selectShowNsfw } from 'redux/selectors/settings';
import { selectPlayingUri, makeSelectContentPositionForUri } from 'redux/selectors/content';
@ -35,6 +37,8 @@ const select = (state, props) => ({
searchBarFocused: selectSearchBarFocused(state),
fileInfoErrors: selectFileInfoErrors(state),
nextFileToPlay: makeSelectFirstRecommendedFileForUri(props.uri)(state),
nsfw: makeSelectClaimIsNsfw(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -1,6 +1,5 @@
// @flow
import 'babel-polyfill';
import type { Claim } from 'types/claim';
import * as React from 'react';
// @if TARGET='app'
import { remote } from 'electron';
@ -19,7 +18,7 @@ type Props = {
position: ?number,
downloadPath: string,
fileName: string,
claim: Claim,
claim: StreamClaim,
onStartCb: ?() => void,
onFinishCb: ?() => void,
savePosition: number => void,
@ -263,11 +262,6 @@ class MediaPlayer extends React.PureComponent<Props, State> {
// This files are supported using a custom viewer
const { mediaType, contentType } = this.props;
console.log({
mediaType,
contentType
})
return (
MediaPlayer.FILE_MEDIA_TYPES.indexOf(mediaType) > -1 ||
MediaPlayer.SANDBOX_TYPES.indexOf(contentType) > -1
@ -362,13 +356,6 @@ class MediaPlayer extends React.PureComponent<Props, State> {
const isPlayableType = this.playableType();
const { isLoading, loadingStatus } = this.showLoadingScreen(isFileType, isPlayableType);
console.log({
mediaType,
fileSource,
isFileReady,
isFileType
})
return (
<React.Fragment>
{loadingStatus && <LoadingScreen status={loadingStatus} spinner={isLoading} />}

View file

@ -3,14 +3,13 @@ import * as PAGES from 'constants/pages';
import React, { Suspense } from 'react';
import classnames from 'classnames';
import analytics from 'analytics';
import type { Claim } from 'types/claim';
import LoadingScreen from 'component/common/loading-screen';
import PlayButton from './internal/play-button';
const Player = React.lazy(() => import(
/* webpackChunkName: "player-legacy" */
'./internal/player'
));
const Player = React.lazy(() =>
import(/* webpackChunkName: "player-legacy" */
'./internal/player')
);
const SPACE_BAR_KEYCODE = 32;
@ -27,10 +26,6 @@ type Props = {
fileInfoErrors: ?{
[string]: boolean,
},
metadata: ?{
nsfw: boolean,
thumbnail: string,
},
autoplay: boolean,
isLoading: boolean,
isDownloading: boolean,
@ -38,7 +33,7 @@ type Props = {
contentType: string,
changeVolume: number => void,
volume: number,
claim: Claim,
claim: StreamClaim,
uri: string,
savePosition: (string, string, number) => void,
position: ?number,
@ -52,6 +47,8 @@ type Props = {
navigate: (string, {}) => void,
costInfo: ?{ cost: number },
insufficientCredits: boolean,
nsfw: boolean,
thumbnail: ?string,
};
class FileViewer extends React.PureComponent<Props> {
@ -135,9 +132,9 @@ class FileViewer extends React.PureComponent<Props> {
}
handleAutoplay = (props: Props) => {
const { autoplay, playingUri, fileInfo, costInfo, isDownloading, uri, metadata } = props;
const { autoplay, playingUri, fileInfo, costInfo, isDownloading, uri, nsfw } = props;
const playable = autoplay && playingUri !== uri && metadata && !metadata.nsfw;
const playable = autoplay && playingUri !== uri && !nsfw;
if (playable && costInfo && costInfo.cost === 0 && !fileInfo && !isDownloading) {
this.playContent();
@ -184,7 +181,7 @@ class FileViewer extends React.PureComponent<Props> {
}
}
fireAnalyticsEvent(claim: Claim, startTime: ?number, playTime: ?number) {
fireAnalyticsEvent(claim: StreamClaim, startTime: ?number, playTime: ?number) {
const { claimRewards } = this.props;
const { name, claim_id: claimId, txid, nout } = claim;
@ -214,7 +211,6 @@ class FileViewer extends React.PureComponent<Props> {
render() {
const {
metadata,
isLoading,
isDownloading,
playingUri,
@ -230,19 +226,21 @@ class FileViewer extends React.PureComponent<Props> {
obscureNsfw,
mediaType,
insufficientCredits,
thumbnail,
nsfw,
} = this.props;
const isPlaying = playingUri === uri;
let isReadyToPlay = false;
// @if TARGET='app'
const isReadyToPlay = fileInfo && fileInfo.download_path && fileInfo.written_bytes > 0;
isReadyToPlay = fileInfo && fileInfo.download_path && fileInfo.written_bytes > 0;
// @endif
// @if TARGET='web'
// try to play immediately on web, we don't need to call file_list since we are streaming from reflector
// $FlowFixMe
const isReadyToPlay = isPlaying;
isReadyToPlay = isPlaying;
// @endif
const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw;
const shouldObscureNsfw = obscureNsfw && nsfw;
let loadStatusMessage = '';
if (fileInfo && fileInfo.completed && (!fileInfo.download_path || !fileInfo.written_bytes)) {
@ -255,14 +253,13 @@ class FileViewer extends React.PureComponent<Props> {
loadStatusMessage = __('Downloading stream... not long left now!');
}
const poster = metadata && metadata.thumbnail;
const layoverClass = classnames('content__cover', {
'card__media--nsfw': shouldObscureNsfw,
'card__media--disabled': insufficientCredits,
});
const layoverStyle =
!shouldObscureNsfw && poster ? { backgroundImage: `url("${poster}")` } : {};
!shouldObscureNsfw && thumbnail ? { backgroundImage: `url("${thumbnail}")` } : {};
return (
<div className={classnames('video', {}, className)}>
@ -273,10 +270,10 @@ class FileViewer extends React.PureComponent<Props> {
<LoadingScreen status={loadStatusMessage} />
</div>
) : (
<Suspense fallback={<div></div>}>
<Suspense fallback={<div />}>
<Player
fileName={fileInfo.file_name}
poster={poster}
poster={thumbnail}
downloadPath={fileInfo.download_path}
mediaType={mediaType}
contentType={contentType}

View file

@ -1,5 +1,4 @@
// @flow
import type { Reward } from 'types/reward';
import React from 'react';
import RewardLink from 'component/rewardLink';
import Yrbl from 'component/yrbl';

View file

@ -1,6 +1,5 @@
// @flow
import React from 'react';
import type { Claim } from 'types/claim';
import moment from 'moment';
import classnames from 'classnames';
import Button from 'component/button';
@ -11,7 +10,7 @@ import { formatLbryUriForWeb } from 'util/uri';
type Props = {
lastViewed: number,
uri: string,
claim: ?Claim,
claim: ?StreamClaim,
selected: boolean,
onSelect?: () => void,
resolveUri: string => void,
@ -37,9 +36,9 @@ class NavigationHistoryItem extends React.PureComponent<Props> {
let name;
let title;
if (claim && claim.value && claim.value.stream) {
if (claim && claim.value) {
({ name } = claim);
({ title } = claim.value.stream.metadata);
({ title } = claim.value);
}
const navigatePath = formatLbryUriForWeb(uri);

View file

@ -2,11 +2,10 @@
import * as React from 'react';
import Button from 'component/button';
import { buildURI } from 'lbry-redux';
import type { Claim } from 'types/claim';
type Props = {
uri: ?string,
myClaimForUri: ?Claim,
myClaimForUri: ?StreamClaim,
isStillEditing: boolean,
onEditMyClaim: (any, string) => void,
};

View file

@ -1,6 +1,4 @@
// @flow
import type { Claim } from 'types/claim';
import type { PublishParams, UpdatePublishFormData } from 'redux/reducers/publish';
import { COPYRIGHT, OTHER } from 'constants/licenses';
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, MINIMUM_PUBLISH_BID } from 'constants/claim';
import * as ICONS from 'constants/icons';
@ -40,7 +38,7 @@ type Props = {
nameError: ?string,
isResolvingUri: boolean,
winningBidForClaimUri: number,
myClaimForUri: ?Claim,
myClaimForUri: ?StreamClaim,
licenseType: string,
otherLicenseDescription: ?string,
licenseUrl: ?string,
@ -232,12 +230,6 @@ class PublishForm extends React.PureComponent<Props> {
isStillEditing: this.props.isStillEditing,
};
// Editing a claim
if (!filePath && myClaimForUri && myClaimForUri.value) {
const { source } = myClaimForUri.value.stream;
publishParams.sources = source;
}
publish(publishParams);
}

View file

@ -1,11 +1,10 @@
// @flow
import React from 'react';
import FileTile from 'component/fileTile';
import type { Claim } from 'types/claim';
type Props = {
uri: string,
claim: ?Claim,
claim: ?StreamClaim,
recommendedContent: Array<string>,
isSearching: boolean,
search: string => void,
@ -37,10 +36,12 @@ export default class RecommendedContent extends React.PureComponent<Props> {
getRecommendedContent() {
const { claim, search } = this.props;
if (claim && claim.value && claim.value.stream && claim.value.stream.metadata) {
const { title } = claim.value.stream.metadata;
search(title);
this.didSearch = true;
if (claim && claim.value && claim.value) {
const { title } = claim.value;
if (title) {
search(title);
this.didSearch = true;
}
}
}

View file

@ -1,7 +1,12 @@
import { connect } from 'react-redux';
import SelectChannel from './view';
import { selectBalance, selectMyChannelClaims, selectFetchingMyChannels } from 'lbry-redux';
import { doFetchChannelListMine, doCreateChannel } from 'redux/actions/content';
import {
selectBalance,
selectMyChannelClaims,
selectFetchingMyChannels,
doFetchChannelListMine,
doCreateChannel,
} from 'lbry-redux';
const select = state => ({
channels: selectMyChannelClaims(state),

View file

@ -1,5 +1,4 @@
// @flow
import type { Claim } from 'types/claim';
import * as ICONS from 'constants/icons';
import React from 'react';
import Button from 'component/button';
@ -7,7 +6,7 @@ import CopyableText from 'component/copyableText';
import ToolTip from 'component/common/tooltip';
type Props = {
claim: Claim,
claim: StreamClaim,
onDone: () => void,
speechShareable: boolean,
isChannel: boolean,
@ -31,8 +30,7 @@ class SocialShare extends React.PureComponent<Props> {
const { claim_id: claimId, name: claimName, channel_name: channelName, value } = claim;
const { speechShareable, onDone } = this.props;
const channelClaimId =
value && value.publisherSignature && value.publisherSignature.certificateId;
const channelClaimId = claim.signing_channel && claim.signing_channel.claim_id;
const getSpeechUri = (): string => {
if (isChannel) {

View file

@ -1,5 +1,4 @@
// @flow
import type { Status } from 'types/status';
import * as React from 'react';
import * as MODALS from 'constants/modal_types';
import { Lbry } from 'lbry-redux';
@ -97,11 +96,11 @@ export default class SplashScreen extends React.PureComponent<Props, State> {
});
}
updateStatusCallback(status: Status) {
updateStatusCallback(status: StatusResponse) {
const { notifyUnlockWallet, authenticate, modal } = this.props;
const { launchedModal } = this.state;
if (status.error) {
if (status.connection_status.code !== 'connected') {
this.setState({ error: true });
return;
}

View file

@ -1,5 +1,4 @@
// @flow
import type { Transaction } from 'types/transaction';
import * as TXN_TYPES from 'constants/transaction_types';
import * as ICONS from 'constants/icons';
import React from 'react';

View file

@ -1,9 +1,8 @@
// @flow
import type { Transaction } from 'types/transaction';
import * as icons from 'constants/icons';
import * as MODALS from 'constants/modal_types';
import * as React from 'react';
import { List } from 'react-virtualized'
import { List } from 'react-virtualized';
import { FormField, Form } from 'component/common/form';
import Button from 'component/button';
import FileExporter from 'component/common/file-exporter';

View file

@ -1,5 +1,4 @@
// @flow
import type { Transaction } from 'types/transaction';
import * as icons from 'constants/icons';
import React, { Fragment } from 'react';
import BusyIndicator from 'component/common/busy-indicator';

View file

@ -2,12 +2,13 @@
import React from 'react';
import Button from 'component/button';
import { buildURI } from 'lbry-redux';
import type { Claim } from 'types/claim';
type Props = {
isResolvingUri: boolean,
claim: Claim,
channelUri: ?string,
link: ?boolean,
claim: ?StreamClaim,
channelClaim: ?ChannelClaim,
// Lint thinks we aren't using these, even though we are.
// Possibly because the resolve function is an arrow function that is passed in props?
resolveUri: string => void,
@ -32,25 +33,23 @@ class UriIndicator extends React.PureComponent<Props> {
};
render() {
const { claim, link, isResolvingUri } = this.props;
const { link, isResolvingUri, claim } = this.props;
if (!claim) {
return <span className="empty">{isResolvingUri ? 'Validating...' : 'Unused'}</span>;
}
const { channel_name: channelName, signature_is_valid: signatureIsValid, value } = claim;
const channelClaimId =
value && value.publisherSignature && value.publisherSignature.certificateId;
if (!channelName) {
if (!claim.signing_channel) {
return <span className="channel-name">Anonymous</span>;
}
const { name, claim_id: claimId } = claim.signing_channel;
let channelLink;
if (signatureIsValid) {
channelLink = link ? buildURI({ channelName, claimId: channelClaimId }) : false;
if (claim.is_channel_signature_valid) {
channelLink = link ? buildURI({ channelName: name, claimId }) : false;
}
const inner = <span className="channel-name">{channelName}</span>;
const inner = <span className="channel-name">{name}</span>;
if (!channelLink) {
return inner;

View file

@ -1,5 +1,3 @@
// @flow
import type { Claim } from 'types/claim';
import React from 'react';
import * as ICONS from 'constants/icons';
import Button from 'component/button';
@ -15,18 +13,7 @@ import styles from './audioViewer.module.scss';
const isButterchurnSupported = detectButterchurnSupport();
const EQ_BANDS_SIMPLE = [
55,
150,
250,
400,
500,
1000,
2000,
4000,
8000,
16000,
]
const EQ_BANDS_SIMPLE = [55, 150, 250, 400, 500, 1000, 2000, 4000, 8000, 16000];
/*
const EQ_LOWSHELF = EQ_BANDS_SIMPLE.shift();
const EQ_HIGHSHELF = EQ_BANDS_SIMPLE.pop();
@ -41,15 +28,15 @@ const eqFilters = EQ.map(function(band) {
});
*/
type Props = {
source: {
downloadPath: string,
fileName: string,
},
contentType: string,
poster?: string,
claim: Claim,
};
// type Props = {
// source: {
// downloadPath: string,
// fileName: string,
// },
// contentType: string,
// poster?: string,
// claim: StreamClaim,
// };
const presets = [
require('butterchurn-presets/presets/converted/Flexi - when monopolies were the future [simple warp + non-reactive moebius].json'),
@ -61,9 +48,9 @@ const presets = [
require('butterchurn-presets/presets/converted/Zylot - Crosshair Dimension (Light of Ages).json'),
];
class AudioVideoViewer extends React.PureComponent<Props> {
audioNode: ?HTMLAudioElement;
player: ?{ dispose: () => void };
class AudioVideoViewer extends React.PureComponent {
// audioNode: ?HTMLAudioElement;
// player: ?{ dispose: () => void };
state = {
playing: false,
@ -107,12 +94,16 @@ class AudioVideoViewer extends React.PureComponent<Props> {
audioSource.connect(audioContext.destination);
if (isButterchurnSupported) {
const visualizer = me.visualizer = butterchurn.createVisualizer(audioContext, me.canvasNode, {
height: canvasHeight,
width: canvasWidth,
pixelRatio: window.devicePixelRatio || 1,
textureRatio: 1,
});
const visualizer = (me.visualizer = butterchurn.createVisualizer(
audioContext,
me.canvasNode,
{
height: canvasHeight,
width: canvasWidth,
pixelRatio: window.devicePixelRatio || 1,
textureRatio: 1,
}
));
visualizer.connectAudio(audioSource);
visualizer.loadPreset(presets[Math.floor(Math.random() * presets.length)], 2.0);
@ -120,10 +111,10 @@ class AudioVideoViewer extends React.PureComponent<Props> {
me._frameCycle = () => {
requestAnimationFrame(me._frameCycle);
if(me.state.enableMilkdrop === true) {
if (me.state.enableMilkdrop === true) {
visualizer.render();
}
}
};
me._frameCycle();
}
@ -145,12 +136,7 @@ class AudioVideoViewer extends React.PureComponent<Props> {
jsmediatags.Config.setDisallowedXhrHeaders(['If-Modified-Since', 'Range']);
jsmediatags.read(path, {
onSuccess: function(result) {
const {
album,
artist,
title,
picture
} = result.tags;
const { album, artist, title, picture } = result.tags;
if (picture) {
const byteArray = new Uint8Array(picture.data);
@ -169,7 +155,7 @@ class AudioVideoViewer extends React.PureComponent<Props> {
},
onError: function(error) {
console.log(':(', error.type, error.info);
}
},
});
}
@ -203,18 +189,26 @@ class AudioVideoViewer extends React.PureComponent<Props> {
const path = `https://api.lbry.tv/content/claims/${claim.name}/${claim.claim_id}/stream.mp4`;
const playButton = (
<div onClick={()=>{
<div
onClick={() => {
const audioNode = this.audioNode;
if (audioNode.paused) {
audioNode.play();
} else {
audioNode.pause();
}
}} className={playing ? styles.playButtonPause : styles.playButtonPlay}></div>
}}
className={playing ? styles.playButtonPause : styles.playButtonPlay}
/>
);
return (
<div className={userActive ? styles.userActive : styles.wrapper} onMouseEnter={()=>me.setState({ userActive: true })} onMouseLeave={()=>me.setState({ userActive: false })} onContextMenu={stopContextMenu}>
<div
className={userActive ? styles.userActive : styles.wrapper}
onMouseEnter={() => me.setState({ userActive: true })}
onMouseLeave={() => me.setState({ userActive: false })}
onContextMenu={stopContextMenu}
>
<div className={enableMilkdrop ? styles.containerWithMilkdrop : styles.container}>
<div style={{ position: 'absolute', top: 0, right: 0 }}>
<Tooltip onComponent body={__('Toggle Visualizer')}>
@ -226,9 +220,12 @@ class AudioVideoViewer extends React.PureComponent<Props> {
}
// Get new preset
this.visualizer.loadPreset(presets[Math.floor(Math.random() * presets.length)], 2.0);
this.visualizer.loadPreset(
presets[Math.floor(Math.random() * presets.length)],
2.0
);
this.setState({ enableMilkdrop: !enableMilkdrop })
this.setState({ enableMilkdrop: !enableMilkdrop });
}}
/>
</Tooltip>
@ -251,24 +248,56 @@ class AudioVideoViewer extends React.PureComponent<Props> {
/>
</Tooltip>
</div>
<div ref={node => (this.waveNode = node)} className={styles.wave}></div>
<div ref={node => (this.waveNode = node)} className={styles.wave} />
<div className={styles.infoContainer}>
<div className={renderArt ? styles.infoArtContainer : styles.infoArtContainerHidden}>
<img className={styles.infoArtImage} ref={node => (this.artNode = node)} />
{renderArt && playButton}
</div>
<div className={showSongDetails ? (renderArt ? styles.songDetailsContainer : styles.songDetailsContainerNoArt) : styles.songDetailsContainerHidden}>
<div
className={
showSongDetails
? renderArt
? styles.songDetailsContainer
: styles.songDetailsContainerNoArt
: styles.songDetailsContainerHidden
}
>
<div className={renderArt ? styles.songDetails : styles.songDetailsNoArt}>
{artist && <div className={styles.detailsLineArtist}><Button icon={ICONS.MUSIC_ARTIST} className={styles.detailsIconArtist} />{artist}</div>}
{title && <div className={styles.detailsLineSong}><Button icon={ICONS.MUSIC_SONG} className={styles.detailsIconSong} />{title}</div>}
{album && <div className={styles.detailsLineAlbum}><Button icon={ICONS.MUSIC_ALBUM} className={styles.detailsIconAlbum} />{album}</div>}
{artist && (
<div className={styles.detailsLineArtist}>
<Button icon={ICONS.MUSIC_ARTIST} className={styles.detailsIconArtist} />
{artist}
</div>
)}
{title && (
<div className={styles.detailsLineSong}>
<Button icon={ICONS.MUSIC_SONG} className={styles.detailsIconSong} />
{title}
</div>
)}
{album && (
<div className={styles.detailsLineAlbum}>
<Button icon={ICONS.MUSIC_ALBUM} className={styles.detailsIconAlbum} />
{album}
</div>
)}
</div>
</div>
</div>
{!renderArt && <div className={styles.playButtonDetachedContainer}>{playButton}</div>}
</div>
<canvas ref={node => (this.canvasNode = node)} className={enableMilkdrop ? styles.milkdrop : styles.milkdropDisabled} />
<audio ref={node => (this.audioNode = node)} src={path} style={{ position: 'absolute', top: '-100px' }} onPlay={()=>this.setState({ playing: true })} onPause={()=>this.setState({ playing: false })} />
<canvas
ref={node => (this.canvasNode = node)}
className={enableMilkdrop ? styles.milkdrop : styles.milkdropDisabled}
/>
<audio
ref={node => (this.audioNode = node)}
src={path}
style={{ position: 'absolute', top: '-100px' }}
onPlay={() => this.setState({ playing: true })}
onPause={() => this.setState({ playing: false })}
/>
</div>
);
}

View file

@ -4,10 +4,10 @@ import React, { Suspense } from 'react';
import LoadingScreen from 'component/common/loading-screen';
import MarkdownPreview from 'component/common/markdown-preview';
const LazyCodeViewer = React.lazy(() => import(
/* webpackChunkName: "codeViewer" */
'component/viewers/codeViewer'
));
const LazyCodeViewer = React.lazy<*>(() =>
import(/* webpackChunkName: "codeViewer" */
'component/viewers/codeViewer')
);
type Props = {
theme: string,
@ -84,7 +84,7 @@ class DocumentViewer extends React.PureComponent<Props, State> {
<div className="file-render__viewer document-viewer">
{loading && !error && <LoadingScreen status={loadingMessage} spinner />}
{error && <LoadingScreen status={errorMessage} spinner={!error} />}
{isReady && <Suspense fallback={<div></div>}>{this.renderDocument()}</Suspense>}
{isReady && <Suspense fallback={<div />}>{this.renderDocument()}</Suspense>}
</div>
);
}

View file

@ -1,11 +1,10 @@
// @flow
import type { Claim } from 'types/claim';
import React, { Suspense } from 'react';
import { stopContextMenu } from 'util/context-menu';
import analytics from 'analytics';
import(/* webpackChunkName: "videojs" */
/* webpackPreload: true */
'video.js/dist/video-js.css');
'video.js/dist/video-js.css');
type Props = {
source: {
@ -14,7 +13,7 @@ type Props = {
},
contentType: string,
poster?: string,
claim: Claim,
claim: StreamClaim,
};
class AudioVideoViewer extends React.PureComponent<Props> {
@ -50,11 +49,13 @@ class AudioVideoViewer extends React.PureComponent<Props> {
import(/* webpackChunkName: "videojs" */
/* webpackMode: "lazy" */
/* webpackPreload: true */
'video.js').then(videojs => {
'video.js').then(videojs => {
if (videojs.__esModule) {
videojs = videojs.default;
this.player = videojs(this.videoNode, videoJsOptions, () => {});
} else {
throw Error('Unable to import and use videojs');
}
this.player = videojs(this.videoNode, videoJsOptions, () => {});
});
}

View file

@ -2,12 +2,11 @@
import React from 'react';
import Button from 'component/button';
import { FormField, Form } from 'component/common/form';
import type { Claim } from 'types/claim';
type Props = {
uri: string,
title: string,
claim: Claim,
claim: StreamClaim,
isPending: boolean,
sendSupport: (number, string, string) => void,
onCancel: () => void,

View file

@ -2,14 +2,13 @@
import React from 'react';
import FilePrice from 'component/filePrice';
import { Modal } from 'modal/modal';
import type { Metadata } from 'types/claim';
type Props = {
closeModal: () => void,
loadVideo: string => void,
uri: string,
cancelPurchase: () => void,
metadata: Metadata,
metadata: StreamMetadata,
};
class ModalAffirmPurchase extends React.PureComponent<Props> {
@ -42,7 +41,7 @@ class ModalAffirmPurchase extends React.PureComponent<Props> {
>
<section className="card__content">
<p>
{__('This will purchase')} <strong>{`"${title}"`}</strong> {__('for')}{' '}
{__('This will purchase')} <strong>{title ? `"${title}"` : uri}</strong> {__('for')}{' '}
<strong>
<FilePrice uri={uri} showFullPrice inheritStyle showLBC={false} />
</strong>{' '}

View file

@ -1,21 +1,15 @@
import { connect } from 'react-redux';
import { doHideModal } from 'redux/actions/app';
import { doUploadThumbnail, doUpdatePublishForm } from 'redux/actions/publish';
import { selectPublishFormValues } from 'redux/selectors/publish';
import ModalConfirmThumbnailUpload from './view';
const select = state => {
const publishState = selectPublishFormValues(state);
return { nsfw: publishState.nsfw };
};
const perform = dispatch => ({
closeModal: () => dispatch(doHideModal()),
upload: (path, nsfw = false) => dispatch(doUploadThumbnail(path, nsfw)),
upload: path => dispatch(doUploadThumbnail(path)),
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
});
export default connect(
select,
null,
perform
)(ModalConfirmThumbnailUpload);

View file

@ -4,23 +4,22 @@ import { Modal } from 'modal/modal';
import { FormField } from 'component/common/form';
type Props = {
upload: (string, boolean) => void,
upload: string => void,
path: string,
nsfw: boolean,
closeModal: () => void,
updatePublishForm: ({}) => void,
};
class ModalConfirmThumbnailUpload extends React.PureComponent<Props> {
upload() {
const { upload, updatePublishForm, closeModal, path, nsfw } = this.props;
upload(path, nsfw);
const { upload, updatePublishForm, closeModal, path } = this.props;
upload(path);
updatePublishForm({ thumbnailPath: path });
closeModal();
}
render() {
const { closeModal, path, updatePublishForm, nsfw } = this.props;
const { closeModal, path, updatePublishForm } = this.props;
return (
<Modal
@ -36,14 +35,6 @@ class ModalConfirmThumbnailUpload extends React.PureComponent<Props> {
<p>{__('Are you sure you want to upload this thumbnail to spee.ch')}?</p>
<blockquote>{path}</blockquote>
<FormField
type="checkbox"
name="content_is_mature"
label={__('For mature audiences only')}
checked={nsfw}
onChange={event => updatePublishForm({ nsfw: event.target.checked })}
/>
</section>
</Modal>
);

View file

@ -10,7 +10,9 @@ type Props = {
class ModalError extends React.PureComponent<Props> {
componentDidMount() {
Lbryio.call('event', 'desktop_error', { error_message: JSON.stringify(this.props.error) });
if (process.env.NODE_ENV === 'production') {
Lbryio.call('event', 'desktop_error', { error_message: JSON.stringify(this.props.error) });
}
}
render() {
const { closeModal, error } = this.props;

View file

@ -1,16 +1,17 @@
// @flow
import React from 'react';
import { Modal } from 'modal/modal';
import type { Metadata } from 'types/claim';
type Props = {
metadata: Metadata,
uri: string,
metadata: StreamMetadata,
closeModal: () => void,
};
class ModalFileTimeout extends React.PureComponent<Props> {
render() {
const {
uri,
metadata: { title },
closeModal,
} = this.props;
@ -26,7 +27,7 @@ class ModalFileTimeout extends React.PureComponent<Props> {
<p className="error-modal__error-list">
{__('LBRY was unable to download the stream')}:
<div>
<b>{`"${title}"`}</b>
<b>{title ? `"${title}"` : uri}</b>
</div>
</p>
</section>

View file

@ -1,5 +1,4 @@
// @flow
import type { Transaction } from 'types/transaction';
import React from 'react';
import { Modal } from 'modal/modal';
import * as txnTypes from 'constants/transaction_types';

View file

@ -1,5 +1,4 @@
// @flow
import type { UrlLocation } from 'types/location';
import React from 'react';
import BusyIndicator from 'component/common/busy-indicator';
import Button from 'component/button';

View file

@ -1,6 +1,4 @@
// @flow
import type { Claim } from 'types/claim';
import type { UrlLocation } from 'types/location';
import * as icons from 'constants/icons';
import * as MODALS from 'constants/modal_types';
import React, { useEffect } from 'react';
@ -19,8 +17,8 @@ type Props = {
totalPages: number,
fetching: boolean,
params: { page: number },
claim: Claim,
claimsInChannel: Array<Claim>,
claim: ChannelClaim,
claimsInChannel: Array<StreamClaim>,
channelIsMine: boolean,
fetchClaims: (string, number) => void,
history: { push: string => void },
@ -82,10 +80,10 @@ function ChannelPage(props: Props) {
{name}
{fetching && <BusyIndicator />}
</h1>
<span>{`lbry://${permanentUrl}`}</span>
<span>{permanentUrl}</span>
<div className="channel-info__actions__group">
<SubscribeButton uri={`lbry://${permanentUrl}`} channelName={name} />
<SubscribeButton uri={permanentUrl} channelName={name} />
<Button
button="alt"
icon={icons.SHARE}

View file

@ -13,6 +13,9 @@ import {
makeSelectMetadataForUri,
makeSelectChannelForClaimUri,
selectBalance,
makeSelectTitleForUri,
makeSelectThumbnailForUri,
makeSelectClaimIsNsfw,
} from 'lbry-redux';
import {
doFetchViewCount,
@ -41,6 +44,9 @@ const select = (state, props) => ({
channelUri: makeSelectChannelForClaimUri(props.uri, true)(state),
viewCount: makeSelectViewCountForUri(props.uri)(state),
balance: selectBalance(state),
title: makeSelectTitleForUri(props.uri)(state),
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
nsfw: makeSelectClaimIsNsfw(props.uri)(state),
});
const perform = dispatch => ({

View file

@ -1,10 +1,8 @@
// @flow
import type { Claim, Metadata } from 'types/claim';
import type { FileInfo } from 'types/file_info';
import * as MODALS from 'constants/modal_types';
import * as icons from 'constants/icons';
import * as React from 'react';
import { buildURI, normalizeURI } from 'lbry-redux';
import {buildURI, normalizeURI} from 'lbry-redux';
import FileViewer from 'component/fileViewer';
import Thumbnail from 'component/common/thumbnail';
import FilePrice from 'component/filePrice';
@ -22,15 +20,15 @@ import getMediaType from 'util/get-media-type';
import RecommendedContent from 'component/recommendedContent';
type Props = {
claim: Claim,
fileInfo: FileInfo,
metadata: Metadata,
claim: StreamClaim,
fileInfo: FileListItem,
metadata: StreamMetadata,
contentType: string,
uri: string,
rewardedContentClaimIds: Array<string>,
obscureNsfw: boolean,
claimIsMine: boolean,
costInfo: ?{ cost: number },
costInfo: ?{cost: number},
fetchFileInfo: string => void,
fetchCostInfo: string => void,
setViewed: string => void,
@ -39,10 +37,13 @@ type Props = {
channelUri: string,
viewCount: number,
prepareEdit: ({}, string) => void,
openModal: (id: string, { uri: string }) => void,
openModal: (id: string, {uri: string}) => void,
markSubscriptionRead: (string, string) => void,
fetchViewCount: string => void,
balance: number,
title: string,
thumbnail: ?string,
nsfw: boolean,
};
class FilePage extends React.Component<Props> {
@ -71,11 +72,11 @@ class FilePage extends React.Component<Props> {
claim,
} = this.props;
if (isSubscribed) {
if(isSubscribed) {
this.removeFromSubscriptionNotifications();
}
if (claimIsMine) {
if(claimIsMine) {
fetchViewCount(claim.claim_id);
}
@ -90,26 +91,26 @@ class FilePage extends React.Component<Props> {
}
componentWillReceiveProps(nextProps: Props) {
const { fetchFileInfo, uri, setViewed } = this.props;
const {fetchFileInfo, uri, setViewed} = this.props;
// @if TARGET='app'
if (nextProps.fileInfo === undefined) {
if(nextProps.fileInfo === undefined) {
fetchFileInfo(uri);
}
// @endif
if (uri !== nextProps.uri) {
if(uri !== nextProps.uri) {
setViewed(nextProps.uri);
}
}
componentDidUpdate(prevProps: Props) {
const { isSubscribed, claim, uri, fetchViewCount, claimIsMine } = this.props;
const {isSubscribed, claim, uri, fetchViewCount, claimIsMine} = this.props;
if (!prevProps.isSubscribed && isSubscribed) {
if(!prevProps.isSubscribed && isSubscribed) {
this.removeFromSubscriptionNotifications();
}
if (prevProps.uri !== uri && claimIsMine) {
if(prevProps.uri !== uri && claimIsMine) {
fetchViewCount(claim.claim_id);
}
}
@ -117,7 +118,7 @@ class FilePage extends React.Component<Props> {
removeFromSubscriptionNotifications() {
// Always try to remove
// If it doesn't exist, nothing will happen
const { markSubscriptionRead, uri, channelUri } = this.props;
const {markSubscriptionRead, uri, channelUri} = this.props;
markSubscriptionRead(channelUri, uri);
}
@ -137,21 +138,18 @@ class FilePage extends React.Component<Props> {
channelUri,
viewCount,
balance,
title,
thumbnail,
nsfw,
} = this.props;
// File info
const { title, thumbnail } = metadata;
const { height, channel_name: channelName } = claim;
const { PLAYABLE_MEDIA_TYPES, PREVIEW_MEDIA_TYPES } = FilePage;
const {height, channel_name: channelName} = claim;
const {PLAYABLE_MEDIA_TYPES, PREVIEW_MEDIA_TYPES} = FilePage;
const isRewardContent = (rewardedContentClaimIds || []).includes(claim.claim_id);
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw;
const shouldObscureThumbnail = obscureNsfw && nsfw;
const fileName = fileInfo ? fileInfo.file_name : null;
const mediaType = getMediaType(contentType, fileName);
console.log({
mediaType,
contentType,
fileName,
});
const showFile =
PLAYABLE_MEDIA_TYPES.includes(mediaType) || PREVIEW_MEDIA_TYPES.includes(mediaType);
@ -164,12 +162,12 @@ class FilePage extends React.Component<Props> {
// This is what the user is used to seeing, they don't care about the claim id
// We will select the claim id before they publish
let editUri;
if (claimIsMine) {
const uriObject: { contentName: string, claimId: string, channelName?: string } = {
if(claimIsMine) {
const uriObject: {contentName: string, claimId: string, channelName?: string} = {
contentName: claim.name,
claimId: claim.claim_id,
};
if (channelName) {
if(channelName) {
uriObject.channelName = channelName;
}
@ -204,16 +202,16 @@ class FilePage extends React.Component<Props> {
(thumbnail ? (
<Thumbnail shouldObscure={shouldObscureThumbnail} src={thumbnail} />
) : (
<div
className={classnames('content__empty', {
'content__empty--nsfw': shouldObscureThumbnail,
})}
>
<div className="card__media-text">
{__("Sorry, looks like we can't preview this file.")}
<div
className={classnames('content__empty', {
'content__empty--nsfw': shouldObscureThumbnail,
})}
>
<div className="card__media-text">
{__("Sorry, looks like we can't preview this file.")}
</div>
</div>
</div>
))}
))}
</div>
<div className="grid-area--info media__content media__content--large">
@ -225,13 +223,13 @@ class FilePage extends React.Component<Props> {
size={20}
iconColor="red"
icon={icons.FEATURED}
// Figure out how to get the tooltip to overlap the navbar on the file page and I will love you
// https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue
// https://spee.ch/4/overflow-issue
// tooltip="bottom"
// Figure out how to get the tooltip to overlap the navbar on the file page and I will love you
// https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue
// https://spee.ch/4/overflow-issue
// tooltip="bottom"
/>
)}
{metadata.nsfw && <div className="badge badge--nsfw">NSFW</div>}
{nsfw && <div className="badge badge--nsfw">MATURE</div>}
<FilePrice badge uri={normalizeURI(uri)} />
</div>
@ -271,7 +269,7 @@ class FilePage extends React.Component<Props> {
button="alt"
icon={icons.TIP}
label={__('Send a tip')}
onClick={() => openModal(MODALS.SEND_TIP, { uri })}
onClick={() => openModal(MODALS.SEND_TIP, {uri})}
/>
</React.Fragment>
)}
@ -279,7 +277,7 @@ class FilePage extends React.Component<Props> {
button="alt"
icon={icons.SHARE}
label={__('Share')}
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable })}
onClick={() => openModal(MODALS.SOCIAL_SHARE, {uri, speechShareable})}
/>
</div>

View file

@ -1,5 +1,4 @@
// @flow
import type { Claim } from 'types/claim';
import React from 'react';
import Button from 'component/button';
import FileList from 'component/fileList';
@ -7,7 +6,7 @@ import Page from 'component/page';
import { PAGES } from 'lbry-redux';
type Props = {
claims: Array<Claim>,
claims: Array<StreamClaim>,
checkPendingPublishes: () => void,
fetching: boolean,
sortBy: string,

View file

@ -6,7 +6,6 @@ import RewardTile from 'component/rewardTile';
import Button from 'component/button';
import Page from 'component/page';
import classnames from 'classnames';
import type { Reward } from 'types/reward';
import { rewards as REWARD_TYPES } from 'lbryinc';
import UnsupportedOnWeb from 'component/common/unsupported-on-web';

View file

@ -1,5 +1,4 @@
// @flow
import type { UrlLocation } from 'types/location';
import * as ICONS from 'constants/icons';
import React, { useEffect, Fragment } from 'react';
import { isURIValid, normalizeURI, parseURI } from 'lbry-redux';

View file

@ -9,7 +9,7 @@ import Page from 'component/page';
import FileSelector from 'component/common/file-selector';
import UnsupportedOnWeb from 'component/common/unsupported-on-web';
export type Price = {
type Price = {
currency: string,
amount: number,
};

View file

@ -1,18 +1,16 @@
// @flow
import type { UrlLocation } from 'types/location';
import React from 'react';
import BusyIndicator from 'component/common/busy-indicator';
import ChannelPage from 'page/channel';
import FilePage from 'page/file';
import Page from 'component/page';
import Button from 'component/button';
import type { Claim } from 'types/claim';
type Props = {
isResolvingUri: boolean,
resolveUri: string => void,
uri: string,
claim: Claim,
claim: StreamClaim,
totalPages: number,
location: UrlLocation,
blackListedOutpoints: Array<{

View file

@ -1,6 +1,4 @@
// @flow
import type { ViewMode } from 'types/subscription';
import type { Claim } from 'types/claim';
import { VIEW_ALL, VIEW_LATEST_FIRST } from 'constants/subscriptions';
import React, { Fragment } from 'react';
import Button from 'component/button';
@ -19,7 +17,7 @@ type Props = {
viewMode: ViewMode,
doSetViewMode: ViewMode => void,
hasSubscriptions: boolean,
subscriptions: Array<{ uri: string, ...Claim }>,
subscriptions: Array<{ uri: string, ...StreamClaim }>,
autoDownload: boolean,
onChangeAutoDownload: (SyntheticInputEvent<*>) => void,
unreadSubscriptions: Array<{ channel: string, uris: Array<string> }>,

View file

@ -1,6 +1,4 @@
// @flow
import type { ViewMode } from 'types/subscription';
import type { Claim } from 'types/claim';
import * as SETTINGS from 'constants/settings';
import React, { PureComponent } from 'react';
import Page from 'component/page';
@ -13,7 +11,7 @@ type Props = {
channel: string,
uris: Array<string>,
}>,
allSubscriptions: Array<{ uri: string, ...Claim }>,
allSubscriptions: Array<{ uri: string, ...StreamClaim }>,
loading: boolean,
autoDownload: boolean,
viewMode: ViewMode,

View file

@ -1,5 +1,4 @@
// @flow
import type { Dispatch, GetState } from 'types/redux';
import * as NOTIFICATION_TYPES from 'constants/subscriptions';
import { PAGE_SIZE } from 'constants/claim';
import * as MODALS from 'constants/modal_types';
@ -25,9 +24,7 @@ import {
creditsToString,
doError,
} from 'lbry-redux';
import {
makeSelectCostInfoForUri,
} from 'lbryinc';
import { makeSelectCostInfoForUri } from 'lbryinc';
import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/selectors/settings';
import analytics from 'analytics';
import { formatLbryUriForWeb } from 'util/uri';
@ -305,9 +302,12 @@ export function doFetchClaimsByChannel(
data: { uri, page },
});
Lbry.claim_list_by_channel({ uri, page, page_size: pageSize }).then(result => {
const claimResult = result[uri] || {};
const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult;
// TODO: can we keep uri?
// claim_search should accept a uri (this allows for fetching vanity channel content)
const { claimId } = parseURI(uri);
Lbry.claim_search({ channel_id: claimId, page, page_size: pageSize }).then(result => {
const { items: claimsInChannel, page: returnedPage } = result;
if (claimsInChannel && claimsInChannel.length) {
if (page === 1) {
@ -319,7 +319,7 @@ export function doFetchClaimsByChannel(
uri: buildURI(
{
contentName: latest.channel_name,
claimId: latest.value.publisherSignature.certificateId,
claimId: latest.claim_id,
},
false
),
@ -351,51 +351,6 @@ export function doPlayUri(uri: string) {
};
}
export function doFetchChannelListMine() {
return (dispatch: Dispatch) => {
dispatch({
type: ACTIONS.FETCH_CHANNEL_LIST_STARTED,
});
const callback = channels => {
dispatch({
type: ACTIONS.FETCH_CHANNEL_LIST_COMPLETED,
data: { claims: channels },
});
};
Lbry.channel_list().then(callback);
};
}
export function doCreateChannel(name: string, amount: number) {
return (dispatch: Dispatch) => {
dispatch({
type: ACTIONS.CREATE_CHANNEL_STARTED,
});
return new Promise<void>((resolve, reject) => {
Lbry.channel_new({
channel_name: name,
amount: creditsToString(amount),
}).then(
newChannelClaim => {
const channelClaim = newChannelClaim;
channelClaim.name = name;
dispatch({
type: ACTIONS.CREATE_CHANNEL_COMPLETED,
data: { channelClaim },
});
resolve(channelClaim);
},
error => {
reject(error);
}
);
});
};
}
export function savePosition(claimId: string, outpoint: string, position: number) {
return (dispatch: Dispatch) => {
dispatch({

View file

@ -1,11 +1,4 @@
// @flow
import type { Dispatch, GetState } from 'types/redux';
import type { Metadata } from 'types/claim';
import type {
UpdatePublishFormData,
UpdatePublishFormAction,
PublishParams,
} from 'redux/reducers/publish';
import { CC_LICENSES, COPYRIGHT, OTHER, NONE, PUBLIC_DOMAIN } from 'constants/licenses';
import * as MODALS from 'constants/modal_types';
import {
@ -29,9 +22,7 @@ import fs from 'fs';
import path from 'path';
// @endif
type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH };
export const doResetThumbnailStatus = () => (dispatch: Dispatch): Promise<Action> => {
export const doResetThumbnailStatus = () => (dispatch: Dispatch) => {
dispatch({
type: ACTIONS.UPDATE_PUBLISH_FORM,
data: {
@ -67,22 +58,20 @@ export const doResetThumbnailStatus = () => (dispatch: Dispatch): Promise<Action
);
};
export const doClearPublish = () => (dispatch: Dispatch): Promise<Action> => {
export const doClearPublish = () => (dispatch: Dispatch) => {
dispatch({ type: ACTIONS.CLEAR_PUBLISH });
return dispatch(doResetThumbnailStatus());
};
export const doUpdatePublishForm = (publishFormValue: UpdatePublishFormData) => (
dispatch: Dispatch
): UpdatePublishFormAction =>
dispatch(
({
type: ACTIONS.UPDATE_PUBLISH_FORM,
data: { ...publishFormValue },
}: UpdatePublishFormAction)
);
) =>
dispatch({
type: ACTIONS.UPDATE_PUBLISH_FORM,
data: { ...publishFormValue },
});
export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch: Dispatch) => {
export const doUploadThumbnail = (filePath: string) => (dispatch: Dispatch) => {
const thumbnail = fs.readFileSync(filePath);
const fileExt = path.extname(filePath);
const fileName = path.basename(filePath);
@ -119,7 +108,7 @@ export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch:
const file = new File([thumbnail], fileName, { type: `image/${fileExt.slice(1)}` });
data.append('name', name);
data.append('file', file);
data.append('nsfw', nsfw.toString());
return fetch('https://spee.ch/api/claim/publish', {
method: 'POST',
body: data,
@ -139,15 +128,8 @@ export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch:
.catch(err => uploadError(err.message));
};
export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) => {
const {
name,
amount,
channel_name: channelName,
value: {
stream: { metadata },
},
} = claim;
export const doPrepareEdit = (claim: StreamClaim, uri: string) => (dispatch: Dispatch) => {
const { name, amount, channel_name: channelName, value } = claim;
const {
author,
@ -158,26 +140,23 @@ export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) =
amount: 0,
currency: 'LBC',
},
language,
languages,
license,
licenseUrl,
nsfw,
license_url: licenseUrl,
thumbnail,
title,
} = metadata;
} = value;
const publishData: UpdatePublishFormData = {
name,
channel: channelName,
bid: amount,
price: { amount: fee.amount, currency: fee.currency },
contentIsFree: !fee.amount,
author,
description,
fee,
language,
nsfw,
thumbnail,
languages,
thumbnail: thumbnail ? thumbnail.url : null,
title,
uri,
uploadThumbnailStatus: thumbnail ? THUMBNAIL_STATUSES.MANUAL : undefined,
@ -204,6 +183,8 @@ export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) =
};
export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getState: () => {}) => {
dispatch({ type: ACTIONS.PUBLISH_START });
const state = getState();
const myChannels = selectMyChannelClaims(state);
const myClaims = selectMyClaimsWithoutChannels(state);
@ -217,55 +198,63 @@ export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getStat
license,
licenseUrl,
thumbnail,
nsfw,
channel,
title,
contentIsFree,
price,
fee,
uri,
nsfw,
} = params;
// get the claim id from the channel name, we will use that instead
const namedChannelClaim = myChannels.find(myChannel => myChannel.name === channel);
const channelId = namedChannelClaim ? namedChannelClaim.claim_id : '';
const fee = contentIsFree || !price.amount ? undefined : { ...price };
const metadata: Metadata = {
title,
nsfw,
license,
licenseUrl,
language,
thumbnail,
description: description || undefined,
};
const publishPayload: {
name: ?string,
channel_id: string,
bid: ?number,
metadata: ?Metadata,
channel_id?: string,
bid: number,
file_path?: string,
fee?: { amount: string, currency: string },
tags: Array<string>,
} = {
name,
channel_id: channelId,
bid: creditsToString(bid),
metadata,
title,
license,
license_url: licenseUrl,
languages: [language],
description,
thumbnail_url: thumbnail,
tags: [],
};
// Temporary solution to keep the same publish flow with the new tags api
// Eventually we will allow users to enter their own tags on publish
// `nsfw` will probably be removed
if (nsfw) {
publishPayload.tags.push('mature');
}
if (channelId) {
publishPayload.channel_id = channelId;
}
if (fee) {
metadata.fee = {
publishPayload.fee = {
currency: fee.currency,
amount: creditsToString(fee.amount),
};
}
// only pass file on new uploads, not metadata only edits.
// Only pass file on new uploads, not metadata only edits.
// The sdk will figure it out
if (filePath) publishPayload.file_path = filePath;
dispatch({ type: ACTIONS.PUBLISH_START });
const success = pendingClaim => {
const success = successResponse => {
analytics.apiLogPublish();
const pendingClaim = successResponse.outputs[0];
const actions = [];
actions.push({
@ -280,8 +269,8 @@ export const doPublish = (params: PublishParams) => (dispatch: Dispatch, getStat
const isMatch = claim => claim.claim_id === pendingClaim.claim_id;
const isEdit = myClaims.some(isMatch);
const myNewClaims = isEdit
? myClaims.map(claim => (isMatch(claim) ? pendingClaim.output : claim))
: myClaims.concat(pendingClaim.output);
? myClaims.map(claim => (isMatch(claim) ? pendingClaim : claim))
: myClaims.concat(pendingClaim);
actions.push({
type: ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED,
@ -313,7 +302,7 @@ export const doCheckPendingPublishes = () => (dispatch: Dispatch, getState: GetS
let publishCheckInterval;
const checkFileList = () => {
Lbry.claim_list_mine().then(claims => {
Lbry.claim_list().then(claims => {
claims.forEach(claim => {
// If it's confirmed, check if it was pending previously
if (claim.confirmations > 0 && pendingById[claim.claim_id]) {
@ -322,7 +311,7 @@ export const doCheckPendingPublishes = () => (dispatch: Dispatch, getState: GetS
// If it's confirmed, check if we should notify the user
if (selectosNotificationsEnabled(getState())) {
const notif = new window.Notification('LBRY Publish Complete', {
body: `${claim.value.stream.metadata.title} has been published to lbry://${
body: `${claim.value.stream.title} has been published to lbry://${
claim.name
}. Click here to view it`,
silent: false,

View file

@ -1,12 +1,4 @@
// @flow
import type { Dispatch, GetState } from 'types/redux';
import type {
SubscriptionState,
Subscription,
SubscriptionNotificationType,
ViewMode,
UnreadSubscription,
} from 'types/subscription';
import { PAGE_SIZE } from 'constants/claim';
import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings';
@ -239,8 +231,10 @@ export const doCheckSubscription = (subscriptionUri: string, shouldNotify?: bool
);
}
const { claimId } = parseURI(subscriptionUri);
// We may be duplicating calls here. Can this logic be baked into doFetchClaimsByChannel?
Lbry.claim_list_by_channel({ uri: subscriptionUri, page: 1, page_size: PAGE_SIZE }).then(
Lbry.claim_search({ channel_id: claimId, page: 1, page_size: PAGE_SIZE }).then(
claimListByChannel => {
const claimResult = claimListByChannel[subscriptionUri] || {};
const { claims_in_channel: claimsInChannel } = claimResult;
@ -268,9 +262,7 @@ export const doCheckSubscription = (subscriptionUri: string, shouldNotify?: bool
const uri = buildURI({ contentName: claim.name, claimId: claim.claim_id }, true);
const shouldDownload =
shouldAutoDownload &&
Boolean(
downloadCount < SUBSCRIPTION_DOWNLOAD_LIMIT && !claim.value.stream.metadata.fee
);
Boolean(downloadCount < SUBSCRIPTION_DOWNLOAD_LIMIT && !claim.value.stream.fee);
// Add the new content to the list of "un-read" subscriptions
if (shouldNotify) {

View file

@ -4,7 +4,6 @@ import { buildURI } from 'lbry-redux';
import * as ACTIONS from 'constants/action_types';
import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses';
import { CHANNEL_ANONYMOUS } from 'constants/claim';
import type { Source } from 'types/claim';
type PublishState = {
editingURI: ?string,
@ -15,7 +14,7 @@ type PublishState = {
currency: string,
},
title: string,
thumbnail: string,
thumbnail_url: string,
thumbnailPath: string,
uploadThumbnailStatus: string,
description: string,
@ -30,60 +29,6 @@ type PublishState = {
licenseUrl: string,
};
export type UpdatePublishFormData = {
filePath?: string,
contentIsFree?: boolean,
price?: {
amount: number,
currency: string,
},
title?: string,
thumbnail?: string,
uploadThumbnailStatus?: string,
thumbnailPath?: string,
description?: string,
language?: string,
channel?: string,
channelId?: string,
name?: string,
nameError?: string,
bid?: number,
bidError?: string,
otherLicenseDescription?: string,
licenseUrl?: string,
licenseType?: string,
uri?: string,
};
export type UpdatePublishFormAction = {
type: ACTIONS.UPDATE_PUBLISH_FORM | ACTIONS.DO_PREPARE_EDIT,
data: UpdatePublishFormData,
};
export type PublishParams = {
name?: string,
bid?: number,
filePath?: string,
description: ?string,
language: string,
publishingLicense?: string,
publishingLicenseUrl?: string,
thumbnail: ?string,
nsfw: boolean,
channel: string,
channelId?: string,
title: string,
contentIsFree: boolean,
uri?: string,
license: ?string,
licenseUrl: ?string,
price: {
currency: string,
amount: number,
},
sources?: Source,
};
const defaultState: PublishState = {
editingURI: undefined,
filePath: undefined,
@ -93,7 +38,7 @@ const defaultState: PublishState = {
currency: 'LBC',
},
title: '',
thumbnail: '',
thumbnail_url: '',
thumbnailPath: '',
uploadThumbnailStatus: THUMBNAIL_STATUSES.API_DOWN,
description: '',

View file

@ -2,18 +2,6 @@
import * as ACTIONS from 'constants/action_types';
import { VIEW_ALL } from 'constants/subscriptions';
import { handleActions } from 'util/redux-utils';
import type {
SubscriptionState,
Subscription,
DoChannelSubscribe,
DoChannelUnsubscribe,
SetSubscriptionLatest,
DoUpdateSubscriptionUnreads,
DoRemoveSubscriptionUnreads,
FetchedSubscriptionsSucess,
SetViewMode,
GetSuggestedSubscriptionsSuccess,
} from 'types/subscription';
const defaultState: SubscriptionState = {
subscriptions: [],

View file

@ -1,64 +0,0 @@
// @flow
// Currently incomplete
export type Source = {
contentType: string,
source: string,
sourceType: string,
version: string,
};
export type Metadata = {
nsfw: boolean,
title: string,
thumbnail: ?string,
description: ?string,
license: ?string,
language: string,
fee?:
| {
amount: number, // should be a string https://github.com/lbryio/lbry/issues/1576
currency: string,
address: string,
version: string,
}
| {
// We don't include a version or address in the metadata field when publishing
amount: number,
currency: string,
},
};
// Actual claim type has more values than this
// Add them as they are used
export type Claim = {
address: string,
amount: number,
claim_id: string,
claim_sequence: number,
decoded_claim: boolean,
depth: number,
effective_amount: number,
has_signature: boolean,
height: number,
has_signature: boolean,
hex: string,
name: string,
nout: number,
permanent_url: string,
channel_name: ?string,
txid: string,
nout: number,
signature_is_valid: boolean,
valid_at_height: number,
value: ?{
publisherSignature: ?{
certificateId: string,
},
stream: {
metadata: Metadata,
source: Source,
},
},
};

View file

@ -1,5 +0,0 @@
// @flow
export type FormikActions = {
setSubmitting: boolean => mixed,
};

View file

@ -1,21 +0,0 @@
// @flow
export type FileInfo = {
absolute_channel_position: ?number,
name: string,
channelName: ?string,
pending?: boolean,
channel_claim_id: string,
file_name: string,
download_path: string,
value?: {
publisherSignature: {
certificateId: string,
},
},
metadata: {
publisherSignature: {
certificateId: string,
},
},
};

View file

@ -1,6 +0,0 @@
// @flow
/* eslint-disable no-use-before-define */
export type GetState = () => any;
export type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
export type Dispatch = (action: {} | Promise<*> | Array<{}> | ThunkAction) => any; // Need to refer to ThunkAction
/* eslint-enable */

View file

@ -1,48 +0,0 @@
// @flow
export type Status = {
error?: {},
is_running: boolean,
is_first_run: boolean,
installation_id: string,
skipped_components: Array<string>,
startup_status: {
blob_manager: boolean,
blockchain_headers: boolean,
database: boolean,
dht: boolean,
exchange_rate_manager: boolean,
file_manager: boolean,
hash_announcer: boolean,
payment_rate_manager: boolean,
peer_protocol_server: boolean,
rate_limiter: boolean,
stream_identifier: boolean,
upnp: boolean,
wallet: boolean,
},
blob_manager: {
finished_blobs: number,
},
connection_status: {
code: string,
message: string,
},
dht: {
node_id: string,
peers_in_routing_table: number,
},
hash_announcer: {
announce_queue_size: number,
},
wallet?: {
best_blockhash: string,
blocks: number,
blocks_behind: number,
is_encrypted: boolean,
is_locked: boolean,
},
blockchain_headers?: {
download_progress: number,
},
};

View file

@ -1,11 +0,0 @@
// @flow
export type Transaction = {
amount: number,
claim_id: string,
claim_name: string,
fee: number,
nout: number,
txid: string,
type: string,
date: Date,
};

View file

@ -1,12 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>lbry.tv</title>
<meta charset="utf-8" />
<!-- @if TARGET='app' -->
<title>LBRY</title>
<!-- @endif -->
<!-- @if TARGET='web' -->
<title>lbry.tv</title>
<meta property="og:url" content="https://beta.lbry.tv" />
<meta property="og:title" content="LBRY On The Web" />
<meta property="og:description" content="All your favorite LBRY content in your browser." />
<meta property="og:image" content="/og.png" />
<!-- @endif -->
</head>
<body>

View file

@ -56,12 +56,7 @@ let baseConfig = {
{
test: /\.s?css$/,
exclude: /\.module.scss$/,
use: [
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader',
],
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
},
{
test: /\.(png|svg|gif)$/,

View file

@ -3973,20 +3973,6 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
eslint-config-airbnb-base@^12.1.0:
version "12.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944"
integrity sha512-/vjm0Px5ZCpmJqnjIzcFb9TKZrKWz0gnuG/7Gfkt0Db1ELJR51xkZth+t14rYdqWgX836XbuxtArbIHlVhbLBA==
dependencies:
eslint-restricted-globals "^0.1.1"
eslint-config-airbnb@^16.1.0:
version "16.1.0"
resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz#2546bfb02cc9fe92284bf1723ccf2e87bc45ca46"
integrity sha512-zLyOhVWhzB/jwbz7IPSbkUuj7X2ox4PHXTcZkEmDqTvd0baJmJyuxlFPDlZOE/Y5bC+HQRaEkT3FoHo9wIdRiw==
dependencies:
eslint-config-airbnb-base "^12.1.0"
eslint-config-prettier@^2.9.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.10.0.tgz#ec07bc1d01f87d09f61d3840d112dc8a9791e30b"
@ -4124,11 +4110,6 @@ eslint-plugin-standard@^4.0.0:
resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c"
integrity sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA==
eslint-restricted-globals@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7"
integrity sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=
eslint-scope@3.7.1:
version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8"
@ -4702,10 +4683,10 @@ flatten@^1.0.2:
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=
flow-bin@^0.94.0:
version "0.94.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.94.0.tgz#b5d58fe7559705b73a18229f97edfc3ab6ffffcb"
integrity sha512-DYF7r9CJ/AksfmmB4+q+TyLMoeQPRnqtF1Pk7KY3zgfkB/nVuA3nXyzqgsIPIvnMSiFEXQcFK4z+iPxSLckZhQ==
flow-bin@^0.97.0:
version "0.97.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.97.0.tgz#036ffcfc27503367a9d906ec9d843a0aa6f6bb83"
integrity sha512-jXjD05gkatLuC4+e28frH1hZoRwr1iASP6oJr61Q64+kR4kmzaS+AdFBhYgoYS5kpoe4UzwDebWK8ETQFNh00w==
flow-typed@^2.3.0:
version "2.5.1"
@ -6475,17 +6456,17 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
yargs "^13.2.2"
zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#d4c7dea65f7179974e9b96c863022fe7b049ff7d:
lbry-redux@lbryio/lbry-redux#cc42856676541120b088e4228c04246ba8ff3274:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/d4c7dea65f7179974e9b96c863022fe7b049ff7d"
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/cc42856676541120b088e4228c04246ba8ff3274"
dependencies:
proxy-polyfill "0.1.6"
reselect "^3.0.0"
uuid "^3.3.2"
lbryinc@lbryio/lbryinc#4f2d4a50986bffab0b05d9f6cd7c2f0a856a0e02:
lbryinc@lbryio/lbryinc#9665f2d1c818f1a86b2e5daab642f6879746f25f:
version "0.0.1"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/4f2d4a50986bffab0b05d9f6cd7c2f0a856a0e02"
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/9665f2d1c818f1a86b2e5daab642f6879746f25f"
dependencies:
reselect "^3.0.0"