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] [ignore]
node_modules/
[include] [include]
[libs] [libs]
./flow-typed ./flow-typed
node_modules/lbry-redux/flow-typed/
[lints] [lints]

View file

@ -1,6 +1,6 @@
// @flow // @flow
export type UrlLocation = { declare type UrlLocation = {
search: string, search: string,
hash: string, hash: string,
host: 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 // @flow
export type Reward = { declare type Reward = {
created_at: string, created_at: string,
id: number, id: number,
reward_amount: number, reward_amount: number,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,13 +3,12 @@ import * as React from 'react';
import { buildURI, SORT_OPTIONS } from 'lbry-redux'; import { buildURI, SORT_OPTIONS } from 'lbry-redux';
import { FormField, Form } from 'component/common/form'; import { FormField, Form } from 'component/common/form';
import FileCard from 'component/fileCard'; import FileCard from 'component/fileCard';
import type { FileInfo } from 'types/file_info';
type Props = { type Props = {
hideFilter: boolean, hideFilter: boolean,
sortByHeight?: boolean, sortByHeight?: boolean,
claimsById: Array<{}>, claimsById: Array<StreamClaim>,
fileInfos: Array<FileInfo>, fileInfos: Array<FileListItem>,
sortBy: string, sortBy: string,
page?: string, page?: string,
setFileListSort: (?string, string) => void, setFileListSort: (?string, string) => void,
@ -80,7 +79,7 @@ class FileList extends React.PureComponent<Props> {
return metadata.title || claimName; return metadata.title || claimName;
} else if (value) { } else if (value) {
// published claim // published claim
const { title } = value.stream.metadata; const { title } = value.stream;
return title || name; return title || name;
} }
// Invalid claim // Invalid claim
@ -109,14 +108,11 @@ class FileList extends React.PureComponent<Props> {
}; };
} }
getChannelSignature = (fileInfo: FileInfo) => { getChannelSignature = (fileInfo: { pending: boolean } & FileListItem) => {
if (fileInfo.pending) { if (fileInfo.pending) {
return undefined; return undefined;
} }
if (fileInfo.value) {
return fileInfo.value.publisherSignature.certificateId;
}
return fileInfo.channel_claim_id; return fileInfo.channel_claim_id;
}; };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,4 @@
// @flow // @flow
import type { Status } from 'types/status';
import * as React from 'react'; import * as React from 'react';
import * as MODALS from 'constants/modal_types'; import * as MODALS from 'constants/modal_types';
import { Lbry } from 'lbry-redux'; 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 { notifyUnlockWallet, authenticate, modal } = this.props;
const { launchedModal } = this.state; const { launchedModal } = this.state;
if (status.error) { if (status.connection_status.code !== 'connected') {
this.setState({ error: true }); this.setState({ error: true });
return; return;
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,4 @@
// @flow // @flow
import type { Claim } from 'types/claim';
import React, { Suspense } from 'react'; import React, { Suspense } from 'react';
import { stopContextMenu } from 'util/context-menu'; import { stopContextMenu } from 'util/context-menu';
import analytics from 'analytics'; import analytics from 'analytics';
@ -14,7 +13,7 @@ type Props = {
}, },
contentType: string, contentType: string,
poster?: string, poster?: string,
claim: Claim, claim: StreamClaim,
}; };
class AudioVideoViewer extends React.PureComponent<Props> { class AudioVideoViewer extends React.PureComponent<Props> {
@ -53,8 +52,10 @@ class AudioVideoViewer extends React.PureComponent<Props> {
'video.js').then(videojs => { 'video.js').then(videojs => {
if (videojs.__esModule) { if (videojs.__esModule) {
videojs = videojs.default; videojs = videojs.default;
}
this.player = videojs(this.videoNode, videoJsOptions, () => {}); this.player = videojs(this.videoNode, videoJsOptions, () => {});
} else {
throw Error('Unable to import and use videojs');
}
}); });
} }

View file

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

View file

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

View file

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

View file

