Merge origin/master into issue/763
This commit is contained in:
commit
4cab8055ca
27 changed files with 194 additions and 42 deletions
|
@ -8,7 +8,7 @@ Web UI version numbers should always match the corresponding version of LBRY App
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
### Added
|
### Added
|
||||||
*
|
* Added copy address button to the Wallet Address component on Send / Receive (#875)
|
||||||
*
|
*
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -1,3 +1,58 @@
|
||||||
## Contributing to LBRY
|
# Contributing to LBRY
|
||||||
|
|
||||||
https://lbry.io/faq/contributing
|
You found this page! That means you are well on your way to contributing to the LBRY app. This application is primarily written in JavaScript and is built on [Electron](https://electronjs.org) while utilizing [React](https://reactjs.org) and [Redux](https://redux.js.org) for UI and application state.
|
||||||
|
|
||||||
|
## TL;DR?
|
||||||
|
* [Here](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee) is a list of help wanted issues.
|
||||||
|
* Comment on an issue to let us know if you are going to work on it, don't take an issue that someone has reserved less than 3 days ago
|
||||||
|
* Submit a pull request and get paid in LBC
|
||||||
|
* Don't hesitate to contact us with any questions or comments
|
||||||
|
|
||||||
|
## Longer Version
|
||||||
|
|
||||||
|
LBRY is an open source project, and therefore is developed out in the open for everyone to see. What you see here are the latest source code changes and issues.
|
||||||
|
|
||||||
|
Since LBRY is an based around a decentralized community, we believe that the app will be strongest if it receives contributions from individuals outside the core team -- such as yourself!
|
||||||
|
|
||||||
|
In order to make contributing as easy and rewarding of possible, we have instituted the following system:
|
||||||
|
|
||||||
|
* Anyone can view all issues in the system by clicking on the [Issues](https://github.com/lbryio/lbry-app/issues) button at the top of the page. Feel free to add an issue if you think we have missed something (and you might earn some LBC in the process because we do tip people for reporting bugs).
|
||||||
|
* Once on the [Issues](https://github.com/lbryio/lbry-app/issues) page, a potential contributor can filter issues by the [Help Wanted](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee) label to see a curated list of suggested issues with which community members can help.
|
||||||
|
* Every [Help Wanted](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee) issue is ranked on a scale from zero to four.
|
||||||
|
|
||||||
|
Level | Description
|
||||||
|
--- | ---
|
||||||
|
[**level 0**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+0%22+no%3Aassignee) | Typos and text edits -- a tech savvy non-programmer can fix these
|
||||||
|
[**level 1**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+1%22+no%3Aassignee) | Programming issues that require little knowledge of how the LBRY app works
|
||||||
|
[**level 2**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+2%22+no%3Aassignee) | Issues of average difficulty that require the developer to dig into how the app works a little bit
|
||||||
|
[**level 3**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+3%22+no%3Aassignee) | Issues that are likely too tricky to be level 2 or require more thinking outside of the box
|
||||||
|
[**level 4**](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level+4%22+no%3Aassignee) | Big features or really hard issues
|
||||||
|
|
||||||
|
The process of ranking issues is highly subjective. The purpose of sorting issues like this is to give contributors a general idea about the type of issues they are looking at. It could very well be the case that a level 1 issue is more difficult than a level 2, for instance. This system is meant to help you find relevant issues, not to prevent you from working on issues that you otherwise would. If these rankings don't work for you, feel free to ignore them.
|
||||||
|
|
||||||
|
* After deciding what to work on, a potential contributor can [fork](https://help.github.com/articles/fork-a-repo/) this repository, make his or her changes, and submit a [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). A contributor wanting to reserve an issue in advance can leave a comment saying that he or she is working on it. Contributors should respect other people's efforts to complete issues in a timely manner and, therefore, not begin working on anything reserved (or updated) within the last 3 days. If someone has been officially assigned an issue via Github's assignment system, it is also not available. Contributors are encouraged to ask if they have any questions about issue availability.
|
||||||
|
* Once the pull request is visible in the LBRY repo, a LBRY team member will review it and make sure it is up to our standards. At this point, the contributor may have to change his or her code based on our suggestions and comments.
|
||||||
|
* Then, upon a satisfactory review of the code, we will merge it and send the contributor a tip (in LBC) for the contribution.
|
||||||
|
|
||||||
|
We are dedicated to being fair and friendly in this process. In __general__, level 4 issues will be paid more than level 3 issues which will be paid more than level 2, and so on. However, this is not due to their labeling, but rather how difficult they end up being. Maybe an issue labeled "level 1" turns out to be very difficult. In this case we would be **more than happy** to tip accordingly. If you do good work, we want you to be rewarded for it.
|
||||||
|
|
||||||
|
Also, we are here to enable you. We want you to succeed, so do not hesitate to ask questions. If you need some information or assistance in completing an issue, please let us know! That is what we are here for-- pushing development forward.
|
||||||
|
|
||||||
|
Lastly, don't feel limited by this list. Should LBRY have built-in Tor support? Add it! It's not in the issue tracker but maybe it's a good idea. Do you think the search layout is unintuitive? Change it! We welcome all feedback and suggestions. That said, it may be the case that we do not wish to incorporate your change if you don't check with us first (also, please check with us especially if you are planning on adding Tor support :P). If you want to add a feature that is not listed in the issue tracker, go ahead and [create an issue](https://github.com/lbryio/lbry-app/issues/new), and say in the description that you would like to try to implement it yourself. This way we can tell you in advance if we will accept your changes and we can point you in the right direction.
|
||||||
|
|
||||||
|
# Tom's "Voice of the User" Wishlist
|
||||||
|
|
||||||
|
[Anything marked with **both** "Help Wanted" and "Tom's 'Voice of the User' Wishlist"](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22Tom%27s+%5C%22Voice+of+the+User%5C%22+Wishlist%22+label%3A%22help+wanted%22+no%3Aassignee) will earn you an extra 50 LBC on top of what we would otherwise tip you.
|
||||||
|
|
||||||
|
# Get In Touch
|
||||||
|
Name | Role | Discord | Email
|
||||||
|
--- | --- | --- | ---
|
||||||
|
[Liam](https://github.com/liamcardenas) | The application engineer in charge of community development. He is the person to contact with any development/contribution related questions. | liamsdouble | liam@lbry.io
|
||||||
|
[Tom](https://github.com/tzarebczan) | Community manager. He knows more than anyone about the app and all of its flaws. Reach out to him with any questions about how the app works, if a bug has been reported, or if a feature should be requested. | jiggytom | tom@lbry.io
|
||||||
|
[Sean](https://github.com/seanyesmunt) | An application engineer who focuses largely on UI/UX. If you have a design or implementation question, feel free to reach out to him. | sean | sean@lbry.io
|
||||||
|
|
||||||
|
Join our discord [here](https://chat.lbry.io/).
|
||||||
|
|
||||||
|
# More Information
|
||||||
|
|
||||||
|
More information about contributing to LBRY [here](https://lbry.io/faq/contributing).
|
||||||
|
|
|
@ -99,7 +99,7 @@ if ! cmd_exists yarn; then
|
||||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | $SUDO apt-key add -
|
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | $SUDO apt-key add -
|
||||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | $SUDO tee /etc/apt/sources.list.d/yarn.list
|
echo "deb https://dl.yarnpkg.com/debian/ stable main" | $SUDO tee /etc/apt/sources.list.d/yarn.list
|
||||||
$SUDO apt-get update
|
$SUDO apt-get update
|
||||||
$SUDO apt-get install yarn
|
$SUDO apt-get -o Dpkg::Options::="--force-overwrite" install yarn
|
||||||
elif $OSX; then
|
elif $OSX; then
|
||||||
brew install yarn
|
brew install yarn
|
||||||
else
|
else
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
],
|
],
|
||||||
"linux": {
|
"linux": {
|
||||||
"target": "deb",
|
"target": "deb",
|
||||||
|
"category": "Video",
|
||||||
"desktop": {
|
"desktop": {
|
||||||
"MimeType": "x-scheme-handler/lbry",
|
"MimeType": "x-scheme-handler/lbry",
|
||||||
"Exec": "/opt/LBRY/lbry %U"
|
"Exec": "/opt/LBRY/lbry %U"
|
||||||
|
|
|
@ -86,19 +86,21 @@ class FileCard extends React.PureComponent {
|
||||||
<TruncatedText lines={1}>{title}</TruncatedText>
|
<TruncatedText lines={1}>{title}</TruncatedText>
|
||||||
</div>
|
</div>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
<span className="card__indicators">
|
<span className="card__indicators card--file-subtitle">
|
||||||
<FilePrice uri={uri} /> {isRewardContent && <Icon icon={icons.FEATURED} />}{' '}
|
<FilePrice uri={uri} /> {isRewardContent && <Icon icon={icons.FEATURED} />}{' '}
|
||||||
{fileInfo && <Icon icon={icons.LOCAL} />}
|
{fileInfo && <Icon icon={icons.LOCAL} />}
|
||||||
</span>
|
</span>
|
||||||
<UriIndicator uri={uri} smallCard />
|
<span className="card--file-subtitle">
|
||||||
|
<UriIndicator uri={uri} link span smallCard />
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Test for nizuka's design: should we remove description?
|
</Link>
|
||||||
|
{/* Test for nizuka's design: should we remove description?
|
||||||
<div className="card__content card__subtext card__subtext--two-lines">
|
<div className="card__content card__subtext card__subtext--two-lines">
|
||||||
<TruncatedMarkdown lines={2}>{description}</TruncatedMarkdown>
|
<TruncatedMarkdown lines={2}>{description}</TruncatedMarkdown>
|
||||||
</div>
|
</div>
|
||||||
*/}
|
*/}
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
{obscureNsfw && this.state.hovered && <NsfwOverlay />}
|
{obscureNsfw && this.state.hovered && <NsfwOverlay />}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
||||||
import { doFetchAvailability } from 'redux/actions/availability';
|
import { doFetchAvailability } from 'redux/actions/availability';
|
||||||
import { doOpenFileInShell } from 'redux/actions/file_info';
|
import { doOpenFileInShell } from 'redux/actions/file_info';
|
||||||
import { doPurchaseUri, doStartDownload } from 'redux/actions/content';
|
import { doPurchaseUri, doStartDownload } from 'redux/actions/content';
|
||||||
|
import { setVideoPause } from 'redux/actions/video';
|
||||||
import FileDownloadLink from './view';
|
import FileDownloadLink from './view';
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
|
@ -24,6 +25,7 @@ const perform = dispatch => ({
|
||||||
openInShell: path => dispatch(doOpenFileInShell(path)),
|
openInShell: path => dispatch(doOpenFileInShell(path)),
|
||||||
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
|
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
|
||||||
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
|
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
|
||||||
|
setVideoPause: val => dispatch(setVideoPause(val)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(FileDownloadLink);
|
export default connect(select, perform)(FileDownloadLink);
|
||||||
|
|
|
@ -34,7 +34,21 @@ class FileDownloadLink extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { fileInfo, downloading, uri, openInShell, purchaseUri, costInfo, loading } = this.props;
|
const {
|
||||||
|
fileInfo,
|
||||||
|
downloading,
|
||||||
|
uri,
|
||||||
|
openInShell,
|
||||||
|
purchaseUri,
|
||||||
|
costInfo,
|
||||||
|
loading,
|
||||||
|
setVideoPause,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const openFile = () => {
|
||||||
|
openInShell(fileInfo.download_path);
|
||||||
|
setVideoPause(true);
|
||||||
|
};
|
||||||
|
|
||||||
if (loading || downloading) {
|
if (loading || downloading) {
|
||||||
const progress =
|
const progress =
|
||||||
|
@ -82,7 +96,7 @@ class FileDownloadLink extends React.PureComponent {
|
||||||
button="text"
|
button="text"
|
||||||
icon="icon-external-link-square"
|
icon="icon-external-link-square"
|
||||||
className="no-underline"
|
className="no-underline"
|
||||||
onClick={() => openInShell(fileInfo.download_path)}
|
onClick={() => openFile()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,12 @@ export const Header = props => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="header__item">
|
<div className="header__item">
|
||||||
<Link onClick={() => navigate('/subscriptions')} button="alt button--flat" icon="icon-at" />
|
<Link
|
||||||
|
onClick={() => navigate('/subscriptions')}
|
||||||
|
button="alt button--flat"
|
||||||
|
icon="icon-at"
|
||||||
|
title={__('My Subscriptions')}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="header__item header__item--wunderbar">
|
<div className="header__item header__item--wunderbar">
|
||||||
<WunderBar />
|
<WunderBar />
|
||||||
|
|
|
@ -16,6 +16,7 @@ const Link = props => {
|
||||||
navigateParams,
|
navigateParams,
|
||||||
doNavigate,
|
doNavigate,
|
||||||
className,
|
className,
|
||||||
|
span,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const combinedClassName =
|
const combinedClassName =
|
||||||
|
@ -26,7 +27,8 @@ const Link = props => {
|
||||||
|
|
||||||
const onClick =
|
const onClick =
|
||||||
!props.onClick && navigate
|
!props.onClick && navigate
|
||||||
? () => {
|
? event => {
|
||||||
|
event.stopPropagation();
|
||||||
doNavigate(navigate, navigateParams || {});
|
doNavigate(navigate, navigateParams || {});
|
||||||
}
|
}
|
||||||
: props.onClick;
|
: props.onClick;
|
||||||
|
@ -44,17 +46,15 @@ const Link = props => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const linkProps = {
|
||||||
<a
|
className: combinedClassName,
|
||||||
className={combinedClassName}
|
href: href || 'javascript:;',
|
||||||
href={href || 'javascript:;'}
|
title,
|
||||||
title={title}
|
onClick,
|
||||||
onClick={onClick}
|
style,
|
||||||
{...('style' in props ? { style } : {})}
|
};
|
||||||
>
|
|
||||||
{content}
|
return span ? <span {...linkProps}>{content}</span> : <a {...linkProps}>{content}</a>;
|
||||||
</a>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Link;
|
export default Link;
|
||||||
|
|
|
@ -22,7 +22,7 @@ class UriIndicator extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { claim, link, uri, isResolvingUri, smallCard } = this.props;
|
const { claim, link, uri, isResolvingUri, smallCard, span } = this.props;
|
||||||
|
|
||||||
if (isResolvingUri && !claim) {
|
if (isResolvingUri && !claim) {
|
||||||
return <span className="empty">Validating...</span>;
|
return <span className="empty">Validating...</span>;
|
||||||
|
@ -81,7 +81,12 @@ class UriIndicator extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link navigate="/show" navigateParams={{ uri: channelLink }} className="no-underline">
|
<Link
|
||||||
|
navigate="/show"
|
||||||
|
navigateParams={{ uri: channelLink }}
|
||||||
|
className="no-underline"
|
||||||
|
span={span}
|
||||||
|
>
|
||||||
{inner}
|
{inner}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { doChangeVolume } from 'redux/actions/app';
|
||||||
import { selectVolume } from 'redux/selectors/app';
|
import { selectVolume } from 'redux/selectors/app';
|
||||||
import { doPlayUri, doSetPlayingUri } from 'redux/actions/content';
|
import { doPlayUri, doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
|
import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
|
||||||
|
import { setVideoPause } from 'redux/actions/video';
|
||||||
import {
|
import {
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
makeSelectLoadingForUri,
|
makeSelectLoadingForUri,
|
||||||
|
@ -11,6 +12,7 @@ import {
|
||||||
} from 'redux/selectors/file_info';
|
} from 'redux/selectors/file_info';
|
||||||
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
||||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||||
|
import { selectVideoPause } from 'redux/selectors/video';
|
||||||
import Video from './view';
|
import Video from './view';
|
||||||
import { selectPlayingUri } from 'redux/selectors/content';
|
import { selectPlayingUri } from 'redux/selectors/content';
|
||||||
|
|
||||||
|
@ -24,12 +26,14 @@ const select = (state, props) => ({
|
||||||
playingUri: selectPlayingUri(state),
|
playingUri: selectPlayingUri(state),
|
||||||
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
contentType: makeSelectContentTypeForUri(props.uri)(state),
|
||||||
volume: selectVolume(state),
|
volume: selectVolume(state),
|
||||||
|
videoPause: selectVideoPause(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
play: uri => dispatch(doPlayUri(uri)),
|
play: uri => dispatch(doPlayUri(uri)),
|
||||||
cancelPlay: () => dispatch(doSetPlayingUri(null)),
|
cancelPlay: () => dispatch(doSetPlayingUri(null)),
|
||||||
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
||||||
|
setVideoPause: val => dispatch(setVideoPause(val)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(Video);
|
export default connect(select, perform)(Video);
|
||||||
|
|
|
@ -21,6 +21,13 @@ class VideoPlayer extends React.PureComponent {
|
||||||
this.togglePlayListener = this.togglePlay.bind(this);
|
this.togglePlayListener = this.togglePlay.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.videoPause) {
|
||||||
|
this.refs.media.children[0].pause();
|
||||||
|
this.props.setVideoPause(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const container = this.refs.media;
|
const container = this.refs.media;
|
||||||
const { contentType, downloadPath, mediaType, changeVolume, volume } = this.props;
|
const { contentType, downloadPath, mediaType, changeVolume, volume } = this.props;
|
||||||
|
|
|
@ -52,6 +52,8 @@ class Video extends React.PureComponent {
|
||||||
changeVolume,
|
changeVolume,
|
||||||
volume,
|
volume,
|
||||||
uri,
|
uri,
|
||||||
|
videoPause,
|
||||||
|
setVideoPause,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const isPlaying = playingUri === uri;
|
const isPlaying = playingUri === uri;
|
||||||
|
@ -101,6 +103,8 @@ class Video extends React.PureComponent {
|
||||||
downloadCompleted={fileInfo.completed}
|
downloadCompleted={fileInfo.completed}
|
||||||
changeVolume={changeVolume}
|
changeVolume={changeVolume}
|
||||||
volume={volume}
|
volume={volume}
|
||||||
|
videoPause={videoPause}
|
||||||
|
setVideoPause={setVideoPause}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{!isPlaying && (
|
{!isPlaying && (
|
||||||
|
|
|
@ -19,7 +19,7 @@ class WalletAddress extends React.PureComponent {
|
||||||
<p>
|
<p>
|
||||||
{__('Use this wallet address to receive credits sent by another user (or yourself).')}
|
{__('Use this wallet address to receive credits sent by another user (or yourself).')}
|
||||||
</p>
|
</p>
|
||||||
<Address address={receiveAddress} />
|
<Address address={receiveAddress} showCopyButton />
|
||||||
</div>
|
</div>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Link
|
<Link
|
||||||
|
|
|
@ -154,3 +154,6 @@ export const CLEAR_SHAPE_SHIFT = 'CLEAR_SHAPE_SHIFT';
|
||||||
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
|
||||||
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
|
||||||
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
|
||||||
|
|
||||||
|
// Video controls
|
||||||
|
export const SET_VIDEO_PAUSE = 'SET_VIDEO_PAUSE';
|
||||||
|
|
|
@ -48,13 +48,22 @@ ipcRenderer.on('window-is-focused', () => {
|
||||||
dock.setBadge('');
|
dock.setBadge('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
(function(history, ...args) {
|
||||||
|
const { replaceState } = history;
|
||||||
|
const newHistory = history;
|
||||||
|
newHistory.replaceState = function(_, __, path) {
|
||||||
|
amplitude.getInstance().logEvent('NAVIGATION', { destination: path ? path.slice(1) : path });
|
||||||
|
return replaceState.apply(history, args);
|
||||||
|
};
|
||||||
|
})(window.history);
|
||||||
|
|
||||||
document.addEventListener('click', event => {
|
document.addEventListener('click', event => {
|
||||||
let { target } = event;
|
let { target } = event;
|
||||||
while (target && target !== document) {
|
while (target && target !== document) {
|
||||||
if (target.matches('a') || target.matches('button')) {
|
if (target.matches('a') || target.matches('button')) {
|
||||||
// TODO: Look into using accessiblity labels (this would also make the app more accessible)
|
// TODO: Look into using accessiblity labels (this would also make the app more accessible)
|
||||||
const hrefParts = window.location.href.split('#');
|
const hrefParts = window.location.href.split('#');
|
||||||
const element = target.title || (target.text && target.text.trim());
|
const element = target.title || (target.textContent && target.textContent.trim());
|
||||||
if (element) {
|
if (element) {
|
||||||
amplitude.getInstance().logEvent('CLICK', {
|
amplitude.getInstance().logEvent('CLICK', {
|
||||||
target: element,
|
target: element,
|
||||||
|
@ -63,6 +72,7 @@ document.addEventListener('click', event => {
|
||||||
} else {
|
} else {
|
||||||
amplitude.getInstance().logEvent('UNMARKED_CLICK', {
|
amplitude.getInstance().logEvent('UNMARKED_CLICK', {
|
||||||
location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/',
|
location: hrefParts.length > 1 ? hrefParts[hrefParts.length - 1] : '/',
|
||||||
|
source: target.outerHTML,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
selectCurrentModal,
|
selectCurrentModal,
|
||||||
} from 'redux/selectors/app';
|
} from 'redux/selectors/app';
|
||||||
import { doFetchDaemonSettings } from 'redux/actions/settings';
|
import { doFetchDaemonSettings } from 'redux/actions/settings';
|
||||||
import { doBalanceSubscribe, doFetchTransactions } from 'redux/actions/wallet';
|
import { doBalanceSubscribe } from 'redux/actions/wallet';
|
||||||
import { doAuthenticate } from 'redux/actions/user';
|
import { doAuthenticate } from 'redux/actions/user';
|
||||||
import { doFetchFileInfosAndPublishedClaims } from 'redux/actions/file_info';
|
import { doFetchFileInfosAndPublishedClaims } from 'redux/actions/file_info';
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
|
@ -217,7 +217,6 @@ export function doDaemonReady() {
|
||||||
dispatch(doBalanceSubscribe());
|
dispatch(doBalanceSubscribe());
|
||||||
dispatch(doFetchFileInfosAndPublishedClaims());
|
dispatch(doFetchFileInfosAndPublishedClaims());
|
||||||
dispatch(doFetchRewardedContent());
|
dispatch(doFetchRewardedContent());
|
||||||
dispatch(doFetchTransactions(false));
|
|
||||||
if (!selectIsUpgradeSkipped(state)) {
|
if (!selectIsUpgradeSkipped(state)) {
|
||||||
dispatch(doCheckUpgradeAvailable());
|
dispatch(doCheckUpgradeAvailable());
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import Lbry from 'lbry';
|
||||||
import Lbryio from 'lbryio';
|
import Lbryio from 'lbryio';
|
||||||
import Lbryuri from 'lbryuri';
|
import Lbryuri from 'lbryuri';
|
||||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||||
import { selectBalance, selectTransactionItems } from 'redux/selectors/wallet';
|
import { selectMyClaimsRaw } from 'redux/selectors/claims';
|
||||||
|
import { selectBalance } from 'redux/selectors/wallet';
|
||||||
import {
|
import {
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
selectDownloadingByOutpoint,
|
selectDownloadingByOutpoint,
|
||||||
|
@ -482,8 +483,8 @@ export function doPublish(params) {
|
||||||
export function doAbandonClaim(txid, nout) {
|
export function doAbandonClaim(txid, nout) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const transactionItems = selectTransactionItems(state);
|
const myClaims = selectMyClaimsRaw(state);
|
||||||
const { claim_id: claimId, claim_name: name } = transactionItems.find(
|
const { claim_id: claimId, name } = myClaims.find(
|
||||||
claim => claim.txid === txid && claim.nout === nout
|
claim => claim.txid === txid && claim.nout === nout
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import { selectHistoryStack, selectHistoryIndex } from 'redux/selectors/navigation';
|
import { selectHistoryStack, selectHistoryIndex } from 'redux/selectors/navigation';
|
||||||
import { toQueryString } from 'util/query_params';
|
import { toQueryString } from 'util/query_params';
|
||||||
import amplitude from 'amplitude-js';
|
|
||||||
|
|
||||||
export function doNavigate(path, params = {}, options = {}) {
|
export function doNavigate(path, params = {}, options = {}) {
|
||||||
return function(dispatch) {
|
return function(dispatch) {
|
||||||
|
@ -16,8 +15,6 @@ export function doNavigate(path, params = {}, options = {}) {
|
||||||
|
|
||||||
const { scrollY } = options;
|
const { scrollY } = options;
|
||||||
|
|
||||||
amplitude.getInstance().logEvent('NAVIGATION', { destination: url });
|
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.HISTORY_NAVIGATE,
|
type: ACTIONS.HISTORY_NAVIGATE,
|
||||||
data: { url, index: options.index, scrollY },
|
data: { url, index: options.index, scrollY },
|
||||||
|
|
10
src/renderer/redux/actions/video.js
Normal file
10
src/renderer/redux/actions/video.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// @flow
|
||||||
|
import * as actions from 'constants/action_types';
|
||||||
|
import type { Dispatch } from 'redux/reducers/video';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const setVideoPause = (data: boolean) => (dispatch: Dispatch) =>
|
||||||
|
dispatch({
|
||||||
|
type: actions.SET_VIDEO_PAUSE,
|
||||||
|
data,
|
||||||
|
});
|
|
@ -29,13 +29,13 @@ export function doBalanceSubscribe() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doFetchTransactions(fetch_tip_info = true) {
|
export function doFetchTransactions() {
|
||||||
return function(dispatch) {
|
return function(dispatch) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.FETCH_TRANSACTIONS_STARTED,
|
type: ACTIONS.FETCH_TRANSACTIONS_STARTED,
|
||||||
});
|
});
|
||||||
|
|
||||||
Lbry.transaction_list({ include_tip_info: fetch_tip_info }).then(results => {
|
Lbry.transaction_list({ include_tip_info: true }).then(results => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.FETCH_TRANSACTIONS_COMPLETED,
|
type: ACTIONS.FETCH_TRANSACTIONS_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// @flow
|
// @flow
|
||||||
import handleActions from 'util/redux-utils';
|
import { handleActions } from 'util/redux-utils';
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import * as STATUSES from 'constants/shape_shift';
|
import * as STATUSES from 'constants/shape_shift';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import handleActions from 'util/redux-utils';
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
export type Subscription = {
|
export type Subscription = {
|
||||||
channelName: string,
|
channelName: string,
|
||||||
|
|
25
src/renderer/redux/reducers/video.js
Normal file
25
src/renderer/redux/reducers/video.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// @flow
|
||||||
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
|
export type VideoState = { videoPause: boolean };
|
||||||
|
|
||||||
|
type setVideoPause = {
|
||||||
|
type: ACTIONS.SET_VIDEO_PAUSE,
|
||||||
|
data: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Action = setVideoPause;
|
||||||
|
export type Dispatch = (action: Action) => any;
|
||||||
|
|
||||||
|
const defaultState = { videoPause: false };
|
||||||
|
|
||||||
|
export default handleActions(
|
||||||
|
{
|
||||||
|
[ACTIONS.SET_VIDEO_PAUSE]: (state: VideoState, action: setVideoPause): VideoState => ({
|
||||||
|
...state,
|
||||||
|
videoPause: action.data,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
defaultState
|
||||||
|
);
|
6
src/renderer/redux/selectors/video.js
Normal file
6
src/renderer/redux/selectors/video.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
const selectState = state => state.video || {};
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const selectVideoPause = createSelector(selectState, state => state.videoPause);
|
|
@ -13,6 +13,7 @@ import userReducer from 'redux/reducers/user';
|
||||||
import walletReducer from 'redux/reducers/wallet';
|
import walletReducer from 'redux/reducers/wallet';
|
||||||
import shapeShiftReducer from 'redux/reducers/shape_shift';
|
import shapeShiftReducer from 'redux/reducers/shape_shift';
|
||||||
import subscriptionsReducer from 'redux/reducers/subscriptions';
|
import subscriptionsReducer from 'redux/reducers/subscriptions';
|
||||||
|
import videoReducer from 'redux/reducers/video';
|
||||||
import { persistStore, autoRehydrate } from 'redux-persist';
|
import { persistStore, autoRehydrate } from 'redux-persist';
|
||||||
import createCompressor from 'redux-persist-transform-compress';
|
import createCompressor from 'redux-persist-transform-compress';
|
||||||
import createFilter from 'redux-persist-transform-filter';
|
import createFilter from 'redux-persist-transform-filter';
|
||||||
|
@ -63,6 +64,7 @@ const reducers = combineReducers({
|
||||||
user: userReducer,
|
user: userReducer,
|
||||||
shapeShift: shapeShiftReducer,
|
shapeShift: shapeShiftReducer,
|
||||||
subscriptions: subscriptionsReducer,
|
subscriptions: subscriptionsReducer,
|
||||||
|
video: videoReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bulkThunk = createBulkThunkMiddleware();
|
const bulkThunk = createBulkThunkMiddleware();
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
// util for creating reducers
|
// util for creating reducers
|
||||||
// based off of redux-actions
|
// based off of redux-actions
|
||||||
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
|
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
|
||||||
const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
|
|
||||||
|
// eslint-disable-next-line import/prefer-default-export
|
||||||
|
export const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
|
||||||
const handler = actionMap[action.type];
|
const handler = actionMap[action.type];
|
||||||
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
|
@ -13,5 +15,3 @@ const handleActions = (actionMap, defaultState) => (state = defaultState, action
|
||||||
// returning a copy here breaks redux-persist
|
// returning a copy here breaks redux-persist
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { handleActions as default };
|
|
||||||
|
|
Loading…
Reference in a new issue