commit
d7b4e91544
23 changed files with 350 additions and 92 deletions
|
@ -46,7 +46,8 @@
|
||||||
"electron-log": "^2.2.12",
|
"electron-log": "^2.2.12",
|
||||||
"electron-updater": "^4.0.6",
|
"electron-updater": "^4.0.6",
|
||||||
"express": "^4.16.4",
|
"express": "^4.16.4",
|
||||||
"keytar": "^4.4.1"
|
"keytar": "^4.4.1",
|
||||||
|
"tiny-relative-date": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.0.0",
|
"@babel/core": "^7.0.0",
|
||||||
|
@ -122,7 +123,7 @@
|
||||||
"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#3c862e72b36e620155b9c968be9b2bcbd9e0580c",
|
"lbry-redux": "lbryio/lbry-redux#141593500693a93db74c62ef5a9fe67b43896603",
|
||||||
"lbryinc": "lbryio/lbryinc#43d382d9b74d396a581a74d87e4c53105e04f845",
|
"lbryinc": "lbryio/lbryinc#43d382d9b74d396a581a74d87e4c53105e04f845",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
|
@ -148,7 +149,7 @@
|
||||||
"react-dom": "^16.8.2",
|
"react-dom": "^16.8.2",
|
||||||
"react-feather": "^1.0.8",
|
"react-feather": "^1.0.8",
|
||||||
"react-ga": "^2.5.7",
|
"react-ga": "^2.5.7",
|
||||||
"react-hot-loader": "^4.7.2",
|
"react-hot-loader": "^4.11.1",
|
||||||
"react-modal": "^3.1.7",
|
"react-modal": "^3.1.7",
|
||||||
"react-paginate": "^5.2.1",
|
"react-paginate": "^5.2.1",
|
||||||
"react-pose": "^4.0.5",
|
"react-pose": "^4.0.5",
|
||||||
|
|
7
src/ui/component/comment/index.js
Normal file
7
src/ui/component/comment/index.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import Comment from './view';
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)(Comment);
|
36
src/ui/component/comment/view.jsx
Normal file
36
src/ui/component/comment/view.jsx
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import relativeDate from 'tiny-relative-date';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
author: string,
|
||||||
|
message: string,
|
||||||
|
timePosted: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
function Comment(props: Props) {
|
||||||
|
const { author, timePosted, message } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className="comment">
|
||||||
|
<div className="comment__meta card__actions--between">
|
||||||
|
<strong>{author || __('Anonymous')}</strong>
|
||||||
|
|
||||||
|
<time dateTime={timePosted}>{relativeDate(timePosted)}</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>{message}</p>
|
||||||
|
{/* The following is for adding threaded replies, upvoting and downvoting */}
|
||||||
|
{/* <div className="comment__actions card__actions--between"> */}
|
||||||
|
{/* <button className={'button button--primary'}>Reply</button> */}
|
||||||
|
|
||||||
|
{/* <span className="comment__actions-wrap"> */}
|
||||||
|
{/* <button className="comment__action upvote">Up</button> */}
|
||||||
|
{/* <button className="comment__action downvote">Down</button> */}
|
||||||
|
{/* </span> */}
|
||||||
|
{/* </div> */}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Comment;
|
17
src/ui/component/commentCreate/index.js
Normal file
17
src/ui/component/commentCreate/index.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { doCommentCreate, makeSelectClaimForUri } from 'lbry-redux';
|
||||||
|
import { CommentCreate } from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
createComment: (comment, claimId, channel) => dispatch(doCommentCreate(comment, claimId, channel)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(CommentCreate);
|
83
src/ui/component/commentCreate/view.jsx
Normal file
83
src/ui/component/commentCreate/view.jsx
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
// @flow
|
||||||
|
import { CHANNEL_NEW } from 'constants/claim';
|
||||||
|
import React from 'react';
|
||||||
|
import { FormField, Form } from 'component/common/form';
|
||||||
|
import Button from 'component/button';
|
||||||
|
import ChannelSection from 'component/selectChannel';
|
||||||
|
import usePersistedState from 'util/use-persisted-state';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
uri: string,
|
||||||
|
claim: StreamClaim,
|
||||||
|
createComment: (string, string, string) => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function CommentCreate(props: Props) {
|
||||||
|
const { createComment, claim } = props;
|
||||||
|
const { claim_id: claimId } = claim;
|
||||||
|
const [commentValue, setCommentValue] = usePersistedState(`comment-${claimId}`, '');
|
||||||
|
const [commentAck, setCommentAck] = usePersistedState('comment-acknowledge', false);
|
||||||
|
const [channel, setChannel] = usePersistedState('comment-channel', 'anonymous');
|
||||||
|
|
||||||
|
function handleCommentChange(event) {
|
||||||
|
setCommentValue(event.target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChannelChange(channel) {
|
||||||
|
setChannel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCommentAck(event) {
|
||||||
|
setCommentAck(true);
|
||||||
|
}
|
||||||
|
function handleSubmit() {
|
||||||
|
if (channel !== CHANNEL_NEW && commentValue.length) createComment(commentValue, claimId, channel);
|
||||||
|
setCommentValue('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{commentAck !== true && (
|
||||||
|
<section>
|
||||||
|
<div className="card__content">
|
||||||
|
<div className="media__title">About comments..</div>
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
<div className="media__subtitle">I acknowledge something.</div>
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
<Button button="primary" onClick={handleCommentAck} label={__('Got it!')} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
{commentAck === true && (
|
||||||
|
<section>
|
||||||
|
<Form onSubmit={handleSubmit}>
|
||||||
|
<div className="card__content">
|
||||||
|
<ChannelSection channel={channel} onChannelChange={handleChannelChange} />
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
<FormField
|
||||||
|
disabled={channel === CHANNEL_NEW}
|
||||||
|
type="textarea"
|
||||||
|
name="content_description"
|
||||||
|
label={__('Comment')}
|
||||||
|
placeholder={__('Your comment')}
|
||||||
|
value={commentValue}
|
||||||
|
onChange={handleCommentChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="card__actions">
|
||||||
|
<Button
|
||||||
|
button="primary"
|
||||||
|
disabled={channel === CHANNEL_NEW || !commentValue.length}
|
||||||
|
type="submit"
|
||||||
|
label={__('Post')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</section>
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
16
src/ui/component/commentsList/index.js
Normal file
16
src/ui/component/commentsList/index.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { makeSelectCommentsForUri, doCommentList } from 'lbry-redux';
|
||||||
|
import CommentsList from './view';
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
comments: makeSelectCommentsForUri(props.uri)(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
fetchComments: uri => dispatch(doCommentList(uri)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(CommentsList);
|
38
src/ui/component/commentsList/view.jsx
Normal file
38
src/ui/component/commentsList/view.jsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// @flow
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import Comment from 'component/comment';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
comments: Array<any>,
|
||||||
|
fetchComments: string => void,
|
||||||
|
uri: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
function CommentList(props: Props) {
|
||||||
|
const { fetchComments, uri, comments } = props;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchComments(uri);
|
||||||
|
}, [fetchComments, uri]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className="comments">
|
||||||
|
{comments &&
|
||||||
|
comments.map(comment => {
|
||||||
|
return (
|
||||||
|
<Comment
|
||||||
|
author={comment.channel_name}
|
||||||
|
claimId={comment.channel_id}
|
||||||
|
commentId={comment.comment_id}
|
||||||
|
key={comment.channel_id + comment.comment_id}
|
||||||
|
message={comment.comment}
|
||||||
|
parentId={comment.parent_id || null}
|
||||||
|
timePosted={comment.timestamp * 1000}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CommentList;
|
|
@ -154,6 +154,7 @@ export class FormField extends React.PureComponent<Props> {
|
||||||
} else if (type === 'textarea') {
|
} else if (type === 'textarea') {
|
||||||
input = (
|
input = (
|
||||||
<fieldset-section>
|
<fieldset-section>
|
||||||
|
<label htmlFor={name}>{label}</label>
|
||||||
<textarea type={type} id={name} {...inputProps} />
|
<textarea type={type} id={name} {...inputProps} />
|
||||||
</fieldset-section>
|
</fieldset-section>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,12 +4,9 @@ import {
|
||||||
makeSelectContentTypeForUri,
|
makeSelectContentTypeForUri,
|
||||||
makeSelectMetadataForUri,
|
makeSelectMetadataForUri,
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
doToast,
|
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { selectUser } from 'lbryinc';
|
import { selectUser } from 'lbryinc';
|
||||||
import { doOpenFileInFolder } from 'redux/actions/file';
|
import { doOpenFileInFolder } from 'redux/actions/file';
|
||||||
import { selectHasClickedComment } from 'redux/selectors/app';
|
|
||||||
import { doClickCommentButton } from 'redux/actions/app';
|
|
||||||
import FileDetails from './view';
|
import FileDetails from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -17,14 +14,11 @@ const select = (state, props) => ({
|
||||||
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
|
||||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||||
hasClickedComment: selectHasClickedComment(state),
|
|
||||||
user: selectUser(state),
|
user: selectUser(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
openFolder: path => dispatch(doOpenFileInFolder(path)),
|
openFolder: path => dispatch(doOpenFileInFolder(path)),
|
||||||
showSnackBar: message => dispatch(doToast({ message })),
|
|
||||||
clickCommentButton: () => dispatch(doClickCommentButton()),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React, { Fragment, PureComponent } from 'react';
|
import React, { Fragment, PureComponent } from 'react';
|
||||||
import { Lbryio } from 'lbryinc';
|
|
||||||
import MarkdownPreview from 'component/common/markdown-preview';
|
import MarkdownPreview from 'component/common/markdown-preview';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import Expandable from 'component/expandable';
|
import Expandable from 'component/expandable';
|
||||||
|
@ -12,33 +11,12 @@ type Props = {
|
||||||
metadata: StreamMetadata,
|
metadata: StreamMetadata,
|
||||||
openFolder: string => void,
|
openFolder: string => void,
|
||||||
contentType: string,
|
contentType: string,
|
||||||
clickCommentButton: () => void,
|
|
||||||
showSnackBar: React$Node => void,
|
|
||||||
hasClickedComment: boolean,
|
|
||||||
user: ?any,
|
user: ?any,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileDetails extends PureComponent<Props> {
|
class FileDetails extends PureComponent<Props> {
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
(this: any).handleCommentClick = this.handleCommentClick.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCommentClick() {
|
|
||||||
const { clickCommentButton, showSnackBar } = this.props;
|
|
||||||
|
|
||||||
clickCommentButton();
|
|
||||||
Lbryio.call('user_tag', 'edit', { add: 'comments-waitlist' });
|
|
||||||
showSnackBar(
|
|
||||||
<span>
|
|
||||||
{__('Thanks! Comments are coming soon')}
|
|
||||||
<sup>TM</sup>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { claim, contentType, fileInfo, metadata, openFolder, hasClickedComment, user } = this.props;
|
const { claim, contentType, fileInfo, metadata, openFolder } = this.props;
|
||||||
|
|
||||||
if (!claim || !metadata) {
|
if (!claim || !metadata) {
|
||||||
return (
|
return (
|
||||||
|
@ -104,26 +82,6 @@ class FileDetails extends PureComponent<Props> {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Expandable>
|
</Expandable>
|
||||||
|
|
||||||
<div className="media__info-title">Comments</div>
|
|
||||||
|
|
||||||
<div className="card__actions--center">
|
|
||||||
<Button
|
|
||||||
data-id="add-comment"
|
|
||||||
disabled={hasClickedComment}
|
|
||||||
button="primary"
|
|
||||||
label={__('Want to comment?')}
|
|
||||||
onClick={this.handleCommentClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
{hasClickedComment && (
|
|
||||||
<p className="media__info-text media__info-text--center">
|
|
||||||
{user
|
|
||||||
? __('Your support has been added. You will be notified when comments are available.')
|
|
||||||
: __('Your support has been added. Comments are coming soon.')}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +1,57 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { remote } from 'electron';
|
import { remote } from 'electron';
|
||||||
import React, { Suspense } from 'react';
|
import React 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<*>(() =>
|
const AudioViewer = React.lazy<*>(() =>
|
||||||
import(/* webpackChunkName: "audioViewer" */
|
import(
|
||||||
'component/viewers/audioViewer')
|
/* webpackChunkName: "audioViewer" */
|
||||||
|
'component/viewers/audioViewer'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const DocumentViewer = React.lazy<*>(() =>
|
const DocumentViewer = React.lazy<*>(() =>
|
||||||
import(/* webpackChunkName: "documentViewer" */
|
import(
|
||||||
'component/viewers/documentViewer')
|
/* webpackChunkName: "documentViewer" */
|
||||||
|
'component/viewers/documentViewer'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const DocxViewer = React.lazy<*>(() =>
|
const DocxViewer = React.lazy<*>(() =>
|
||||||
import(/* webpackChunkName: "docxViewer" */
|
import(
|
||||||
'component/viewers/docxViewer')
|
/* webpackChunkName: "docxViewer" */
|
||||||
|
'component/viewers/docxViewer'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const HtmlViewer = React.lazy<*>(() =>
|
const HtmlViewer = React.lazy<*>(() =>
|
||||||
import(/* webpackChunkName: "htmlViewer" */
|
import(
|
||||||
'component/viewers/htmlViewer')
|
/* webpackChunkName: "htmlViewer" */
|
||||||
|
'component/viewers/htmlViewer'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const PdfViewer = React.lazy<*>(() =>
|
const PdfViewer = React.lazy<*>(() =>
|
||||||
import(/* webpackChunkName: "pdfViewer" */
|
import(
|
||||||
'component/viewers/pdfViewer')
|
/* webpackChunkName: "pdfViewer" */
|
||||||
|
'component/viewers/pdfViewer'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
const ComicBookViewer = React.lazy<*>(() =>
|
const ComicBookViewer = React.lazy<*>(() =>
|
||||||
import(/* webpackChunkName: "comicBookViewer" */
|
import(
|
||||||
'component/viewers/comicBookViewer')
|
/* webpackChunkName: "comicBookViewer" */
|
||||||
|
'component/viewers/comicBookViewer'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const ThreeViewer = React.lazy<*>(() =>
|
const ThreeViewer = React.lazy<*>(() =>
|
||||||
import(/* webpackChunkName: "threeViewer" */
|
import(
|
||||||
'component/viewers/threeViewer')
|
/* webpackChunkName: "threeViewer" */
|
||||||
|
'component/viewers/threeViewer'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
|
@ -174,7 +188,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.fee && claim.value.fee.amount > 0) {
|
if (claim && claim.value.fee && Number(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.'
|
||||||
);
|
);
|
||||||
|
|
|
@ -76,7 +76,9 @@ class FileViewer extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handleAutoplay(this.props);
|
this.handleAutoplay(this.props);
|
||||||
window.addEventListener('keydown', this.handleKeyDown);
|
// Commented out because it would play/pause if you were typing in the comment field
|
||||||
|
// Need a way to check if you are typing
|
||||||
|
// window.addEventListener('keydown', this.handleKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prev: Props) {
|
componentDidUpdate(prev: Props) {
|
||||||
|
|
|
@ -157,7 +157,13 @@ class ChannelSection extends React.PureComponent<Props, State> {
|
||||||
<BusyIndicator message="Updating channels" />
|
<BusyIndicator message="Updating channels" />
|
||||||
) : (
|
) : (
|
||||||
<fieldset-section>
|
<fieldset-section>
|
||||||
<FormField name="channel" type="select" onChange={this.handleChannelChange} value={channel}>
|
<FormField
|
||||||
|
name="channel"
|
||||||
|
label={__('Channel')}
|
||||||
|
type="select"
|
||||||
|
onChange={this.handleChannelChange}
|
||||||
|
value={channel}
|
||||||
|
>
|
||||||
<option value={CHANNEL_ANONYMOUS}>{__('Anonymous')}</option>
|
<option value={CHANNEL_ANONYMOUS}>{__('Anonymous')}</option>
|
||||||
{channels.map(({ name }) => (
|
{channels.map(({ name }) => (
|
||||||
<option key={name} value={name}>
|
<option key={name} value={name}>
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React, { Suspense } from 'react';
|
import React from 'react';
|
||||||
import { stopContextMenu } from 'util/context-menu';
|
import { stopContextMenu } from 'util/context-menu';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import(/* webpackChunkName: "videojs" */
|
import(
|
||||||
/* webpackPreload: true */
|
/* webpackChunkName: "videojs" */
|
||||||
'video.js/dist/video-js.css');
|
/* webpackPreload: true */
|
||||||
|
'video.js/dist/video-js.css'
|
||||||
|
);
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
source: {
|
source: {
|
||||||
|
@ -44,10 +46,12 @@ class AudioVideoViewer extends React.PureComponent<Props> {
|
||||||
sources,
|
sources,
|
||||||
};
|
};
|
||||||
|
|
||||||
import(/* webpackChunkName: "videojs" */
|
import(
|
||||||
|
/* webpackChunkName: "videojs" */
|
||||||
/* webpackMode: "lazy" */
|
/* webpackMode: "lazy" */
|
||||||
/* webpackPreload: true */
|
/* webpackPreload: true */
|
||||||
'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, () => {});
|
||||||
|
|
|
@ -20,6 +20,8 @@ import classnames from 'classnames';
|
||||||
import getMediaType from 'util/get-media-type';
|
import getMediaType from 'util/get-media-type';
|
||||||
import RecommendedContent from 'component/recommendedContent';
|
import RecommendedContent from 'component/recommendedContent';
|
||||||
import ClaimTags from 'component/claimTags';
|
import ClaimTags from 'component/claimTags';
|
||||||
|
import CommentsList from 'component/commentsList';
|
||||||
|
import CommentCreate from 'component/commentCreate';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
claim: StreamClaim,
|
claim: StreamClaim,
|
||||||
|
@ -285,6 +287,10 @@ class FilePage extends React.Component<Props> {
|
||||||
</div>
|
</div>
|
||||||
<div className="media__info--large">
|
<div className="media__info--large">
|
||||||
<FileDetails uri={uri} />
|
<FileDetails uri={uri} />
|
||||||
|
|
||||||
|
<div className="media__info-title">{__('Comments')}</div>
|
||||||
|
<CommentCreate uri={uri} />
|
||||||
|
<CommentsList uri={uri} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid-area--related">
|
<div className="grid-area--related">
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
walletReducer,
|
walletReducer,
|
||||||
notificationsReducer,
|
notificationsReducer,
|
||||||
tagsReducerBuilder,
|
tagsReducerBuilder,
|
||||||
|
commentReducer,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { userReducer, rewardsReducer, costInfoReducer, blacklistReducer, homepageReducer, statsReducer } from 'lbryinc';
|
import { userReducer, rewardsReducer, costInfoReducer, blacklistReducer, homepageReducer, statsReducer } from 'lbryinc';
|
||||||
import appReducer from 'redux/reducers/app';
|
import appReducer from 'redux/reducers/app';
|
||||||
|
@ -34,6 +35,7 @@ export default history =>
|
||||||
availability: availabilityReducer,
|
availability: availabilityReducer,
|
||||||
blacklist: blacklistReducer,
|
blacklist: blacklistReducer,
|
||||||
claims: claimsReducer,
|
claims: claimsReducer,
|
||||||
|
comments: commentReducer,
|
||||||
content: contentReducer,
|
content: contentReducer,
|
||||||
costInfo: costInfoReducer,
|
costInfo: costInfoReducer,
|
||||||
fileInfo: fileInfoReducer,
|
fileInfo: fileInfoReducer,
|
||||||
|
|
|
@ -36,6 +36,7 @@ export type AppState = {
|
||||||
isUpgradeAvailable: ?boolean,
|
isUpgradeAvailable: ?boolean,
|
||||||
isUpgradeSkipped: ?boolean,
|
isUpgradeSkipped: ?boolean,
|
||||||
hasClickedComment: boolean,
|
hasClickedComment: boolean,
|
||||||
|
enhancedLayout: boolean,
|
||||||
searchOptionsExpanded: boolean,
|
searchOptionsExpanded: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
@import 'component/button';
|
@import 'component/button';
|
||||||
@import 'component/card';
|
@import 'component/card';
|
||||||
@import 'component/channel';
|
@import 'component/channel';
|
||||||
|
@import 'component/comments';
|
||||||
@import 'component/content';
|
@import 'component/content';
|
||||||
@import 'component/credit';
|
@import 'component/credit';
|
||||||
@import 'component/dat-gui';
|
@import 'component/dat-gui';
|
||||||
|
|
|
@ -25,10 +25,6 @@
|
||||||
.card--section {
|
.card--section {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: var(--spacing-large);
|
padding: var(--spacing-large);
|
||||||
|
|
||||||
.card__content:not(:last-of-type) {
|
|
||||||
margin-bottom: var(--spacing-large);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card--space-between {
|
.card--space-between {
|
||||||
|
@ -100,6 +96,10 @@
|
||||||
// C O N T E N T
|
// C O N T E N T
|
||||||
|
|
||||||
.card__content {
|
.card__content {
|
||||||
|
&:not(:last-of-type) {
|
||||||
|
margin-bottom: var(--spacing-medium);
|
||||||
|
}
|
||||||
|
|
||||||
p:not(:last-child) {
|
p:not(:last-child) {
|
||||||
margin-bottom: var(--spacing-medium);
|
margin-bottom: var(--spacing-medium);
|
||||||
}
|
}
|
||||||
|
|
55
src/ui/scss/component/_comments.scss
Normal file
55
src/ui/scss/component/_comments.scss
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
.comments {
|
||||||
|
margin-top: var(--spacing-large);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
padding: var(--spacing-medium) 0;
|
||||||
|
|
||||||
|
&:not(:last-of-type) {
|
||||||
|
border-bottom: 1px solid var(--lbry-gray-1);
|
||||||
|
|
||||||
|
[data-mode='dark'] & {
|
||||||
|
border-color: rgba($lbry-gray-5, 0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// .comment__actions-wrap {
|
||||||
|
// align-items: center;
|
||||||
|
// display: flex;
|
||||||
|
// justify-content: space-between;
|
||||||
|
// width: 4.5rem;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// .comment__action {
|
||||||
|
// @include hide-text;
|
||||||
|
// width: 0;
|
||||||
|
// height: 0;
|
||||||
|
// transition: border-color 0.2s;
|
||||||
|
|
||||||
|
// &.downvote {
|
||||||
|
// border-top: 2rem solid var(--lbry-orange-3);
|
||||||
|
// border-right: 1rem solid transparent;
|
||||||
|
// border-left: 1rem solid transparent;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// border-top-color: var(--lbry-yellow-3);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// &.upvote {
|
||||||
|
// border-right: 1rem solid transparent;
|
||||||
|
// border-bottom: 2rem solid var(--lbry-teal-4);
|
||||||
|
// border-left: 1rem solid transparent;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// border-bottom-color: var(--lbry-green-2);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
.comment__meta {
|
||||||
|
time {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,12 @@ export default function usePersistedState(key, firstTimeDefault) {
|
||||||
// If no key is passed in, act as a normal `useState`
|
// If no key is passed in, act as a normal `useState`
|
||||||
let defaultValue;
|
let defaultValue;
|
||||||
if (key) {
|
if (key) {
|
||||||
defaultValue = localStorage.getItem(key);
|
const item = localStorage.getItem(key);
|
||||||
|
if (item === 'true') {
|
||||||
|
defaultValue = true;
|
||||||
|
} else if (item === 'false') {
|
||||||
|
defaultValue = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!defaultValue) {
|
if (!defaultValue) {
|
||||||
|
|
|
@ -287,5 +287,11 @@
|
||||||
"% complete": "% complete",
|
"% complete": "% complete",
|
||||||
"Updates published": "Updates published",
|
"Updates published": "Updates published",
|
||||||
"Your updates have been published to LBRY at the address": "Your updates have been published to LBRY at the address",
|
"Your updates have been published to LBRY at the address": "Your updates have been published to LBRY at the address",
|
||||||
"The updates will take a few minutes to appear for other LBRY users. Until then your file will be listed as \"pending\" under your published files.": "The updates will take a few minutes to appear for other LBRY users. Until then your file will be listed as \"pending\" under your published files."
|
"The updates will take a few minutes to appear for other LBRY users. Until then your file will be listed as \"pending\" under your published files.": "The updates will take a few minutes to appear for other LBRY users. Until then your file will be listed as \"pending\" under your published files.",
|
||||||
|
"Comments": "Comments",
|
||||||
|
"Comment": "Comment",
|
||||||
|
"Your comment": "Your comment",
|
||||||
|
"Post": "Post",
|
||||||
|
"Channel": "Channel",
|
||||||
|
"No modifier provided after separator %s.": "No modifier provided after separator %s."
|
||||||
}
|
}
|
17
yarn.lock
17
yarn.lock
|
@ -6641,9 +6641,9 @@ 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#3c862e72b36e620155b9c968be9b2bcbd9e0580c:
|
lbry-redux@lbryio/lbry-redux#141593500693a93db74c62ef5a9fe67b43896603:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/3c862e72b36e620155b9c968be9b2bcbd9e0580c"
|
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/141593500693a93db74c62ef5a9fe67b43896603"
|
||||||
dependencies:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
@ -9469,10 +9469,10 @@ react-ga@^2.5.7:
|
||||||
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.7.tgz#1c80a289004bf84f84c26d46f3a6a6513081bf2e"
|
resolved "https://registry.yarnpkg.com/react-ga/-/react-ga-2.5.7.tgz#1c80a289004bf84f84c26d46f3a6a6513081bf2e"
|
||||||
integrity sha512-UmATFaZpEQDO96KFjB5FRLcT6hFcwaxOmAJZnjrSiFN/msTqylq9G+z5Z8TYzN/dbamDTiWf92m6MnXXJkAivQ==
|
integrity sha512-UmATFaZpEQDO96KFjB5FRLcT6hFcwaxOmAJZnjrSiFN/msTqylq9G+z5Z8TYzN/dbamDTiWf92m6MnXXJkAivQ==
|
||||||
|
|
||||||
react-hot-loader@^4.7.2:
|
react-hot-loader@^4.11.1:
|
||||||
version "4.8.7"
|
version "4.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.8.7.tgz#8bcec3105d4e0e3cba52aa59800568fc9fb3322d"
|
resolved "https://registry.yarnpkg.com/react-hot-loader/-/react-hot-loader-4.11.1.tgz#2cabbd0f1c8a44c28837b86d6ce28521e6d9a8ac"
|
||||||
integrity sha512-ctWAu8iwp37qd4w1qhjN6neDA1e5bSmAUY46L2l5SeK+i8AfzX+7lrpaLW4TJVaiBv5MlqIzA1ClNnvlvsy5Lg==
|
integrity sha512-HAC0UedYzM3mD+ZaQHesntFO0yi2ftOV4ZMMRTj43E4GvW5sQqYTPvur+6J7EaH3MDr/RqjDKXyCqKepV8+y7w==
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-levenshtein "^2.0.6"
|
fast-levenshtein "^2.0.6"
|
||||||
global "^4.3.0"
|
global "^4.3.0"
|
||||||
|
@ -11213,6 +11213,11 @@ tiny-invariant@^1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463"
|
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463"
|
||||||
integrity sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g==
|
integrity sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g==
|
||||||
|
|
||||||
|
tiny-relative-date@^1.3.0:
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07"
|
||||||
|
integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==
|
||||||
|
|
||||||
tiny-warning@^1.0.0:
|
tiny-warning@^1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28"
|
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28"
|
||||||
|
|
Loading…
Reference in a new issue