@ -4,23 +4,22 @@ import { Modal } from 'modal/modal';
import { FormField } from 'component/common/form'; import { FormField } from 'component/common/form';
type Props = { type Props = {
upload: (string, boolean) => void, upload: string => void,
path: string, path: string,
nsfw: boolean,
closeModal: () => void, closeModal: () => void,
updatePublishForm: ({}) => void, updatePublishForm: ({}) => void,
}; };
class ModalConfirmThumbnailUpload extends React.PureComponent<Props> { class ModalConfirmThumbnailUpload extends React.PureComponent<Props> {
upload() { upload() {
const { upload, updatePublishForm, closeModal, path, nsfw } = this.props; const { upload, updatePublishForm, closeModal, path } = this.props;
upload(path, nsfw); upload(path);
updatePublishForm({ thumbnailPath: path }); updatePublishForm({ thumbnailPath: path });
closeModal(); closeModal();
} }
render() { render() {
const { closeModal, path, updatePublishForm, nsfw } = this.props; const { closeModal, path, updatePublishForm } = this.props;
return ( return (
<Modal <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> <p>{__('Are you sure you want to upload this thumbnail to spee.ch')}?</p>
<blockquote>{path}</blockquote> <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> </section>
</Modal> </Modal>
); );

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,4 @@
// @flow // @flow
import type { Claim, Metadata } from 'types/claim';
import type { FileInfo } from 'types/file_info';
import * as MODALS from 'constants/modal_types'; import * as MODALS from 'constants/modal_types';
import * as icons from 'constants/icons'; import * as icons from 'constants/icons';
import * as React from 'react'; import * as React from 'react';
@ -22,9 +20,9 @@ import getMediaType from 'util/get-media-type';
import RecommendedContent from 'component/recommendedContent'; import RecommendedContent from 'component/recommendedContent';
type Props = { type Props = {
claim: Claim, claim: StreamClaim,
fileInfo: FileInfo, fileInfo: FileListItem,
metadata: Metadata, metadata: StreamMetadata,
contentType: string, contentType: string,
uri: string, uri: string,
rewardedContentClaimIds: Array<string>, rewardedContentClaimIds: Array<string>,
@ -43,6 +41,9 @@ type Props = {
markSubscriptionRead: (string, string) => void, markSubscriptionRead: (string, string) => void,
fetchViewCount: string => void, fetchViewCount: string => void,
balance: number, balance: number,
title: string,
thumbnail: ?string,
nsfw: boolean,
}; };
class FilePage extends React.Component<Props> { class FilePage extends React.Component<Props> {
@ -137,21 +138,18 @@ class FilePage extends React.Component<Props> {
channelUri, channelUri,
viewCount, viewCount,
balance, balance,
title,
thumbnail,
nsfw,
} = this.props; } = this.props;
// File info // File info
const { title, thumbnail } = metadata;
const {height, channel_name: channelName} = claim; const {height, channel_name: channelName} = claim;
const {PLAYABLE_MEDIA_TYPES, PREVIEW_MEDIA_TYPES} = FilePage; const {PLAYABLE_MEDIA_TYPES, PREVIEW_MEDIA_TYPES} = FilePage;
const isRewardContent = (rewardedContentClaimIds || []).includes(claim.claim_id); const isRewardContent = (rewardedContentClaimIds || []).includes(claim.claim_id);
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw; const shouldObscureThumbnail = obscureNsfw && nsfw;
const fileName = fileInfo ? fileInfo.file_name : null; const fileName = fileInfo ? fileInfo.file_name : null;
const mediaType = getMediaType(contentType, fileName); const mediaType = getMediaType(contentType, fileName);
console.log({
mediaType,
contentType,
fileName,
});
const showFile = const showFile =
PLAYABLE_MEDIA_TYPES.includes(mediaType) || PREVIEW_MEDIA_TYPES.includes(mediaType); PLAYABLE_MEDIA_TYPES.includes(mediaType) || PREVIEW_MEDIA_TYPES.includes(mediaType);
@ -231,7 +229,7 @@ class FilePage extends React.Component<Props> {
// tooltip="bottom" // tooltip="bottom"
/> />
)} )}
{metadata.nsfw && <div className="badge badge--nsfw">NSFW</div>} {nsfw && <div className="badge badge--nsfw">MATURE</div>}
<FilePrice badge uri={normalizeURI(uri)} /> <FilePrice badge uri={normalizeURI(uri)} />
</div> </div>

View file

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

View file

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

View file

@ -1,5 +1,4 @@
// @flow // @flow
import type { UrlLocation } from 'types/location';
import * as ICONS from 'constants/icons'; import * as ICONS from 'constants/icons';
import React, { useEffect, Fragment } from 'react'; import React, { useEffect, Fragment } from 'react';
import { isURIValid, normalizeURI, parseURI } from 'lbry-redux'; 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 FileSelector from 'component/common/file-selector';
import UnsupportedOnWeb from 'component/common/unsupported-on-web'; import UnsupportedOnWeb from 'component/common/unsupported-on-web';
export type Price = { type Price = {
currency: string, currency: string,
amount: number, amount: number,
}; };

View file

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

View file

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

View file

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

View file

@ -1,5 +1,4 @@
// @flow // @flow
import type { Dispatch, GetState } from 'types/redux';
import * as NOTIFICATION_TYPES from 'constants/subscriptions'; import * as NOTIFICATION_TYPES from 'constants/subscriptions';
import { PAGE_SIZE } from 'constants/claim'; import { PAGE_SIZE } from 'constants/claim';
import * as MODALS from 'constants/modal_types'; import * as MODALS from 'constants/modal_types';
@ -25,9 +24,7 @@ import {
creditsToString, creditsToString,
doError, doError,
} from 'lbry-redux'; } from 'lbry-redux';
import { import { makeSelectCostInfoForUri } from 'lbryinc';
makeSelectCostInfoForUri,
} from 'lbryinc';
import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/selectors/settings'; import { makeSelectClientSetting, selectosNotificationsEnabled } from 'redux/selectors/settings';
import analytics from 'analytics'; import analytics from 'analytics';
import { formatLbryUriForWeb } from 'util/uri'; import { formatLbryUriForWeb } from 'util/uri';
@ -305,9 +302,12 @@ export function doFetchClaimsByChannel(
data: { uri, page }, data: { uri, page },
}); });
Lbry.claim_list_by_channel({ uri, page, page_size: pageSize }).then(result => { // TODO: can we keep uri?
const claimResult = result[uri] || {}; // claim_search should accept a uri (this allows for fetching vanity channel content)
const { claims_in_channel: claimsInChannel, returned_page: returnedPage } = claimResult; 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 (claimsInChannel && claimsInChannel.length) {
if (page === 1) { if (page === 1) {
@ -319,7 +319,7 @@ export function doFetchClaimsByChannel(
uri: buildURI( uri: buildURI(
{ {
contentName: latest.channel_name, contentName: latest.channel_name,
claimId: latest.value.publisherSignature.certificateId, claimId: latest.claim_id,
}, },
false 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) { export function savePosition(claimId: string, outpoint: string, position: number) {
return (dispatch: Dispatch) => { return (dispatch: Dispatch) => {
dispatch({ dispatch({

View file

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

View file

@ -1,12 +1,4 @@
// @flow // @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 { PAGE_SIZE } from 'constants/claim';
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings'; 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? // 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 => { claimListByChannel => {
const claimResult = claimListByChannel[subscriptionUri] || {}; const claimResult = claimListByChannel[subscriptionUri] || {};
const { claims_in_channel: claimsInChannel } = claimResult; 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 uri = buildURI({ contentName: claim.name, claimId: claim.claim_id }, true);
const shouldDownload = const shouldDownload =
shouldAutoDownload && shouldAutoDownload &&
Boolean( Boolean(downloadCount < SUBSCRIPTION_DOWNLOAD_LIMIT && !claim.value.stream.fee);
downloadCount < SUBSCRIPTION_DOWNLOAD_LIMIT && !claim.value.stream.metadata.fee
);
// Add the new content to the list of "un-read" subscriptions // Add the new content to the list of "un-read" subscriptions
if (shouldNotify) { if (shouldNotify) {

View file

@ -4,7 +4,6 @@ import { buildURI } from 'lbry-redux';
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses'; import * as THUMBNAIL_STATUSES from 'constants/thumbnail_upload_statuses';
import { CHANNEL_ANONYMOUS } from 'constants/claim'; import { CHANNEL_ANONYMOUS } from 'constants/claim';
import type { Source } from 'types/claim';
type PublishState = { type PublishState = {
editingURI: ?string, editingURI: ?string,
@ -15,7 +14,7 @@ type PublishState = {
currency: string, currency: string,
}, },
title: string, title: string,
thumbnail: string, thumbnail_url: string,
thumbnailPath: string, thumbnailPath: string,
uploadThumbnailStatus: string, uploadThumbnailStatus: string,
description: string, description: string,
@ -30,60 +29,6 @@ type PublishState = {
licenseUrl: string, 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 = { const defaultState: PublishState = {
editingURI: undefined, editingURI: undefined,
filePath: undefined, filePath: undefined,
@ -93,7 +38,7 @@ const defaultState: PublishState = {
currency: 'LBC', currency: 'LBC',
}, },
title: '', title: '',
thumbnail: '', thumbnail_url: '',
thumbnailPath: '', thumbnailPath: '',
uploadThumbnailStatus: THUMBNAIL_STATUSES.API_DOWN, uploadThumbnailStatus: THUMBNAIL_STATUSES.API_DOWN,
description: '', description: '',

View file

@ -2,18 +2,6 @@
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import { VIEW_ALL } from 'constants/subscriptions'; import { VIEW_ALL } from 'constants/subscriptions';
import { handleActions } from 'util/redux-utils'; import { handleActions } from 'util/redux-utils';
import type {
SubscriptionState,
Subscription,
DoChannelSubscribe,
DoChannelUnsubscribe,
SetSubscriptionLatest,
DoUpdateSubscriptionUnreads,
DoRemoveSubscriptionUnreads,
FetchedSubscriptionsSucess,
SetViewMode,
GetSuggestedSubscriptionsSuccess,
} from 'types/subscription';
const defaultState: SubscriptionState = { const defaultState: SubscriptionState = {
subscriptions: [], 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> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>lbry.tv</title>
<meta charset="utf-8" /> <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:url" content="https://beta.lbry.tv" />
<meta property="og:title" content="LBRY On The Web" /> <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:description" content="All your favorite LBRY content in your browser." />
<meta property="og:image" content="/og.png" /> <meta property="og:image" content="/og.png" />
<!-- @endif -->
</head> </head>
<body> <body>

View file

@ -56,12 +56,7 @@ let baseConfig = {
{ {
test: /\.s?css$/, test: /\.s?css$/,
exclude: /\.module.scss$/, exclude: /\.module.scss$/,
use: [ use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
'style-loader',
'css-loader',
'postcss-loader',
'sass-loader',
],
}, },
{ {
test: /\.(png|svg|gif)$/, 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" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= 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: eslint-config-prettier@^2.9.0:
version "2.10.0" version "2.10.0"
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.10.0.tgz#ec07bc1d01f87d09f61d3840d112dc8a9791e30b" 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" resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.0.0.tgz#f845b45109c99cd90e77796940a344546c8f6b5c"
integrity sha512-OwxJkR6TQiYMmt1EsNRMe5qG3GsbjlcOhbGUBY4LtavF9DsLaTcoR+j2Tdjqi23oUwKNUqX7qcn5fPStafMdlA== 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: eslint-scope@3.7.1:
version "3.7.1" version "3.7.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" 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" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=
flow-bin@^0.94.0: flow-bin@^0.97.0:
version "0.94.0" version "0.97.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.94.0.tgz#b5d58fe7559705b73a18229f97edfc3ab6ffffcb" resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.97.0.tgz#036ffcfc27503367a9d906ec9d843a0aa6f6bb83"
integrity sha512-DYF7r9CJ/AksfmmB4+q+TyLMoeQPRnqtF1Pk7KY3zgfkB/nVuA3nXyzqgsIPIvnMSiFEXQcFK4z+iPxSLckZhQ== integrity sha512-jXjD05gkatLuC4+e28frH1hZoRwr1iASP6oJr61Q64+kR4kmzaS+AdFBhYgoYS5kpoe4UzwDebWK8ETQFNh00w==
flow-typed@^2.3.0: flow-typed@^2.3.0:
version "2.5.1" version "2.5.1"
@ -6475,17 +6456,17 @@ lazy-val@^1.0.3, lazy-val@^1.0.4:
yargs "^13.2.2" yargs "^13.2.2"
zstd-codec "^0.1.1" zstd-codec "^0.1.1"
lbry-redux@lbryio/lbry-redux#d4c7dea65f7179974e9b96c863022fe7b049ff7d: lbry-redux@lbryio/lbry-redux#cc42856676541120b088e4228c04246ba8ff3274:
version "0.0.1" 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: dependencies:
proxy-polyfill "0.1.6" proxy-polyfill "0.1.6"
reselect "^3.0.0" reselect "^3.0.0"
uuid "^3.3.2" uuid "^3.3.2"
lbryinc@lbryio/lbryinc#4f2d4a50986bffab0b05d9f6cd7c2f0a856a0e02: lbryinc@lbryio/lbryinc#9665f2d1c818f1a86b2e5daab642f6879746f25f:
version "0.0.1" 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: dependencies:
reselect "^3.0.0" reselect "^3.0.0"