0.36.0 SDK changes #2407

Merged
neb-b merged 2 commits from sdk-changes into master 2019-04-26 23:33:13 +02:00
71 changed files with 526 additions and 845 deletions

View file

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

View file

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

View file

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

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

@ -0,0 +1,52 @@
// @flow
declare type UpdatePublishFormData = {
kauffj commented 2019-04-26 23:21:25 +02:00 (Migrated from github.com)
Review

Naming is a little inconsistent between these two types.

Also, you may be able to use Intersection Types to avoid repeating the shared values across both (but I've never used this feature).

Naming is a little inconsistent between these two types. Also, you may be able to use Intersection Types to avoid repeating the shared values across both (but I've never used this feature).
neb-b commented 2019-04-26 23:25:50 +02:00 (Migrated from github.com)
Review

Yeah... the publish types were one of the first added. Now that I have a better idea of flow and have developed some patterns in other parts of the app this can be cleaned up a lot. Planning to clean it up when I add the tags to the publish page

Yeah... the publish types were one of the first added. Now that I have a better idea of flow and have developed some patterns in other parts of the app this can be cleaned up a lot. Planning to clean it up when I add the tags to the publish page
filePath?: string,
contentIsFree?: boolean,
price?: {
amount: number,
currency: string,
},
title?: string,
thumbnail_url?: string,
uploadThumbnailStatus?: string,
thumbnailPath?: string,
description?: string,
language?: string,
channel?: string,
channelId?: string,
name?: string,
nameError?: string,
bid?: number,
bidError?: string,
otherLicenseDescription?: string,
licenseUrl?: string,
licenseType?: string,
uri?: string,
};
declare type PublishParams = {
name: ?string,
bid: ?number,
filePath?: string,
description: ?string,
language: string,
publishingLicense?: string,
publishingLicenseUrl?: string,
thumbnail: ?string,
channel: string,
channelId?: string,
title: string,
contentIsFree: boolean,
uri?: string,
license: ?string,
licenseUrl: ?string,
fee?: {
currency: string,
amount: number,
},
// This is bad.
// Will be removed for tags soon
nsfw: boolean,
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,3 @@
// @flow
import type { Claim } from 'types/claim';
import React from 'react';
neb-b commented 2019-04-24 15:43:00 +02:00 (Migrated from github.com)
Review

I removed flow from this file because there were ~30 type errors. If we have flow in a file, it should cover everything so we don't need to constantly use --no-verify

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,8 @@
// @flow
import type { Claim, Metadata } from 'types/claim';
import type { FileInfo } from 'types/file_info';
import * as MODALS from 'constants/modal_types';
import * as icons from 'constants/icons';
import * as React from 'react';
import { buildURI, normalizeURI } from 'lbry-redux';
import {buildURI, normalizeURI} from 'lbry-redux';
import FileViewer from 'component/fileViewer';
import Thumbnail from 'component/common/thumbnail';
import FilePrice from 'component/filePrice';
@ -22,15 +20,15 @@ import getMediaType from 'util/get-media-type';
import RecommendedContent from 'component/recommendedContent';
type Props = {
claim: Claim,
fileInfo: FileInfo,
metadata: Metadata,
claim: StreamClaim,
fileInfo: FileListItem,
metadata: StreamMetadata,
contentType: string,
uri: string,
rewardedContentClaimIds: Array<string>,
obscureNsfw: boolean,
claimIsMine: boolean,
costInfo: ?{ cost: number },
costInfo: ?{cost: number},
fetchFileInfo: string => void,
fetchCostInfo: string => void,
setViewed: string => void,
kauffj commented 2019-04-26 23:11:41 +02:00 (Migrated from github.com)
Review

It looks like this component both receives the entire Claim and properties of the claim independently. Should it always be one or the other?

It looks like this component both receives the entire `Claim` and properties of the claim independently. Should it always be one or the other?
neb-b commented 2019-04-26 23:29:42 +02:00 (Migrated from github.com)
Review

I think we should start moving to bring in individual properties that components use: see makeSelectTitleForUri, instead of large objects if the component only uses a couple of values.

It's faster for react to update because then it isn't doing tons of un-needed checks to see if an object changed at all.

I think we should start moving to bring in individual properties that components use: see `makeSelectTitleForUri`, instead of large objects if the component only uses a couple of values. It's faster for react to update because then it isn't doing tons of un-needed checks to see if an object changed at all.
@ -39,10 +37,13 @@ type Props = {
channelUri: string,
viewCount: number,
prepareEdit: ({}, string) => void,
openModal: (id: string, { uri: string }) => void,
openModal: (id: string, {uri: string}) => void,
markSubscriptionRead: (string, string) => void,
fetchViewCount: string => void,
balance: number,
title: string,
thumbnail: ?string,
nsfw: boolean,
};
class FilePage extends React.Component<Props> {
@ -71,11 +72,11 @@ class FilePage extends React.Component<Props> {
claim,
} = this.props;
if (isSubscribed) {
if(isSubscribed) {
this.removeFromSubscriptionNotifications();
}
if (claimIsMine) {
if(claimIsMine) {
fetchViewCount(claim.claim_id);
}
@ -90,26 +91,26 @@ class FilePage extends React.Component<Props> {
}
componentWillReceiveProps(nextProps: Props) {
const { fetchFileInfo, uri, setViewed } = this.props;
const {fetchFileInfo, uri, setViewed} = this.props;
// @if TARGET='app'
if (nextProps.fileInfo === undefined) {
if(nextProps.fileInfo === undefined) {
fetchFileInfo(uri);
}
// @endif
if (uri !== nextProps.uri) {
if(uri !== nextProps.uri) {
setViewed(nextProps.uri);
}
}
componentDidUpdate(prevProps: Props) {
const { isSubscribed, claim, uri, fetchViewCount, claimIsMine } = this.props;
const {isSubscribed, claim, uri, fetchViewCount, claimIsMine} = this.props;
if (!prevProps.isSubscribed && isSubscribed) {
if(!prevProps.isSubscribed && isSubscribed) {
this.removeFromSubscriptionNotifications();
}
if (prevProps.uri !== uri && claimIsMine) {
if(prevProps.uri !== uri && claimIsMine) {
fetchViewCount(claim.claim_id);
}
}
@ -117,7 +118,7 @@ class FilePage extends React.Component<Props> {
removeFromSubscriptionNotifications() {
// Always try to remove
// If it doesn't exist, nothing will happen
const { markSubscriptionRead, uri, channelUri } = this.props;
const {markSubscriptionRead, uri, channelUri} = this.props;
markSubscriptionRead(channelUri, uri);
}
@ -137,21 +138,18 @@ class FilePage extends React.Component<Props> {
channelUri,
viewCount,
balance,
title,
thumbnail,
nsfw,
} = this.props;
// File info
const { title, thumbnail } = metadata;
const { height, channel_name: channelName } = claim;
const { PLAYABLE_MEDIA_TYPES, PREVIEW_MEDIA_TYPES } = FilePage;
const {height, channel_name: channelName} = claim;
const {PLAYABLE_MEDIA_TYPES, PREVIEW_MEDIA_TYPES} = FilePage;
const isRewardContent = (rewardedContentClaimIds || []).includes(claim.claim_id);
const shouldObscureThumbnail = obscureNsfw && metadata.nsfw;
const shouldObscureThumbnail = obscureNsfw && nsfw;
const fileName = fileInfo ? fileInfo.file_name : null;
const mediaType = getMediaType(contentType, fileName);
console.log({
mediaType,
contentType,
fileName,
});
const showFile =
PLAYABLE_MEDIA_TYPES.includes(mediaType) || PREVIEW_MEDIA_TYPES.includes(mediaType);
@ -164,12 +162,12 @@ class FilePage extends React.Component<Props> {
// This is what the user is used to seeing, they don't care about the claim id
// We will select the claim id before they publish
let editUri;
if (claimIsMine) {
const uriObject: { contentName: string, claimId: string, channelName?: string } = {
if(claimIsMine) {
const uriObject: {contentName: string, claimId: string, channelName?: string} = {
contentName: claim.name,
claimId: claim.claim_id,
};
if (channelName) {
if(channelName) {
uriObject.channelName = channelName;
}
@ -204,16 +202,16 @@ class FilePage extends React.Component<Props> {
(thumbnail ? (
<Thumbnail shouldObscure={shouldObscureThumbnail} src={thumbnail} />
) : (
<div
className={classnames('content__empty', {
'content__empty--nsfw': shouldObscureThumbnail,
})}
>
<div className="card__media-text">
{__("Sorry, looks like we can't preview this file.")}
<div
className={classnames('content__empty', {
'content__empty--nsfw': shouldObscureThumbnail,
})}
>
<div className="card__media-text">
{__("Sorry, looks like we can't preview this file.")}
</div>
</div>
</div>
))}
))}
</div>
<div className="grid-area--info media__content media__content--large">
@ -225,13 +223,13 @@ class FilePage extends React.Component<Props> {
size={20}
iconColor="red"
icon={icons.FEATURED}
// Figure out how to get the tooltip to overlap the navbar on the file page and I will love you
// https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue
// https://spee.ch/4/overflow-issue
// tooltip="bottom"
// Figure out how to get the tooltip to overlap the navbar on the file page and I will love you
// https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue
// https://spee.ch/4/overflow-issue
// tooltip="bottom"
/>
)}
{metadata.nsfw && <div className="badge badge--nsfw">NSFW</div>}
{nsfw && <div className="badge badge--nsfw">MATURE</div>}
<FilePrice badge uri={normalizeURI(uri)} />
</div>
@ -271,7 +269,7 @@ class FilePage extends React.Component<Props> {
button="alt"
icon={icons.TIP}
label={__('Send a tip')}
onClick={() => openModal(MODALS.SEND_TIP, { uri })}
onClick={() => openModal(MODALS.SEND_TIP, {uri})}
/>
</React.Fragment>
)}
@ -279,7 +277,7 @@ class FilePage extends React.Component<Props> {
button="alt"
icon={icons.SHARE}
label={__('Share')}
onClick={() => openModal(MODALS.SOCIAL_SHARE, { uri, speechShareable })}
onClick={() => openModal(MODALS.SOCIAL_SHARE, {uri, speechShareable})}
/>
</div>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Nice :D

Nice :D

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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